shithub: duke3d

Download patch

ref: ebe36c1019c8646de4ea7a647a030999fdc4ebf2
parent: 908713636a12bc3f94627cd3a79c43e7097f40d1
author: unknown <fabien@fabien-PC.(none)>
date: Tue Dec 11 10:05:40 EST 2012

Import of xDuke codebase.

--- /dev/null
+++ b/Engine/Engine.vcproj
@@ -1,0 +1,278 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="Engine"
+	ProjectGUID="{05511B4A-FB13-49E5-AE53-EE1F063C055D}"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\DebugVC7"
+			IntermediateDirectory=".\DebugVC7"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;..\SDL-1.2.9\include&quot;;src\enet\include"
+				PreprocessorDefinitions="nDBGRECORD; nDEBUG; PLATFORM_WIN32; UDP_NETWORKING; WIN32; _LIB; _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE"
+				RuntimeLibrary="0"
+				DefaultCharIsUnsigned="true"
+				UsePrecompiledHeader="0"
+				BrowseInformation="1"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="0"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\ReleaseVC7"
+			IntermediateDirectory=".\ReleaseVC7"
+			ConfigurationType="4"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			WholeProgramOptimization="0"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="1"
+				OmitFramePointers="true"
+				AdditionalIncludeDirectories="&quot;..\..\SDL-1.2.9\include&quot;;src\enet\include"
+				PreprocessorDefinitions="nDBGRECORD; nDEBUG; PLATFORM_WIN32; UDP_NETWORKING; WIN32; _LIB; _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE"
+				RuntimeLibrary="0"
+				BufferSecurityCheck="false"
+				DefaultCharIsUnsigned="true"
+				UsePrecompiledHeader="0"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="0"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+			>
+			<File
+				RelativePath=".\src\a.c"
+				>
+			</File>
+			<File
+				RelativePath="src\cache1d.c"
+				>
+			</File>
+			<File
+				RelativePath="src\Engine.c"
+				>
+			</File>
+			<File
+				RelativePath="src\mmulti.c"
+				>
+			</File>
+			<File
+				RelativePath="src\mmulti_stable.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\src\pragmas.c"
+				>
+			</File>
+			<File
+				RelativePath="src\sdl_driver.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl"
+			>
+			<File
+				RelativePath="src\a.h"
+				>
+			</File>
+			<File
+				RelativePath="src\build.h"
+				>
+			</File>
+			<File
+				RelativePath="src\cache1d.h"
+				>
+			</File>
+			<File
+				RelativePath="src\display.h"
+				>
+			</File>
+			<File
+				RelativePath="src\Engine.h"
+				>
+			</File>
+			<File
+				RelativePath="src\engine_protos.h"
+				>
+			</File>
+			<File
+				RelativePath=".\src\icon.h"
+				>
+			</File>
+			<File
+				RelativePath="src\platform.h"
+				>
+			</File>
+			<File
+				RelativePath=".\src\pragmas.h"
+				>
+			</File>
+			<File
+				RelativePath="src\win32_compat.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="enet"
+			>
+			<File
+				RelativePath="src\enet\host.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\list.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\memory.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\packet.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\peer.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\protocol.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\unix.c"
+				>
+			</File>
+			<File
+				RelativePath="src\enet\win32.c"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
--- /dev/null
+++ b/Engine/src/a.c
@@ -1,0 +1,1095 @@
+#ifndef USE_I386_ASM
+
+// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+// Ken Silverman's official web site: "http://www.advsys.net/ken"
+// See the included license file "BUILDLIC.TXT" for license info.
+// This file has been modified from Ken Silverman's original release
+
+/* DDOI - This file is an attempt to reimplement a_nasm.asm in C */
+
+#include "platform.h"
+#include "build.h"
+
+#define shrd(a,b,c) (((b)<<(32-(c))) | ((a)>>(c)))
+#define shld(a,b,c) (((b)>>(32-(c))) | ((a)<<(c)))
+
+extern long asm1;
+extern long asm2;
+extern long asm3;
+extern long asm4;
+
+long is_vmware_running(void)
+{
+    return 0;
+} /* is_vmware_running */
+
+/* #pragma aux mmxoverlay modify [eax ebx ecx edx] */
+long mmxoverlay(void)
+{
+    return 0;
+} /* mmxoverlay */
+
+/* #pragma aux sethlinesizes parm [eax][ebx][ecx] */
+static unsigned char machxbits_al;
+static unsigned char machxbits_bl;
+static long machxbits_ecx;
+void sethlinesizes(long i1, long i2, long i3)
+{
+    machxbits_al = i1;
+    machxbits_bl = i2;
+    machxbits_ecx = i3;
+} /* sethlinesizes */
+
+static unsigned char* pal_eax;
+void setuphlineasm4(long i1, long i2) { }
+void setpalookupaddress(unsigned char *i1) { pal_eax = i1; }
+
+void hlineasm4(long _count, unsigned long unused_source, long _shade, unsigned long _i4, unsigned long _i5, long i6)
+{
+    /* force into registers (probably only useful on PowerPC)... */
+    register unsigned char *dest = (unsigned char *) i6;
+    register unsigned long i4 = _i4;
+    register unsigned long i5 = _i5;
+    register int shifter = ((256-machxbits_al) & 0x1f);
+    register unsigned long source;
+    register long shade = _shade & 0xffffff00;
+    register long count = _count + 1;
+    register unsigned char bits = machxbits_bl;
+    register unsigned char *lookup = (unsigned char *) machxbits_ecx;
+    register unsigned char *pal = (unsigned char *) pal_eax;
+    register long _asm1 = asm1;
+    register long _asm2 = asm2;
+
+    while (count) {
+	    source = i5 >> shifter;
+	    source = shld(source,i4,bits);
+	    source = lookup[source];
+	    *dest = pal[shade|source];
+	    dest--;
+	    i5 -= _asm1;
+	    i4 -= _asm2;
+	    count--;
+    }
+}
+
+static long rmach_eax;
+static long rmach_ebx;
+static long rmach_ecx;
+static long rmach_edx;
+static long rmach_esi;
+void setuprhlineasm4(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    rmach_eax = i1;
+    rmach_ebx = i2;
+    rmach_ecx = i3;
+    rmach_edx = i4;
+    rmach_esi = i5;
+} /* setuprhlineasm4 */
+
+void rhlineasm4(long i1, long i2, long i3, unsigned long i4, unsigned long i5, long i6)
+{
+    unsigned long ebp = i6 - i1;
+    unsigned long rmach6b = ebp-1;
+
+    if (i1 <= 0) return;
+
+    i6 = i1;
+    do {
+	    i3 = ((i3&0xffffff00)|(*((unsigned char *)i2)));
+	    i4 -= rmach_eax;
+	    ebp = (((i4+rmach_eax) < i4) ? -1 : 0);
+	    i5 -= rmach_ebx;
+	    if ((i5 + rmach_ebx) < i5) i2 -= (rmach_ecx+1);
+	    else i2 -= rmach_ecx;
+	    ebp &= rmach_esi;
+	    i1 = ((i1&0xffffff00)|(((unsigned char *)i3)[rmach_edx]));
+	    ((unsigned char *)rmach6b)[i6] = (i1&0xff);
+	    i2 -= ebp;
+	    i6--;
+    } while (i6);
+} /* rhlineasm4 */
+
+static long rmmach_eax;
+static long rmmach_ebx;
+static long rmmach_ecx;
+static long rmmach_edx;
+static long rmmach_esi;
+void setuprmhlineasm4(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    rmmach_eax = i1;
+    rmmach_ebx = i2;
+    rmmach_ecx = i3;
+    rmmach_edx = i4;
+    rmmach_esi = i5;
+} /* setuprmhlineasm4 */
+
+/* #pragma aux rmhlineasm4 parm [eax][ebx][ecx][edx][esi][edi] */
+void rmhlineasm4(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    unsigned long ebp = i6 - i1;
+    unsigned long rmach6b = ebp-1;
+
+    if (i1 <= 0) return;
+
+    i6 = i1;
+    do {
+	    i3 = ((i3&0xffffff00)|(*((unsigned char *)i2)));
+	    i4 -= rmmach_eax;
+	    ebp = (((i4+rmmach_eax) < i4) ? -1 : 0);
+	    i5 -= rmmach_ebx;
+	    if ((i5 + rmmach_ebx) < i5) i2 -= (rmmach_ecx+1);
+	    else i2 -= rmmach_ecx;
+	    ebp &= rmmach_esi;
+	    if ((i3&0xff) != 255) {
+		    i1 = ((i1&0xffffff00)|(((unsigned char *)i3)[rmmach_edx]));
+		    ((unsigned char *)rmach6b)[i6] = (i1&0xff);
+	    }
+	    i2 -= ebp;
+	    i6--;
+    } while (i6);
+} /* rmhlineasm4 */
+
+
+/* #pragma aux setupqrhlineasm4 parm [eax][ebx][ecx][edx][esi][edi] */
+void setupqrhlineasm4(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    setuprhlineasm4(i1,i2,i3,i4,i5,i6);
+} /* setupqrhlineasm4 */
+
+
+/* #pragma aux qrhlineasm4 parm [eax][ebx][ecx][edx][esi][edi] */
+void qrhlineasm4(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    rhlineasm4(i1,i2,i3,i4,i5,i6);
+} /* qrhlineasm4 */
+
+/* #pragma aux setvlinebpl parm [eax] */
+static long fixchain;
+void setvlinebpl(long i1)
+{
+    fixchain = i1;
+} /* setvlinebpl */
+
+/* #pragma aux fixtransluscence parm [eax] */
+static long tmach;
+void fixtransluscence(long i1)
+{
+    tmach = i1;
+} /* fixtransluscence */
+
+long vlineasm1(long i1, long i2, long i3, long i4, long i5, long i6);
+
+/* #pragma aux prevlineasm1 parm [eax][ebx][ecx][edx][esi][edi] */
+static unsigned char mach3_al;
+long prevlineasm1(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    unsigned char *source = (unsigned char *)i5;
+    unsigned char *dest = (unsigned char *)i6;
+    if (i3 == 0)
+    {
+	    i1 += i4;
+	    ((unsigned long)i4) >>= mach3_al;
+	    i4 = (i4&0xffffff00) | (source[i4]&0xff);
+	    *dest = ((unsigned char*)i2)[i4];
+	    return i1;
+    } else {
+	    return vlineasm1(i1,i2,i3,i4,i5,i6);
+    }
+} /* prevlineasm1 */
+
+/* #pragma aux vlineasm1 parm [eax][ebx][ecx][edx][esi][edi] */
+long vlineasm1(long vince, long palookupoffse, long i3, long vplce, long bufplce, long i6)
+{
+    unsigned long temp;
+    unsigned char *dest = (unsigned char *)i6;
+
+    i3++;
+    while (i3)
+    {
+	    temp = ((unsigned)vplce) >> mach3_al;
+	    temp = ((unsigned char *)bufplce)[temp];
+	    *dest = ((unsigned char*)palookupoffse)[temp];
+	    vplce += vince;
+	    dest += fixchain;
+	    i3--;
+    }
+    return vplce;
+} /* vlineasm1 */
+
+/* #pragma aux setuptvlineasm parm [eax] */
+static unsigned char transmach3_al = 32;
+void setuptvlineasm(long i1)
+{
+    transmach3_al = (i1 & 0x1f);
+} /* setuptvlineasm */
+
+/* #pragma aux tvlineasm1 parm [eax][ebx][ecx][edx][esi][edi] */
+static int transrev = 0;
+long tvlineasm1(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+	unsigned char *source = (unsigned char *)i5;
+	unsigned char *dest = (unsigned char *)i6;
+
+	i3++;
+	while (i3)
+	{
+		unsigned long temp = i4;
+		temp >>= transmach3_al;
+		temp = source[temp];
+		if (temp != 255)
+		{
+			unsigned short val;
+			val = ((unsigned char *)i2)[temp];
+			val |= ((*dest)<<8);
+			if (transrev) val = ((val>>8)|(val<<8));
+			*dest = ((unsigned char *)tmach)[val];
+		}
+		i4 += i1;
+		dest += fixchain;
+		i3--;
+	}
+	return i4;
+} /* tvlineasm1 */
+
+/* #pragma aux setuptvlineasm2 parm [eax][ebx][ecx] */
+static unsigned char tran2shr;
+static unsigned long tran2pal_ebx;
+static unsigned long tran2pal_ecx;
+void setuptvlineasm2(long i1, long i2, long i3)
+{
+	tran2shr = (i1&0x1f);
+	tran2pal_ebx = i2;
+	tran2pal_ecx = i3;
+} /* */
+
+/* #pragma aux tvlineasm2 parm [eax][ebx][ecx][edx][esi][edi] */
+void tvlineasm2(unsigned long i1, unsigned long i2, unsigned long i3, unsigned long i4, unsigned long i5, unsigned long i6)
+{
+	unsigned long ebp = i1;
+	unsigned long tran2inca = i2;
+	unsigned long tran2incb = asm1;
+	unsigned long tran2bufa = i3;
+	unsigned long tran2bufb = i4;
+	unsigned long tran2edi = asm2;
+	unsigned long tran2edi1 = asm2 + 1;
+
+	i6 -= asm2;
+
+	do {
+		i1 = i5 >> tran2shr;
+		i2 = ebp >> tran2shr;
+		i5 += tran2inca;
+		ebp += tran2incb;
+		i3 = ((unsigned char *)tran2bufa)[i1];
+		i4 = ((unsigned char *)tran2bufb)[i2];
+		if (i3 == 255) { // skipdraw1
+			if (i4 != 255) { // skipdraw3
+				unsigned short val;
+				val = ((unsigned char *)tran2pal_ecx)[i4];
+				val |= (((unsigned char *)i6)[tran2edi1]<<8);
+				if (transrev) val = ((val>>8)|(val<<8));
+				((unsigned char *)i6)[tran2edi1] =
+					((unsigned char *)tmach)[val];
+			}
+		} else if (i4 == 255) { // skipdraw2
+			unsigned short val;
+			val = ((unsigned char *)tran2pal_ebx)[i3];
+			val |= (((unsigned char *)i6)[tran2edi]<<8);
+			if (transrev) val = ((val>>8)|(val<<8));
+			((unsigned char *)i6)[tran2edi] =
+				((unsigned char *)tmach)[val];
+		} else {
+			unsigned short l = ((unsigned char *)i6)[tran2edi]<<8;
+			unsigned short r = ((unsigned char *)i6)[tran2edi1]<<8;
+			l |= ((unsigned char *)tran2pal_ebx)[i3];
+			r |= ((unsigned char *)tran2pal_ecx)[i4];
+			if (transrev) {
+				l = ((l>>8)|(l<<8));
+				r = ((r>>8)|(r<<8));
+			}
+			((unsigned char *)i6)[tran2edi] =
+				((unsigned char *)tmach)[l];
+			((unsigned char *)i6)[tran2edi1] =
+				((unsigned char *)tmach)[r];
+		}
+		i6 += fixchain;
+	} while (i6 > i6 - fixchain);
+	asm1 = i5;
+	asm2 = ebp;
+} /* tvlineasm2 */
+
+
+/* #pragma aux mvlineasm1 parm [eax][ebx][ecx][edx][esi][edi] */
+static unsigned char machmv;
+long mvlineasm1(long vince, long palookupoffse, long i3, long vplce, long bufplce, long i6)
+{
+    unsigned long temp;
+    unsigned char *dest = (unsigned char *)i6;
+
+	// FIX_00087: 1024x768 mode being slow. Undone FIX_00070 and fixed font issue again
+    for(;i3>=0;i3--)
+    {
+	    temp = ((unsigned)vplce) >> machmv;
+	    temp = ((unsigned char *)bufplce)[temp];
+	    if (temp != 255) *dest = ((unsigned char*)palookupoffse)[temp];
+	    vplce += vince;
+	    dest += fixchain;
+    }
+    return vplce;
+} /* mvlineasm1 */
+
+/* #pragma aux setupvlineasm parm [eax] */
+void setupvlineasm(long i1)
+{
+    mach3_al = (i1&0x1f);
+} /* setupvlineasm */
+
+extern long vplce[4], vince[4], palookupoffse[4], bufplce[4];
+
+#if HAVE_POWERPC
+/* About 25% faster than the scalar version on my 12" Powerbook. --ryan. */
+static void vlineasm4_altivec(long i1, long i2)
+{
+    unsigned int mach_array[4] = { (unsigned int) mach3_al,
+                                   (unsigned int) mach3_al,
+                                   (unsigned int) mach3_al,
+                                   (unsigned int) mach3_al };
+
+    unsigned int temp[4];
+    unsigned long index = (i2 + ylookup[i1]);
+    unsigned char *dest = (unsigned char*)(-ylookup[i1]);
+
+    register vector unsigned int vec_temp;
+    register vector signed int vec_vplce;
+    register vector signed int vec_vince;
+    register vector signed int vec_bufplce;
+    register vector unsigned int vec_shifter;
+
+    register unsigned char *pal0 = (unsigned char *) palookupoffse[0];
+    register unsigned char *pal1 = (unsigned char *) palookupoffse[1];
+    register unsigned char *pal2 = (unsigned char *) palookupoffse[2];
+    register unsigned char *pal3 = (unsigned char *) palookupoffse[3];
+
+    vec_shifter = vec_ld(0, mach_array);
+    vec_vplce = vec_ld(0, vplce);
+    vec_vince = vec_ld(0, vince);
+    vec_bufplce = vec_ld(0, bufplce);
+
+    do {
+        vec_temp = (vector unsigned int) vec_sr(vec_vplce, vec_shifter);
+        vec_temp = (vector unsigned int) vec_add(vec_bufplce, (vector signed int) vec_temp);
+        vec_st(vec_temp, 0x00, temp);
+        vec_vplce = vec_add(vec_vplce, vec_vince);
+	    dest[index] = pal0[*((unsigned char *) temp[0])];
+	    dest[index+1] = pal1[*((unsigned char *) temp[1])];
+	    dest[index+2] = pal2[*((unsigned char *) temp[2])];
+	    dest[index+3] = pal3[*((unsigned char *) temp[3])];
+        dest += fixchain;
+    } while (((unsigned)dest - fixchain) < ((unsigned)dest));
+
+    vec_st(vec_vplce, 0, vplce);
+}
+#endif
+
+/* #pragma aux vlineasm4 parm [ecx][edi] modify [eax ebx ecx edx esi edi] */
+void vlineasm4(long i1, long i2)
+{
+#if HAVE_POWERPC
+    if (has_altivec)
+        vlineasm4_altivec(i1, i2);
+    else
+#endif
+    {
+        int i;
+        unsigned long temp;
+        unsigned long index = (i2 + ylookup[i1]);
+        unsigned char *dest = (unsigned char*)(-ylookup[i1]);
+        do {
+            for (i = 0; i < 4; i++)
+            {
+        	    temp = ((unsigned)vplce[i]) >> mach3_al;
+        	    temp = (((unsigned char*)(bufplce[i]))[temp]);
+        	    dest[index+i] = ((unsigned char*)(palookupoffse[i]))[temp];
+	            vplce[i] += vince[i];
+            }
+            dest += fixchain;
+        } while (((unsigned)dest - fixchain) < ((unsigned)dest));
+    }
+} /* vlineasm4 */
+
+/* #pragma aux setupmvlineasm parm [eax] */
+void setupmvlineasm(long i1)
+{
+    machmv = (i1&0x1f);
+} /* setupmvlineasm */
+
+/* #pragma aux mvlineasm4 parm [ecx][edi] modify [eax ebx ecx edx esi edi] */
+void mvlineasm4(long i1, long i2)
+{
+    int i;
+    unsigned long temp;
+    unsigned long index = (i2 + ylookup[i1]);
+    unsigned char *dest = (unsigned char*)(-ylookup[i1]);
+    do {
+        for (i = 0; i < 4; i++)
+        {
+	    temp = ((unsigned)vplce[i]) >> machmv;
+	    temp = (((unsigned char*)(bufplce[i]))[temp]);
+	    if (temp != 255)
+		    dest[index+i] = ((unsigned char*)(palookupoffse[i]))[temp];
+	    vplce[i] += vince[i];
+        }
+        dest += fixchain;
+    } while (((unsigned)dest - fixchain) < ((unsigned)dest));
+} /* mvlineasm4 */
+
+/* #pragma aux setupspritevline parm [eax][ebx][ecx][edx][esi][edi] */
+static long spal_eax;
+static long smach_eax;
+static long smach2_eax;
+static long smach5_eax;
+static long smach_ecx;
+void setupspritevline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    spal_eax = i1;
+    smach_eax = (i5<<16);
+    smach2_eax = (i5>>16)+i2;
+    smach5_eax = smach2_eax + i4;
+    smach_ecx = i3;
+} /* setupspritevline */
+
+/* #pragma aux spritevline parm [eax][ebx][ecx][edx][esi][edi] */
+void spritevline(long i1, unsigned long i2, long i3, unsigned long i4, long i5, long i6)
+{
+    unsigned char *source = (unsigned char *)i5;
+    unsigned char *dest = (unsigned char *)i6;
+
+dumblabel1:
+    i2 += smach_eax;
+    i1 = (i1&0xffffff00) | (*source&0xff);
+    if ((i2 - smach_eax) > i2) source += smach2_eax + 1;
+    else source += smach2_eax;
+dumblabel2:
+    i1 = (i1&0xffffff00) | (((unsigned char *)spal_eax)[i1]&0xff);
+    *dest = i1;
+    dest += fixchain;
+
+    i4 += smach_ecx;
+    i4--;
+    if (!((i4 - smach_ecx) > i4) && i4 != 0)
+	    goto dumblabel1;
+    if (i4 == 0) return;
+    i2 += smach_eax;
+    i1 = (i1&0xffffff00) | (*source&0xff);
+    if ((i2 - smach_eax) > i2) source += smach5_eax + 1;
+    else source += smach5_eax;
+    goto dumblabel2;
+} /* spritevline */
+
+/* #pragma aux msetupspritevline parm [eax][ebx][ecx][edx][esi][edi] */
+static long mspal_eax;
+static long msmach_eax;
+static long msmach2_eax;
+static long msmach5_eax;
+static long msmach_ecx;
+void msetupspritevline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    mspal_eax = i1;
+    msmach_eax = (i5<<16);
+    msmach2_eax = (i5>>16)+i2;
+    msmach5_eax = smach2_eax + i4;
+    msmach_ecx = i3;
+} /* msetupspritevline */
+
+/* #pragma aux mspritevline parm [eax][ebx][ecx][edx][esi][edi] */
+void mspritevline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    unsigned char *source = (unsigned char *)i5;
+    unsigned char *dest = (unsigned char *)i6;
+
+msdumblabel1:
+    i2 += smach_eax;
+    i1 = (i1&0xffffff00) | (*source&0xff);
+    if ((i2 - smach_eax) > i2) source += smach2_eax + 1;
+    else source += smach2_eax;
+msdumblabel2:
+    if ((i1&0xff) != 255)
+    {
+	    i1 = (i1&0xffffff00) | (((unsigned char *)spal_eax)[i1]&0xff);
+	    *dest = i1;
+    }
+    dest += fixchain;
+
+    i4 += smach_ecx;
+    i4--;
+    if (!((i4 - smach_ecx) > i4) && i4 != 0)
+	    goto msdumblabel1;
+    if (i4 == 0) return;
+    i2 += smach_eax;
+    i1 = (i1&0xffffff00) | (*source&0xff);
+    if ((i2 - smach_eax) > i2) source += smach5_eax + 1;
+    else source += smach5_eax;
+    goto msdumblabel2;
+} /* mspritevline */
+
+/* #pragma aux tsetupspritevline parm [eax][ebx][ecx][edx][esi][edi] */
+unsigned long tspal;
+unsigned long tsmach_eax1;
+unsigned long tsmach_eax2;
+unsigned long tsmach_eax3;
+unsigned long tsmach_ecx;
+void tsetupspritevline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+	tspal = i1;
+	tsmach_eax1 = i5 << 16;
+	tsmach_eax2 = (i5 >> 16) + i2;
+	tsmach_eax3 = tsmach_eax2 + i4;
+	tsmach_ecx = i3;
+} /* tsetupspritevline */
+
+/* #pragma aux tspritevline parm [eax][ebx][ecx][edx][esi][edi] */
+void tspritevline(long i1, long i2, long i3, unsigned long i4, long i5, long i6)
+{
+	while (i3)
+	{
+		i3--;
+		if (i3 != 0)
+		{
+			unsigned long adder = tsmach_eax2;
+			i4 += tsmach_ecx;
+			if (i4 < (i4 - tsmach_ecx)) adder = tsmach_eax3;
+			i1 = *((unsigned char *)i5);
+			i2 += tsmach_eax1;
+			if (i2 < (i2 - tsmach_eax1)) i5++;
+			i5 += adder;
+			// tstartsvline
+			if (i1 != 0xff)
+			{
+				unsigned short val;
+				val = ((unsigned char*)tspal)[i1];
+				val |= ((*((unsigned char *)i6))<<8);
+				if (transrev) val = ((val>>8)|(val<<8));
+				i1 = ((unsigned char *)tmach)[val];
+				*((unsigned char *)i6) = (i1&0xff);
+			}
+			i6 += fixchain;
+		}
+	}
+} /* tspritevline */
+
+/* #pragma aux mhline parm [eax][ebx][ecx][edx][esi][edi] */
+static long mmach_eax;
+static long mmach_asm3;
+static long mmach_asm1;
+static long mmach_asm2;
+void mhlineskipmodify(long i1, unsigned long i2, unsigned long i3, long i4, long i5, long i6);
+void mhline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    mmach_eax = i1;
+    mmach_asm3 = asm3;
+    mmach_asm1 = asm1;
+    mmach_asm2 = asm2;
+    mhlineskipmodify(asm2,i2,i3,i4,i5,i6);
+} /* mhline */
+
+/* #pragma aux mhlineskipmodify parm [eax][ebx][ecx][edx][esi][edi] */
+static unsigned char mshift_al = 26;
+static unsigned char mshift_bl = 6;
+void mhlineskipmodify(long i1, unsigned long i2, unsigned long i3, long i4, long i5, long i6)
+{
+    unsigned long ebx;
+    int counter = (i3>>16);
+    while (counter >= 0)
+    {
+	    ebx = i2 >> mshift_al;
+	    ebx = shld (ebx, (unsigned)i5, mshift_bl);
+	    i1 = ((unsigned char *)mmach_eax)[ebx];
+	    if ((i1&0xff) != 0xff)
+		    *((unsigned char *)i6) = (((unsigned char*)mmach_asm3)[i1]);
+
+	    i2 += mmach_asm1;
+	    i5 += mmach_asm2;
+	    i6++;
+	    counter--;
+    }
+} /* mhlineskipmodify */
+
+/* #pragma aux msethlineshift parm [eax][ebx] */
+void msethlineshift(long i1, long i2)
+{
+    i1 = 256-i1;
+    mshift_al = (i1&0x1f);
+    mshift_bl = (i2&0x1f);
+} /* msethlineshift */
+
+/* #pragma aux thline parm [eax][ebx][ecx][edx][esi][edi] */
+static long tmach_eax;
+static long tmach_asm3;
+static long tmach_asm1;
+static long tmach_asm2;
+void thlineskipmodify(long i1, unsigned long i2, unsigned long i3, long i4, long i5, long i6);
+void thline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    tmach_eax = i1;
+    tmach_asm3 = asm3;
+    tmach_asm1 = asm1;
+    tmach_asm2 = asm2;
+    thlineskipmodify(asm2,i2,i3,i4,i5,i6);
+} /* thline */
+
+/* #pragma aux thlineskipmodify parm [eax][ebx][ecx][edx][esi][edi] */
+static unsigned char tshift_al = 26;
+static unsigned char tshift_bl = 6;
+void thlineskipmodify(long i1, unsigned long i2, unsigned long i3, long i4, long i5, long i6)
+{
+    unsigned long ebx;
+    int counter = (i3>>16);
+    while (counter >= 0)
+    {
+	    ebx = i2 >> tshift_al;
+	    ebx = shld (ebx, (unsigned)i5, tshift_bl);
+	    i1 = ((unsigned char *)tmach_eax)[ebx];
+	    if ((i1&0xff) != 0xff)
+	    {
+		    unsigned short val = (((unsigned char*)tmach_asm3)[i1]);
+		    val |= (*((unsigned char *)i6)<<8);
+		    if (transrev) val = ((val>>8)|(val<<8));
+		    *((unsigned char *)i6) = (((unsigned char*)tmach)[val]);
+	    }
+
+	    i2 += tmach_asm1;
+	    i5 += tmach_asm2;
+	    i6++;
+	    counter--;
+    }
+} /* thlineskipmodify */
+
+/* #pragma aux tsethlineshift parm [eax][ebx] */
+void tsethlineshift(long i1, long i2)
+{
+    i1 = 256-i1;
+    tshift_al = (i1&0x1f);
+    tshift_bl = (i2&0x1f);
+} /* tsethlineshift */
+
+/* #pragma aux setupslopevlin parm [eax][ebx][ecx] modify [edx] */
+static long slopemach_ebx;
+static long slopemach_ecx;
+static long slopemach_edx;
+static unsigned char slopemach_ah1;
+static unsigned char slopemach_ah2;
+static float asm2_f;
+typedef union { unsigned int i; float f; } bitwisef2i;
+void setupslopevlin(long i1, long i2, long i3)
+{
+    bitwisef2i c;
+    slopemach_ebx = i2;
+    slopemach_ecx = i3;
+    slopemach_edx = (1<<(i1&0x1f)) - 1;
+    slopemach_edx <<= ((i1&0x1f00)>>8);
+    slopemach_ah1 = 32-((i1&0x1f00)>>8);
+    slopemach_ah2 = (slopemach_ah1 - (i1&0x1f)) & 0x1f;
+    c.f = asm2_f = (float)asm1;
+    asm2 = c.i;
+} /* setupslopevlin */
+
+extern long reciptable[2048];
+extern long globalx3, globaly3;
+extern long fpuasm;
+#define low32(a) ((a&0xffffffff))
+#define high32(a) ((int)(((__int64)a&(__int64)0xffffffff00000000)>>32))
+
+/* #pragma aux slopevlin parm [eax][ebx][ecx][edx][esi][edi] */
+void slopevlin(long i1, unsigned long i2, long i3, long i4, long i5, long i6)
+{
+    bitwisef2i c;
+    unsigned long ecx,eax,ebx,edx,esi,edi;
+    float a = (float) asm3 + asm2_f;
+    i1 -= slopemach_ecx;
+    esi = i5 + low32((__int64)globalx3 * (__int64)(i2<<3));
+    edi = i6 + low32((__int64)globaly3 * (__int64)(i2<<3));
+    ebx = i4;
+    do {
+	    // -------------
+	    // All this is calculating a fixed point approx. of 1/a
+	    c.f = a;
+	    fpuasm = eax = c.i;
+	    edx = (((long)eax) < 0) ? 0xffffffff : 0;
+	    eax = eax << 1;
+	    ecx = (eax>>24);	//  exponent
+	    eax = ((eax&0xffe000)>>11);
+	    ecx = ((ecx&0xffffff00)|((ecx-2)&0xff));
+	    eax = reciptable[eax/4];
+	    eax >>= (ecx&0x1f);
+	    eax ^= edx;
+	    // -------------
+	    edx = i2;
+	    i2 = eax;
+	    eax -= edx;
+	    ecx = low32((__int64)globalx3 * (__int64)eax);
+	    eax = low32((__int64)globaly3 * (__int64)eax);
+	    a += asm2_f;
+
+	    asm4 = ebx;
+	    ecx = ((ecx&0xffffff00)|(ebx&0xff));
+	    if (ebx >= 8) ecx = ((ecx&0xffffff00)|8);
+
+	    ebx = esi;
+	    edx = edi;
+	    while ((ecx&0xff))
+	    {
+		    ebx >>= slopemach_ah2;
+		    esi += ecx;
+		    edx >>= slopemach_ah1;
+		    ebx &= slopemach_edx;
+		    edi += eax;
+		    i1 += slopemach_ecx;
+		    edx = ((edx&0xffffff00)|((((unsigned char *)(ebx+edx))[slopemach_ebx])));
+		    ebx = *((unsigned long*)i3); // register trickery
+		    i3 -= 4;
+		    eax = ((eax&0xffffff00)|(*((unsigned char *)(ebx+edx))));
+		    ebx = esi;
+		    *((unsigned char *)i1) = (eax&0xff);
+		    edx = edi;
+		    ecx = ((ecx&0xffffff00)|((ecx-1)&0xff));
+	    }
+	    ebx = asm4;
+	    ebx -= 8;	// BITSOFPRECISIONPOW
+    } while ((long)ebx > 0);
+} /* slopevlin */
+
+/* #pragma aux settransnormal parm */
+void settransnormal(void)
+{
+	transrev = 0;
+} /* settransnormal */
+
+/* #pragma aux settransreverse parm */
+void settransreverse(void)
+{
+	transrev = 1;
+} /* settransreverse */
+
+/* #pragma aux setupdrawslab parm [eax][ebx] */
+long setupdrawslab(long i1, long i2)
+{
+    long retval = 0;
+    /*
+    __asm__ __volatile__ (
+        "call _asm_setupdrawslab   \n\t"
+       : "=a" (retval)
+        : "a" (i1), "b" (i2)
+        : "cc", "memory");
+	*/
+    return(retval);
+/*
+_asm_setupdrawslab:
+	mov dword [voxbpl1+2], eax
+	mov dword [voxbpl2+2], eax
+	mov dword [voxbpl3+2], eax
+	mov dword [voxbpl4+2], eax
+	mov dword [voxbpl5+2], eax
+	mov dword [voxbpl6+2], eax
+	mov dword [voxbpl7+2], eax
+	mov dword [voxbpl8+2], eax
+
+	mov dword [voxpal1+2], ebx
+	mov dword [voxpal2+2], ebx
+	mov dword [voxpal3+2], ebx
+	mov dword [voxpal4+2], ebx
+	mov dword [voxpal5+2], ebx
+	mov dword [voxpal6+2], ebx
+	mov dword [voxpal7+2], ebx
+	mov dword [voxpal8+2], ebx
+	ret
+*/
+} /* setupdrawslab */
+
+/* #pragma aux drawslab parm [eax][ebx][ecx][edx][esi][edi] */
+long drawslab(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    long retval = 0;
+    /*
+    __asm__ __volatile__ (
+        "call _asm_drawslab   \n\t"
+       : "=a" (retval)
+        : "a" (i1), "b" (i2), "c" (i3), "d" (i4), "S" (i5), "D" (i6)
+        : "cc", "memory");
+	*/
+    return(retval);
+/*
+_asm_drawslab:
+	push ebp
+	cmp eax, 2
+	je voxbegdraw2
+	ja voxskip2
+	xor eax, eax
+voxbegdraw1:
+	mov ebp, ebx
+	shr ebp, 16
+	add ebx, edx
+	dec ecx
+	mov al, byte [esi+ebp]
+voxpal1: mov al, byte [eax+88888888h]
+	mov byte [edi], al
+voxbpl1: lea edi, [edi+88888888h]
+	jnz voxbegdraw1
+	pop ebp
+	ret
+
+voxbegdraw2:
+	mov ebp, ebx
+	shr ebp, 16
+	add ebx, edx
+	xor eax, eax
+	dec ecx
+	mov al, byte [esi+ebp]
+voxpal2: mov al, byte [eax+88888888h]
+	mov ah, al
+	mov word [edi], ax
+voxbpl2: lea edi, [edi+88888888h]
+	jnz voxbegdraw2
+	pop ebp
+	ret
+
+voxskip2:
+	cmp eax, 4
+	jne voxskip4
+	xor eax, eax
+voxbegdraw4:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal3: mov al, byte [eax+88888888h]
+	mov ah, al
+	shl eax, 8
+	mov al, ah
+	shl eax, 8
+	mov al, ah
+	mov dword [edi], eax
+voxbpl3: add edi, 88888888h
+	dec ecx
+	jnz voxbegdraw4
+	pop ebp
+	ret
+
+voxskip4:
+	add eax, edi
+
+	test edi, 1
+	jz voxskipslab1
+	cmp edi, eax
+	je voxskipslab1
+
+	push eax
+	push ebx
+	push ecx
+	push edi
+voxbegslab1:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal4: mov al, byte [eax+88888888h]
+	mov byte [edi], al
+voxbpl4: add edi, 88888888h
+	dec ecx
+	jnz voxbegslab1
+	pop edi
+	pop ecx
+	pop ebx
+	pop eax
+	inc edi
+
+voxskipslab1:
+	push eax
+	test edi, 2
+	jz voxskipslab2
+	dec eax
+	cmp edi, eax
+	jge voxskipslab2
+
+	push ebx
+	push ecx
+	push edi
+voxbegslab2:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal5: mov al, byte [eax+88888888h]
+	mov ah, al
+	mov word [edi], ax
+voxbpl5: add edi, 88888888h
+	dec ecx
+	jnz voxbegslab2
+	pop edi
+	pop ecx
+	pop ebx
+	add edi, 2
+
+voxskipslab2:
+	mov eax, [esp]
+
+	sub eax, 3
+	cmp edi, eax
+	jge voxskipslab3
+
+voxprebegslab3:
+	push ebx
+	push ecx
+	push edi
+voxbegslab3:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal6: mov al, byte [eax+88888888h]
+	mov ah, al
+	shl eax, 8
+	mov al, ah
+	shl eax, 8
+	mov al, ah
+	mov dword [edi], eax
+voxbpl6: add edi, 88888888h
+	dec ecx
+	jnz voxbegslab3
+	pop edi
+	pop ecx
+	pop ebx
+	add edi, 4
+
+	mov eax, [esp]
+
+	sub eax, 3
+	cmp edi, eax
+	jl voxprebegslab3
+
+voxskipslab3:
+	mov eax, [esp]
+
+	dec eax
+	cmp edi, eax
+	jge voxskipslab4
+
+	push ebx
+	push ecx
+	push edi
+voxbegslab4:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal7: mov al, byte [eax+88888888h]
+	mov ah, al
+	mov word [edi], ax
+voxbpl7: add edi, 88888888h
+	dec ecx
+	jnz voxbegslab4
+	pop edi
+	pop ecx
+	pop ebx
+	add edi, 2
+
+voxskipslab4:
+	pop eax
+
+	cmp edi, eax
+	je voxskipslab5
+
+voxbegslab5:
+	mov ebp, ebx
+	add ebx, edx
+	shr ebp, 16
+	xor eax, eax
+	mov al, byte [esi+ebp]
+voxpal8: mov al, byte [eax+88888888h]
+	mov byte [edi], al
+voxbpl8: add edi, 88888888h
+	dec ecx
+	jnz voxbegslab5
+
+voxskipslab5:
+	pop ebp
+	ret
+*/
+} /* drawslab */
+
+/* #pragma aux stretchhline parm [eax][ebx][ecx][edx][esi][edi] */
+long stretchhline(long i1, long i2, long i3, long i4, long i5, long i6)
+{
+    long retval = 0;
+    /*
+    __asm__ __volatile__ (
+        "call _asm_stretchhline   \n\t"
+       : "=a" (retval)
+        : "a" (i1), "b" (i2), "c" (i3), "d" (i4), "S" (i5), "D" (i6)
+        : "cc", "memory");
+	*/
+    return(retval);
+/*
+_asm_stretchhline:
+	push ebp
+
+	mov eax, ebx
+	shl ebx, 16
+	sar eax, 16
+	and ecx, 0000ffffh
+	or ecx, ebx
+
+	add esi, eax
+	mov eax, edx
+	mov edx, esi
+
+	mov ebp, eax
+	shl eax, 16
+	sar ebp, 16
+
+	add ecx, eax
+	adc esi, ebp
+
+	add eax, eax
+	adc ebp, ebp
+	mov dword [loinc1+2], eax
+	mov dword [loinc2+2], eax
+	mov dword [loinc3+2], eax
+	mov dword [loinc4+2], eax
+
+	inc ch
+
+	jmp begloop
+
+begloop:
+	mov al, [edx]
+loinc1: sub ebx, 88888888h
+	sbb edx, ebp
+	mov ah, [esi]
+loinc2: sub ecx, 88888888h
+	sbb esi, ebp
+	sub edi, 4
+	shl eax, 16
+loinc3: sub ebx, 88888888h
+	mov al, [edx]
+	sbb edx, ebp
+	mov ah, [esi]
+loinc4: sub ecx, 88888888h
+	sbb esi, ebp
+	mov [edi], eax
+	dec cl
+	jnz begloop
+	dec ch
+	jnz begloop
+
+	pop ebp
+	ret
+*/
+} /* stretchhline */
+#else // #if (!defined USE_I386_ASM)
+#pragma message ("USING ASM VERSION")
+#endif // #if (!defined USE_I386_ASM)
--- /dev/null
+++ b/Engine/src/a.h
@@ -1,0 +1,181 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file IS NOT A PART OF Ken Silverman's original release
+ */
+
+#ifndef _INCLUDE_A_H_
+#define _INCLUDE_A_H_
+
+#if (defined __WATCOMC__)
+#error Do not include this header with Watcom C.
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+long mmxoverlay(void);
+long sethlinesizes(long,long,long);
+long setpalookupaddress(char *);
+long setuphlineasm4(long,long);
+long hlineasm4(long,long,long,long,long,long);
+long setuprhlineasm4(long,long,long,long,long,long);
+long rhlineasm4(long,long,long,long,long,long);
+long setuprmhlineasm4(long,long,long,long,long,long);
+long rmhlineasm4(long,long,long,long,long,long);
+long setupqrhlineasm4(long,long,long,long,long,long);
+long qrhlineasm4(long,long,long,long,long,long);
+long setvlinebpl(long);
+long fixtransluscence(long);
+long prevlineasm1(long,long,long,long,long,long);
+long vlineasm1(long,long,long,long,long,long);
+long setuptvlineasm(long);
+long tvlineasm1(long,long,long,long,long,long);
+long setuptvlineasm2(long,long,long);
+long tvlineasm2(long,long,long,long,long,long);
+long mvlineasm1(long,long,long,long,long,long);
+long setupvlineasm(long);
+long vlineasm4(long,long);
+long setupmvlineasm(long);
+long mvlineasm4(long,long);
+void setupspritevline(long,long,long,long,long,long);
+void spritevline(long,long,long,long,long,long);
+void msetupspritevline(long,long,long,long,long,long);
+void mspritevline(long,long,long,long,long,long);
+void tsetupspritevline(long,long,long,long,long,long);
+void tspritevline(long,long,long,long,long,long);
+long mhline(long,long,long,long,long,long);
+long mhlineskipmodify(long,long,long,long,long,long);
+long msethlineshift(long,long);
+long thline(long,long,long,long,long,long);
+long thlineskipmodify(long,long,long,long,long,long);
+long tsethlineshift(long,long);
+long setupslopevlin(long,long,long);
+long slopevlin(long,long,long,long,long,long);
+long settransnormal(void);
+long settransreverse(void);
+long setupdrawslab(long,long);
+long drawslab(long,long,long,long,long,long);
+long stretchhline(long,long,long,long,long,long);
+long is_vmware_running(void);
+
+    /* !!! This part might be better stated as "USE_ASM".  --ryan. */
+#ifdef USE_I386_ASM
+  long asm_mmxoverlay(void);
+  long asm_sethlinesizes(long,long,long);
+  long asm_setpalookupaddress(char *);
+  long asm_setuphlineasm4(long,long);
+  long asm_hlineasm4(long,long,long,long,long,long);
+  long asm_setuprhlineasm4(long,long,long,long,long,long);
+  long asm_rhlineasm4(long,long,long,long,long,long);
+  long asm_setuprmhlineasm4(long,long,long,long,long,long);
+  long asm_rmhlineasm4(long,long,long,long,long,long);
+  long asm_setupqrhlineasm4(long,long,long,long,long,long);
+  long asm_qrhlineasm4(long,long,long,long,long,long);
+  long asm_setvlinebpl(long);
+  long asm_fixtransluscence(long);
+  long asm_prevlineasm1(long,long,long,long,long,long);
+  long asm_vlineasm1(long,long,long,long,long,long);
+  long asm_setuptvlineasm(long);
+  long asm_tvlineasm1(long,long,long,long,long,long);
+  long asm_setuptvlineasm2(long,long,long);
+  long asm_tvlineasm2(long,long,long,long,long,long);
+  long asm_mvlineasm1(long,long,long,long,long,long);
+  long asm_setupvlineasm(long);
+  long asm_vlineasm4(long,long);
+  long asm_setupmvlineasm(long);
+  long asm_mvlineasm4(long,long);
+  void asm_setupspritevline(long,long,long,long,long,long);
+  void asm_spritevline(long,long,long,long,long,long);
+  void asm_msetupspritevline(long,long,long,long,long,long);
+  void asm_mspritevline(long,long,long,long,long,long);
+  void asm_tsetupspritevline(long,long,long,long,long,long);
+  void asm_tspritevline(long,long,long,long,long,long);
+  long asm_mhline(long,long,long,long,long,long);
+  long asm_mhlineskipmodify(long,long,long,long,long,long);
+  long asm_msethlineshift(long,long);
+  long asm_thline(long,long,long,long,long,long);
+  long asm_thlineskipmodify(long,long,long,long,long,long);
+  long asm_tsethlineshift(long,long);
+  long asm_setupslopevlin(long,long,long);
+  long asm_slopevlin(long,long,long,long,long,long);
+  long asm_settransnormal(void);
+  long asm_settransreverse(void);
+  long asm_setupdrawslab(long,long);
+  long asm_drawslab(long,long,long,long,long,long);
+  long asm_stretchhline(long,long,long,long,long,long);
+  long asm_isvmwarerunning(void);
+
+  /*
+   * !!! I need a reference to this, for mprotect(), but the actual function
+   * !!!  is never called in BUILD...just from other ASM routines. --ryan.
+   */
+  long asm_prohlineasm4(void);
+
+  #if ((defined __GNUC__) && (!defined C_IDENTIFIERS_UNDERSCORED))
+
+    long asm_mmxoverlay(void) __attribute__ ((alias ("_asm_mmxoverlay")));
+    long asm_sethlinesizes(long,long,long) __attribute__ ((alias ("_asm_sethlinesizes")));
+    long asm_setpalookupaddress(char *) __attribute__ ((alias ("_asm_setpalookupaddress")));
+    long asm_setuphlineasm4(long,long) __attribute__ ((alias ("_asm_setuphlineasm4")));
+    long asm_hlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_hlineasm4")));
+    long asm_setuprhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_setuprhlineasm4")));
+    long asm_rhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_rhlineasm4")));
+    long asm_setuprmhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_setuprmhlineasm4")));
+    long asm_rmhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_rmhlineasm4")));
+    long asm_setupqrhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_setupqrhlineasm4")));
+    long asm_qrhlineasm4(long,long,long,long,long,long) __attribute__ ((alias ("_asm_qrhlineasm4")));
+    long asm_setvlinebpl(long) __attribute__ ((alias ("_asm_setvlinebpl")));
+    long asm_fixtransluscence(long) __attribute__ ((alias ("_asm_fixtransluscence")));
+    long asm_prevlineasm1(long,long,long,long,long,long) __attribute__ ((alias ("_asm_prevlineasm1")));
+    long asm_vlineasm1(long,long,long,long,long,long) __attribute__ ((alias ("_asm_vlineasm1")));
+    long asm_setuptvlineasm(long) __attribute__ ((alias ("_asm_setuptvlineasm")));
+    long asm_tvlineasm1(long,long,long,long,long,long) __attribute__ ((alias ("_asm_tvlineasm1")));
+    long asm_setuptvlineasm2(long,long,long) __attribute__ ((alias ("_asm_setuptvlineasm2")));
+    long asm_tvlineasm2(long,long,long,long,long,long) __attribute__ ((alias ("_asm_tvlineasm2")));
+    long asm_mvlineasm1(long,long,long,long,long,long) __attribute__ ((alias ("_asm_mvlineasm1")));
+    long asm_setupvlineasm(long) __attribute__ ((alias ("_asm_setupvlineasm")));
+    long asm_vlineasm4(long,long) __attribute__ ((alias ("_asm_vlineasm4")));
+    long asm_setupmvlineasm(long) __attribute__ ((alias ("_asm_setupmvlineasm")));
+    long asm_mvlineasm4(long,long) __attribute__ ((alias ("_asm_mvlineasm4")));
+    void asm_setupspritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_setupspritevline")));
+    void asm_spritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_spritevline")));
+    void asm_msetupspritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_msetupspritevline")));
+    void asm_mspritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_mspritevline")));
+    void asm_tsetupspritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_tsetupspritevline")));
+    void asm_tspritevline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_tspritevline")));
+    long asm_mhline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_mhline")));
+    long asm_mhlineskipmodify(long,long,long,long,long,long) __attribute__ ((alias ("_asm_mhlineskipmodify")));
+    long asm_msethlineshift(long,long) __attribute__ ((alias ("_asm_msethlineshift")));
+    long asm_thline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_thline")));
+    long asm_thlineskipmodify(long,long,long,long,long,long) __attribute__ ((alias ("_asm_thlineskipmodify")));
+    long asm_tsethlineshift(long,long) __attribute__ ((alias ("_asm_tsethlineshift")));
+    long asm_setupslopevlin(long,long,long) __attribute__ ((alias ("_asm_setupslopevlin")));
+    long asm_slopevlin(long,long,long,long,long,long) __attribute__ ((alias ("_asm_slopevlin")));
+    long asm_settransnormal(void) __attribute__ ((alias ("_asm_settransnormal")));
+    long asm_settransreverse(void) __attribute__ ((alias ("_asm_settransreverse")));
+    long asm_setupdrawslab(long,long) __attribute__ ((alias ("_asm_setupdrawslab")));
+    long asm_drawslab(long,long,long,long,long,long) __attribute__ ((alias ("_asm_drawslab")));
+    long asm_stretchhline(long,long,long,long,long,long) __attribute__ ((alias ("_asm_stretchhline")));
+    long asm_isvmwarerunning(void) __attribute__ ((alias ("_asm_isvmwarerunning")));
+
+    /*
+	 * !!! I need a reference to this, for mprotect(), but the actual function
+     * !!!  is never called in BUILD...just from other ASM routines. --ryan.
+	 */
+    long asm_prohlineasm4(void) __attribute__ ((alias ("_asm_prohlineasm4")));
+
+  #endif /* ELF/GCC */
+#endif /* defined USE_I386_ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* include-once-blocker. */
+
+/* end of a.h ... */
+
+
--- /dev/null
+++ b/Engine/src/build.h
@@ -1,0 +1,316 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+#ifndef _INCLUDE_BUILD_H_
+#define _INCLUDE_BUILD_H_
+
+#define MAXSECTORS 1024
+#define MAXWALLS 8192
+#define MAXSPRITES 4096
+
+#define MAXTILES 9216
+#define MAXSTATUS 1024
+#define MAXPLAYERS 16
+#define MAXXDIM 1600
+#define MAXYDIM 1200
+#define MAXPALOOKUPS 256
+#define MAXPSKYTILES 256
+#define MAXSPRITESONSCREEN 1024
+
+#define CLIPMASK0 (((1L)<<16)+1L)
+#define CLIPMASK1 (((256L)<<16)+64L)
+
+
+	/*
+     * Make all variables in BUILD.H defined in the ENGINE,
+	 *  and externed in GAME
+     * (dear lord.  --ryan.)
+     */
+#ifdef ENGINE
+	#define EXTERN
+#else
+	#define EXTERN extern
+#endif
+
+
+#ifdef PLATFORM_DOS
+#pragma pack(push,1);
+#else
+#pragma pack(1)
+#endif
+
+/*
+ * ceilingstat/floorstat:
+ *   bit 0: 1 = parallaxing, 0 = not                                 "P"
+ *   bit 1: 1 = groudraw, 0 = not
+ *   bit 2: 1 = swap x&y, 0 = not                                    "F"
+ *   bit 3: 1 = double smooshiness                                   "E"
+ *   bit 4: 1 = x-flip                                               "F"
+ *   bit 5: 1 = y-flip                                               "F"
+ *   bit 6: 1 = Align texture to first wall of sector                "R"
+ *   bits 7-8:                                                       "T"
+ *          00 = normal floors
+ *          01 = masked floors
+ *          10 = transluscent masked floors
+ *          11 = reverse transluscent masked floors
+ *   bits 9-15: reserved
+ */
+
+	/* 40 bytes */
+typedef struct
+{
+	short wallptr, wallnum;
+	long ceilingz, floorz;
+	short ceilingstat, floorstat;
+	short ceilingpicnum, ceilingheinum;
+	signed char ceilingshade;
+	unsigned char ceilingpal, ceilingxpanning, ceilingypanning;
+	short floorpicnum, floorheinum;
+	signed char floorshade;
+	unsigned char floorpal, floorxpanning, floorypanning;
+	unsigned char visibility, filler;
+	short lotag, hitag, extra;
+} sectortype;
+
+/*
+ * cstat:
+ *   bit 0: 1 = Blocking wall (use with clipmove, getzrange)         "B"
+ *   bit 1: 1 = bottoms of invisible walls swapped, 0 = not          "2"
+ *   bit 2: 1 = align picture on bottom (for doors), 0 = top         "O"
+ *   bit 3: 1 = x-flipped, 0 = normal                                "F"
+ *   bit 4: 1 = masking wall, 0 = not                                "M"
+ *   bit 5: 1 = 1-way wall, 0 = not                                  "1"
+ *   bit 6: 1 = Blocking wall (use with hitscan / cliptype 1)        "H"
+ *   bit 7: 1 = Transluscence, 0 = not                               "T"
+ *   bit 8: 1 = y-flipped, 0 = normal                                "F"
+ *   bit 9: 1 = Transluscence reversing, 0 = normal                  "T"
+ *   bits 10-15: reserved
+ */
+
+	/* 32 bytes */
+typedef struct
+{
+	long x, y;
+	short point2, nextwall, nextsector, cstat;
+	short picnum, overpicnum;
+	signed char shade;
+	char pal, xrepeat, yrepeat, xpanning, ypanning;
+	short lotag, hitag, extra;
+} walltype;
+
+
+/*
+ * cstat:
+ *   bit 0: 1 = Blocking sprite (use with clipmove, getzrange)       "B"
+ *   bit 1: 1 = transluscence, 0 = normal                            "T"
+ *   bit 2: 1 = x-flipped, 0 = normal                                "F"
+ *   bit 3: 1 = y-flipped, 0 = normal                                "F"
+ *   bits 5-4: 00 = FACE sprite (default)                            "R"
+ *             01 = WALL sprite (like masked walls)
+ *             10 = FLOOR sprite (parallel to ceilings&floors)
+ *   bit 6: 1 = 1-sided sprite, 0 = normal                           "1"
+ *   bit 7: 1 = Real centered centering, 0 = foot center             "C"
+ *   bit 8: 1 = Blocking sprite (use with hitscan / cliptype 1)      "H"
+ *   bit 9: 1 = Transluscence reversing, 0 = normal                  "T"
+ *   bits 10-14: reserved
+ *   bit 15: 1 = Invisible sprite, 0 = not invisible
+ */
+
+	/* 44 bytes */
+typedef struct
+{
+	long x, y, z;
+	short cstat, picnum;
+	signed char shade;
+	unsigned char pal, clipdist, filler;
+	unsigned char xrepeat, yrepeat;
+	signed char xoffset, yoffset;
+	short sectnum, statnum;
+	short ang, owner, xvel, yvel, zvel;
+	short lotag, hitag, extra;
+} spritetype;
+
+#ifdef PLATFORM_DOS
+#pragma pack(pop);
+#else
+#pragma pack()
+#endif
+
+EXTERN sectortype sector[MAXSECTORS];
+EXTERN walltype wall[MAXWALLS];
+EXTERN spritetype sprite[MAXSPRITES];
+
+EXTERN unsigned short mapCRC;
+
+EXTERN long spritesortcnt;
+EXTERN spritetype tsprite[MAXSPRITESONSCREEN];
+
+EXTERN char vidoption;
+EXTERN long xdim, ydim, ylookup[MAXYDIM+1], numpages;
+EXTERN long yxaspect, viewingrange;
+
+EXTERN long validmodecnt;
+EXTERN short validmode[256];
+EXTERN long validmodexdim[256], validmodeydim[256];
+
+EXTERN short numsectors, numwalls;
+EXTERN volatile long totalclock;
+EXTERN long numframes, randomseed;
+EXTERN short sintable[2048];
+EXTERN unsigned char palette[768];
+EXTERN short numpalookups;
+EXTERN char *palookup[MAXPALOOKUPS];
+EXTERN char parallaxtype, showinvisibility;
+EXTERN long parallaxyoffs, parallaxyscale;
+EXTERN long visibility, parallaxvisibility;
+
+EXTERN long windowx1, windowy1, windowx2, windowy2;
+EXTERN short startumost[MAXXDIM], startdmost[MAXXDIM];
+
+EXTERN short pskyoff[MAXPSKYTILES], pskybits;
+
+EXTERN short headspritesect[MAXSECTORS+1], headspritestat[MAXSTATUS+1];
+EXTERN short prevspritesect[MAXSPRITES], prevspritestat[MAXSPRITES];
+EXTERN short nextspritesect[MAXSPRITES], nextspritestat[MAXSPRITES];
+
+EXTERN short tilesizx[MAXTILES], tilesizy[MAXTILES];
+EXTERN char walock[MAXTILES];
+EXTERN long numtiles, picanm[MAXTILES], waloff[MAXTILES];
+
+    /*
+	 * These variables are for auto-mapping with the draw2dscreen function.
+	 * When you load a new board, these bits are all set to 0 - since
+	 * you haven't mapped out anything yet.  Note that these arrays are
+	 * bit-mapped.
+	 * If you want draw2dscreen() to show sprite #54 then you say:
+	 *    spritenum = 54;
+	 *    show2dsprite[spritenum>>3] |= (1<<(spritenum&7));
+	 * And if you want draw2dscreen() to not show sprite #54 then you say:
+	 *    spritenum = 54;
+	 *    show2dsprite[spritenum>>3] &= ~(1<<(spritenum&7));
+	 * Automapping defaults to 0 (do nothing).  If you set automapping to 1,
+	 *    then in 3D mode, the walls and sprites that you see will show up the
+	 *    next time you flip to 2D mode.
+     */
+EXTERN char show2dsector[(MAXSECTORS+7)>>3];
+EXTERN char show2dwall[(MAXWALLS+7)>>3];
+EXTERN char show2dsprite[(MAXSPRITES+7)>>3];
+EXTERN char automapping;
+
+EXTERN char gotpic[(MAXTILES+7)>>3];
+EXTERN char gotsector[(MAXSECTORS+7)>>3];
+
+/*************************************************************************
+POSITION VARIABLES:
+
+		POSX is your x - position ranging from 0 to 65535
+		POSY is your y - position ranging from 0 to 65535
+			(the length of a side of the grid in EDITBORD would be 1024)
+		POSZ is your z - position (height) ranging from 0 to 65535, 0 highest.
+		ANG is your angle ranging from 0 to 2047.  Instead of 360 degrees, or
+			 2 * PI radians, I use 2048 different angles, so 90 degrees would
+			 be 512 in my system.
+
+SPRITE VARIABLES:
+
+	EXTERN short headspritesect[MAXSECTORS+1], headspritestat[MAXSTATUS+1];
+	EXTERN short prevspritesect[MAXSPRITES], prevspritestat[MAXSPRITES];
+	EXTERN short nextspritesect[MAXSPRITES], nextspritestat[MAXSPRITES];
+
+	Example: if the linked lists look like the following:
+		 ��������������������������������������������������������������Ŀ
+		 �      Sector lists:               Status lists:               �
+		 ��������������������������������������������������������������Ĵ
+		 �  Sector0:  4, 5, 8             Status0:  2, 0, 8             �
+		 �  Sector1:  16, 2, 0, 7         Status1:  4, 5, 16, 7, 3, 9   �
+		 �  Sector2:  3, 9                                              �
+		 ����������������������������������������������������������������
+	Notice that each number listed above is shown exactly once on both the
+		left and right side.  This is because any sprite that exists must
+		be in some sector, and must have some kind of status that you define.
+
+
+Coding example #1:
+	To go through all the sprites in sector 1, the code can look like this:
+
+		sectnum = 1;
+		i = headspritesect[sectnum];
+		while (i != -1)
+		{
+			nexti = nextspritesect[i];
+
+			//your code goes here
+			//ex: printf("Sprite %d is in sector %d\n",i,sectnum);
+
+			i = nexti;
+		}
+
+Coding example #2:
+	To go through all sprites with status = 1, the code can look like this:
+
+		statnum = 1;        //status 1
+		i = headspritestat[statnum];
+		while (i != -1)
+		{
+			nexti = nextspritestat[i];
+
+			//your code goes here
+			//ex: printf("Sprite %d has a status of 1 (active)\n",i,statnum);
+
+			i = nexti;
+		}
+
+			 insertsprite(short sectnum, short statnum);
+			 deletesprite(short spritenum);
+			 changespritesect(short spritenum, short newsectnum);
+			 changespritestat(short spritenum, short newstatnum);
+
+TILE VARIABLES:
+		NUMTILES - the number of tiles found TILES.DAT.
+		TILESIZX[MAXTILES] - simply the x-dimension of the tile number.
+		TILESIZY[MAXTILES] - simply the y-dimension of the tile number.
+		WALOFF[MAXTILES] - the actual 32-bit offset pointing to the top-left
+								 corner of the tile.
+		PICANM[MAXTILES] - flags for animating the tile.
+
+TIMING VARIABLES:
+		TOTALCLOCK - When the engine is initialized, TOTALCLOCK is set to zero.
+			From then on, it is incremented 120 times a second by 1.  That
+			means that the number of seconds elapsed is totalclock / 120.
+		NUMFRAMES - The number of times the draw3dscreen function was called
+			since the engine was initialized.  This helps to determine frame
+			rate.  (Frame rate = numframes * 120 / totalclock.)
+
+OTHER VARIABLES:
+
+		STARTUMOST[320] is an array of the highest y-coordinates on each column
+				that my engine is allowed to write to.  You need to set it only
+				once.
+		STARTDMOST[320] is an array of the lowest y-coordinates on each column
+				that my engine is allowed to write to.  You need to set it only
+				once.
+		SINTABLE[2048] is a sin table with 2048 angles rather than the
+			normal 360 angles for higher precision.  Also since SINTABLE is in
+			all integers, the range is multiplied by 16383, so instead of the
+			normal -1<sin(x)<1, the range of sintable is -16383<sintable[]<16383
+			If you use this sintable, you can possibly speed up your code as
+			well as save space in memory.  If you plan to use sintable, 2
+			identities you may want to keep in mind are:
+				sintable[ang&2047]       = sin(ang * (3.141592/1024)) * 16383
+				sintable[(ang+512)&2047] = cos(ang * (3.141592/1024)) * 16383
+		NUMSECTORS - the total number of existing sectors.  Modified every time
+			you call the loadboard function.
+***************************************************************************/
+
+#define PORTSIG  "Port by Ryan C. Gordon, Andrew Henderson, Dan Olson, and a cast of thousands."
+
+#endif  /* defined _INCLUDE_BUILD_H_ */
+
+/* end of build.h ... */
+
+
+
--- /dev/null
+++ b/Engine/src/cache1d.c
@@ -1,0 +1,1009 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "platform.h"
+#include "display.h"
+
+#include "pragmas.h"
+#include "cache1d.h"
+#include "build.h"
+
+#include "../../Game/src/cvar_defs.h"
+
+
+#if (defined USE_PHYSICSFS)
+#include "physfs.h"
+#endif
+
+/*
+ *   This module keeps track of a standard linear cacheing system.
+ *   To use this module, here's all you need to do:
+ *
+ *   Step 1: Allocate a nice BIG buffer, like from 1MB-4MB and
+ *           Call initcache(long cachestart, long cachesize) where
+ *
+ *              cachestart = (long)(pointer to start of BIG buffer)
+ *              cachesize = length of BIG buffer
+ *
+ *   Step 2: Call allocache(long *bufptr, long bufsiz, char *lockptr)
+ *              whenever you need to allocate a buffer, where:
+ *
+ *              *bufptr = pointer to 4-byte pointer to buffer
+ *                 Confused?  Using this method, cache2d can remove
+ *                 previously allocated things from the cache safely by
+ *                 setting the 4-byte pointer to 0.
+ *              bufsiz = number of bytes to allocate
+ *              *lockptr = pointer to locking char which tells whether
+ *                 the region can be removed or not.  If *lockptr = 0 then
+ *                 the region is not locked else its locked.
+ *
+ *   Step 3: If you need to remove everything from the cache, or every
+ *           unlocked item from the cache, you can call uninitcache();
+ *              Call uninitcache(0) to remove all unlocked items, or
+ *              Call uninitcache(1) to remove everything.
+ *           After calling uninitcache, it is still ok to call allocache
+ *           without first calling initcache.
+ */
+
+#define MAXCACHEOBJECTS 9216
+
+static long cachesize = 0;
+long cachecount = 0;
+unsigned char zerochar = 0;
+long cachestart = 0, cacnum = 0, agecount = 0;
+typedef struct { long *hand, leng; unsigned char *lock; } cactype;
+cactype cac[MAXCACHEOBJECTS];
+long lockrecip[200];
+
+// TC game directory
+char game_dir[512] = { '\0' };
+
+void initcache(long dacachestart, long dacachesize)
+{
+	long i;
+
+	for(i=1;i<200;i++) lockrecip[i] = (1<<28)/(200-i);
+
+	cachestart = dacachestart;
+	cachesize = dacachesize;
+
+	cac[0].leng = cachesize;
+	cac[0].lock = &zerochar;
+	cacnum = 1;
+}
+
+void allocache (long *newhandle, long newbytes, unsigned char *newlockptr)
+{
+	long i, z, zz, bestz=0, daval, bestval, besto=0, o1, o2, sucklen, suckz;
+
+	newbytes = ((newbytes+15)&0xfffffff0);
+
+	if ((unsigned)newbytes > (unsigned)cachesize)
+	{
+		printf("Cachesize: %ld\n",cachesize);
+		printf("*Newhandle: 0x%x, Newbytes: %ld, *Newlock: %d\n",(unsigned int)newhandle,newbytes,*newlockptr);
+		reportandexit("BUFFER TOO BIG TO FIT IN CACHE!\n");
+	}
+
+	if (*newlockptr == 0)
+	{
+		reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!\n");
+	}
+
+		/* Find best place */
+	bestval = 0x7fffffff; o1 = cachesize;
+	for(z=cacnum-1;z>=0;z--)
+	{
+		o1 -= cac[z].leng;
+		o2 = o1+newbytes; if (o2 > cachesize) continue;
+
+		daval = 0;
+		for(i=o1,zz=z;i<o2;i+=cac[zz++].leng)
+		{
+			if (*cac[zz].lock == 0) continue;
+			if (*cac[zz].lock >= 200) { daval = 0x7fffffff; break; }
+			daval += (long) mulscale32(cac[zz].leng+65536,lockrecip[*cac[zz].lock]);
+			if (daval >= bestval) break;
+		}
+		if (daval < bestval)
+		{
+			bestval = daval; besto = o1; bestz = z;
+			if (bestval == 0) break;
+		}
+	}
+
+	/*printf("%ld %ld %ld\n",besto,newbytes,*newlockptr);*/
+
+	if (bestval == 0x7fffffff)
+		reportandexit("CACHE SPACE ALL LOCKED UP!\n");
+
+		/* Suck things out */
+	for(sucklen=-newbytes,suckz=bestz;sucklen<0;sucklen+=cac[suckz++].leng)
+		if (*cac[suckz].lock) *cac[suckz].hand = 0;
+
+		/* Remove all blocks except 1 */
+	suckz -= (bestz+1); cacnum -= suckz;
+	copybufbyte(&cac[bestz+suckz],&cac[bestz],(cacnum-bestz)*sizeof(cactype));
+	cac[bestz].hand = newhandle; *newhandle = cachestart+besto;
+	cac[bestz].leng = newbytes;
+	cac[bestz].lock = newlockptr;
+	cachecount++;
+
+		/* Add new empty block if necessary */
+	if (sucklen <= 0) return;
+
+	bestz++;
+	if (bestz == cacnum)
+	{
+		cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)\n");
+		cac[bestz].leng = sucklen;
+		cac[bestz].lock = &zerochar;
+		return;
+	}
+
+	if (*cac[bestz].lock == 0) { cac[bestz].leng += sucklen; return; }
+
+	cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)\n");
+	for(z=cacnum-1;z>bestz;z--) cac[z] = cac[z-1];
+	cac[bestz].leng = sucklen;
+	cac[bestz].lock = &zerochar;
+}
+
+void suckcache (long *suckptr)
+{
+	long i;
+
+		/* Can't exit early, because invalid pointer might be same even though lock = 0 */
+	for(i=0;i<cacnum;i++)
+		if ((long)(*cac[i].hand) == (long)suckptr)
+		{
+			if (*cac[i].lock) *cac[i].hand = 0;
+			cac[i].lock = &zerochar;
+			cac[i].hand = 0;
+
+				/* Combine empty blocks */
+			if ((i > 0) && (*cac[i-1].lock == 0))
+			{
+				cac[i-1].leng += cac[i].leng;
+				cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
+			}
+			else if ((i < cacnum-1) && (*cac[i+1].lock == 0))
+			{
+				cac[i+1].leng += cac[i].leng;
+				cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
+			}
+		}
+}
+
+void agecache(void)
+{
+	long cnt;
+	char ch;
+
+	if (agecount >= cacnum) agecount = cacnum-1;
+	assert(agecount >= 0);
+
+	for(cnt=(cacnum>>4);cnt>=0;cnt--)
+	{
+		ch = (*cac[agecount].lock);
+		if (((ch-2)&255) < 198)
+			(*cac[agecount].lock) = (unsigned char) (ch-1);
+
+		agecount--; if (agecount < 0) agecount = cacnum-1;
+	}
+}
+
+void reportandexit(char *errormessage)
+{
+	long i, j;
+
+	setvmode(0x3);
+	j = 0;
+	for(i=0;i<cacnum;i++)
+	{
+		printf("%ld- ",i);
+		printf("ptr: 0x%lx, ",*cac[i].hand);
+		printf("leng: %ld, ",cac[i].leng);
+		printf("lock: %d\n",*cac[i].lock);
+		j += cac[i].leng;
+	}
+	printf("Cachesize = %ld\n",cachesize);
+	printf("Cacnum = %ld\n",cacnum);
+	printf("Cache length sum = %ld\n",j);
+	printf("ERROR: %s",errormessage);
+	Error(EXIT_FAILURE, "");
+}
+
+#if (!defined USE_PHYSICSFS)
+unsigned char toupperlookup[256] =
+{
+	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+	0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
+	0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f,
+	0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+	0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+	0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
+	0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
+	0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+	0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
+	0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
+	0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
+};
+
+long numgroupfiles = 0;			// number of GRP files actually used.
+long gnumfiles[MAXGROUPFILES];	// number of files on grp
+long groupfil[MAXGROUPFILES] = {-1,-1,-1,-1}; // grp file handles
+long groupfilpos[MAXGROUPFILES];
+char *gfilelist[MAXGROUPFILES];	// name list + size list of all the files in grp
+long *gfileoffs[MAXGROUPFILES];	// offset of the files
+char *groupfil_memory[MAXGROUPFILES]; // addresses of raw GRP files in memory
+long groupefil_crc32[MAXGROUPFILES];
+
+unsigned char filegrp[MAXOPENFILES];
+long filepos[MAXOPENFILES];
+long filehan[MAXOPENFILES] =
+{
+	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+};
+
+#else
+static PHYSFS_file *filehan[MAXOPENFILES] =
+{
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+#endif
+
+
+long initgroupfile(const char *filename)
+{
+#if (defined USE_PHYSICSFS)
+    static int initted_physfs = 0;
+    static int added_cwd = 0;
+
+    if (!initted_physfs)
+    {
+        if (!PHYSFS_init(_argv[0]))
+            return(-1);
+        initted_physfs = 1;
+    } /* if */
+
+    if (!added_cwd)
+    {
+        if (!PHYSFS_addToSearchPath(".", 0))
+            return(-1);
+        added_cwd = 1;
+    } /* if */
+
+    if (!PHYSFS_addToSearchPath(filename, 1))
+        return(-1);
+
+    return(1); /* uhh...? */
+#else
+	char buf[16];
+	long i, j, k;
+
+	printf("Loading %s ...\n", filename);
+
+	if (numgroupfiles >= MAXGROUPFILES) return(-1);
+
+	groupfil_memory[numgroupfiles] = NULL; // addresses of raw GRP files in memory
+	groupefil_crc32[numgroupfiles] = 0;
+
+	groupfil[numgroupfiles] = open(filename,O_BINARY|O_RDWR,S_IREAD);
+	if (groupfil[numgroupfiles] >= 0)
+	{
+		groupfilpos[numgroupfiles] = 0;
+		read(groupfil[numgroupfiles],buf,16);
+		if ((buf[0] != 'K') || (buf[1] != 'e') || (buf[2] != 'n') ||
+			 (buf[3] != 'S') || (buf[4] != 'i') || (buf[5] != 'l') ||
+			 (buf[6] != 'v') || (buf[7] != 'e') || (buf[8] != 'r') ||
+			 (buf[9] != 'm') || (buf[10] != 'a') || (buf[11] != 'n'))
+		{
+			close(groupfil[numgroupfiles]);
+			groupfil[numgroupfiles] = -1;
+			return(-1);
+		}
+
+		//The ".grp" file format is just a collection of a lot of files stored into 1 big one. 
+		//I tried to make the format as simple as possible: The first 12 bytes contains my name, 
+		//"KenSilverman". The next 4 bytes is the number of files that were compacted into the 
+		//group file. Then for each file, there is a 16 byte structure, where the first 12 
+		//bytes are the filename, and the last 4 bytes are the file's size. The rest of the 
+		//group file is just the raw data packed one after the other in the same order as the list 
+		//of files. - ken
+
+		gnumfiles[numgroupfiles] = BUILDSWAP_INTEL32(*((long *)&buf[12]));
+
+		if ((gfilelist[numgroupfiles] = (char *)kmalloc(gnumfiles[numgroupfiles]<<4)) == 0)
+			{ Error(EXIT_FAILURE, "Not enough memory for file grouping system\n"); }
+		if ((gfileoffs[numgroupfiles] = (long *)kmalloc((gnumfiles[numgroupfiles]+1)<<2)) == 0)
+			{ Error(EXIT_FAILURE, "Not enough memory for file grouping system\n"); }
+
+		// load index (name+size)
+		read(groupfil[numgroupfiles],gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
+
+		j = 0;
+		for(i=0;i<gnumfiles[numgroupfiles];i++)
+		{
+			k = BUILDSWAP_INTEL32(*((long *)&gfilelist[numgroupfiles][(i<<4)+12])); // get size
+			gfilelist[numgroupfiles][(i<<4)+12] = 0;
+			gfileoffs[numgroupfiles][i] = j; // absolute offset list of all files. 0 for 1st file
+			j += k;
+		}
+		gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
+	}
+
+	// Compute CRC32 of thw whole grp and implicitely caches the GRP in memory through windows
+	lseek(groupfil[numgroupfiles], 0, SEEK_SET);
+
+	i = 1000000; // FIX_00086: grp loaded by smaller sucessive chunks to avoid overloading low ram computers
+	groupfil_memory[numgroupfiles] = malloc(i);
+	while(j=read(groupfil[numgroupfiles], groupfil_memory[numgroupfiles], i))
+	{
+		groupefil_crc32[numgroupfiles] = crc32_update(groupfil_memory[numgroupfiles], j, groupefil_crc32[numgroupfiles]);	
+	}
+
+	free(groupfil_memory[numgroupfiles]);  
+	groupfil_memory[numgroupfiles] = 0;
+
+	numgroupfiles++;
+	return(groupfil[numgroupfiles-1]);
+#endif
+}
+
+void uninitgroupfile(void)
+{
+#if (defined USE_PHYSICSFS)
+    PHYSFS_deinit();
+    memset(filehan, '\0', sizeof (filehan));
+#else
+	long i;
+
+	for(i=numgroupfiles-1;i>=0;i--)
+		if (groupfil[i] != -1)
+		{
+			kfree(gfilelist[i]);
+			kfree(gfileoffs[i]);
+			close(groupfil[i]);
+			groupfil[i] = -1;
+		}
+#endif
+}
+
+#if (defined USE_PHYSICSFS)
+static int locateOneElement(char *buf)
+{
+    char *ptr;
+    char **rc;
+    char **i;
+
+    if (PHYSFS_exists(buf))
+        return(1);  /* quick rejection: exists in current case. */
+
+    ptr = strrchr(buf, '/');  /* find entry at end of path. */
+    if (ptr == NULL)
+    {
+        rc = PHYSFS_enumerateFiles("/");
+        ptr = buf;
+    } /* if */
+    else
+    {
+        *ptr = '\0';
+        rc = PHYSFS_enumerateFiles(buf);
+        *ptr = '/';
+        ptr++;  /* point past dirsep to entry itself. */
+    } /* else */
+
+    for (i = rc; *i != NULL; i++)
+    {
+        if (stricmp(*i, ptr) == 0)
+        {
+            strcpy(ptr, *i); /* found a match. Overwrite with this case. */
+            PHYSFS_freeList(rc);
+            return(1);
+        } /* if */
+    } /* for */
+
+    /* no match at all... */
+    PHYSFS_freeList(rc);
+    return(0);
+} /* locateOneElement */
+
+
+int PHYSFSEXT_locateCorrectCase(char *buf)
+{
+    int rc;
+    char *ptr;
+    char *prevptr;
+
+    while (*buf == '/')  /* skip any '/' at start of string... */
+        buf++;
+
+    ptr = prevptr = buf;
+    if (*ptr == '\0')
+        return(0);  /* Uh...I guess that's success. */
+
+    while ((ptr = strchr(ptr + 1, '/')) != NULL)
+    {
+        *ptr = '\0';  /* block this path section off */
+        rc = locateOneElement(buf);
+        *ptr = '/'; /* restore path separator */
+        if (!rc)
+            return(-2);  /* missing element in path. */
+    } /* while */
+
+    /* check final element... */
+    return(locateOneElement(buf) ? 0 : -1);
+} /* PHYSFSEXT_locateCorrectCase */
+#endif
+
+
+void crc32_table_gen(unsigned int* crc32_table) /* build CRC32 table */
+{
+    unsigned int crc, poly;
+    int	i, j;
+
+    poly = 0xEDB88320L;
+    for (i = 0; i < 256; i++)
+    {
+		crc = i;
+		for (j = 8; j > 0; j--)
+		{
+			if (crc & 1)
+				crc = (crc >> 1) ^ poly;
+			else
+				crc >>= 1;
+		}
+		crc32_table[i] = crc;
+    }
+}
+
+unsigned int crc32(unsigned char *buf, unsigned int length)
+{
+	unsigned int initial_crc;
+
+	initial_crc = 0;
+	return(crc32_update(buf, length, initial_crc));
+}
+
+unsigned int crc32_update(unsigned char *buf, unsigned int length, unsigned int crc_to_update)
+{
+	unsigned int crc32_table[256];
+
+	crc32_table_gen(crc32_table);
+
+	crc_to_update ^= 0xFFFFFFFF;
+
+	while (length--)
+		crc_to_update = crc32_table[(crc_to_update ^ *buf++) & 0xFF] ^ (crc_to_update >> 8);
+	
+	return crc_to_update ^ 0xFFFFFFFF;
+}
+
+
+/*
+ *                                      16   12   5
+ * this is the CCITT CRC 16 polynomial X  + X  + X  + 1.
+ * This is 0x1021 when x is 2, but the way the algorithm works
+ * we use 0x8408 (the reverse of the bit pattern).  The high
+ * bit is always assumed to be set, thus we only use 16 bits to
+ * represent the 17 bit value.
+*/
+
+#define POLY 0x8408   /* 1021H bit reversed */
+
+unsigned short crc16(char *data_p, unsigned short length)
+{
+      unsigned char i;
+      unsigned int data;
+      unsigned int crc = 0xffff;
+
+      if (length == 0)
+            return (~crc);
+      do
+      {
+            for (i=0, data=(unsigned int)0xff & *data_p++;
+                 i < 8; 
+                 i++, data >>= 1)
+            {
+                  if ((crc & 0x0001) ^ (data & 0x0001))
+                        crc = (crc >> 1) ^ POLY;
+                  else  crc >>= 1;
+            }
+      } while (--length);
+
+      crc = ~crc;
+      data = crc;
+      crc = (crc << 8) | (data >> 8 & 0xff);
+
+      return (crc);
+}
+
+long kopen4load(const char *filename, int readfromGRP)
+{ // FIX_00072: all files are now 1st searched in Duke's root folder and then in the GRP.
+#if (defined USE_PHYSICSFS)
+    int i;
+    PHYSFS_file *rc;
+    char _filename[64];
+
+    assert(strlen(filename) < sizeof (_filename));
+    strcpy(_filename, filename);
+    PHYSFSEXT_locateCorrectCase(_filename);
+
+    rc = PHYSFS_openRead(_filename);
+    if (rc == NULL)
+        return(-1);
+
+    for (i = 0; i < MAXOPENFILES; i++)
+    {
+        if (filehan[i] == NULL)
+        {
+            filehan[i] = rc;
+            return(i);
+        }
+    }
+
+    PHYSFS_close(rc);  /* oh well. */
+    return(-1);
+#else
+	long i, j, k, fil, newhandle;
+	unsigned char bad;
+	char *gfileptr;
+
+	newhandle = MAXOPENFILES-1;
+	while (filehan[newhandle] != -1)
+	{
+		newhandle--;
+		if (newhandle < 0)
+		{
+			Error(EXIT_FAILURE, "Too Many files open!\n");
+		}
+	}
+
+	if(!readfromGRP)
+		if ((fil = open(filename,O_BINARY|O_RDONLY)) != -1)
+		{
+			filegrp[newhandle] = 255;
+			filehan[newhandle] = fil;
+			filepos[newhandle] = 0;
+			printf("Reading external %s \n", filename);
+			return(newhandle);
+		}
+
+	for(k=numgroupfiles-1;k>=0;k--)
+	{
+		if (groupfil[k] != -1)
+		{
+			for(i=gnumfiles[k]-1;i>=0;i--)
+			{
+				gfileptr = (char *)&gfilelist[k][i<<4];
+
+				bad = 0;
+				for(j=0;j<13;j++)
+				{
+					if (!filename[j]) break;
+					if (toupperlookup[(int) filename[j]] != toupperlookup[(int) gfileptr[j]])
+						{ bad = 1; break; }
+				}
+				if (bad) continue;
+
+				filegrp[newhandle] = (unsigned char) k;
+				filehan[newhandle] = i;
+				filepos[newhandle] = 0;
+				return(newhandle);
+			}
+		}
+	}
+	return(-1);
+#endif
+}
+
+long kread(long handle, void *buffer, long leng)
+{
+#if (defined USE_PHYSICSFS)
+    return(PHYSFS_read(filehan[handle], buffer, 1, leng));
+return(leng);
+#else
+	long i, filenum, groupnum;
+
+	filenum = filehan[handle];
+	groupnum = filegrp[handle];
+
+	if (groupnum == 255) // Reading external
+	{
+		return(read(filenum,buffer,leng));
+	}
+
+	if (groupfil[groupnum] != -1)
+	{
+		i = gfileoffs[groupnum][filenum]+filepos[handle];
+		if (i != groupfilpos[groupnum])
+		{
+			lseek(groupfil[groupnum],i+((gnumfiles[groupnum]+1)<<4),SEEK_SET);
+			groupfilpos[groupnum] = i;
+		}
+		leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-filepos[handle]);
+		leng = read(groupfil[groupnum],buffer,leng);
+		filepos[handle] += leng;
+		groupfilpos[groupnum] += leng;
+		return(leng);
+	}
+
+	return(0);
+#endif
+}
+
+int kread16(long handle, short *buffer)
+{
+    if (kread(handle, buffer, 2) != 2)
+        return(0);
+
+    *buffer = BUILDSWAP_INTEL16(*buffer);
+    return(1);
+}
+
+int kread32(long handle, long *buffer)
+{
+    if (kread(handle, buffer, 4) != 4)
+        return(0);
+
+    *buffer = BUILDSWAP_INTEL32(*buffer);
+    return(1);
+}
+
+int kread8(long handle, char *buffer)
+{
+    if (kread(handle, buffer, 1) != 1)
+        return(0);
+
+    return(1);
+}
+
+long klseek(long handle, long offset, long whence)
+{
+#if (defined USE_PHYSICSFS)
+    if (whence == SEEK_END)  /* !!! FIXME: You can try PHYSFS_filelength(). */
+    {
+		Error(EXIT_FAILURE, "Unsupported seek semantic!\n");
+    } /* if */
+
+    if (whence == SEEK_CUR)
+        offset += PHYSFS_tell(filehan[handle]);
+
+    if (!PHYSFS_seek(filehan[handle], offset))
+        return(-1);
+
+    return(offset);
+#else
+	long i, groupnum;
+
+	groupnum = filegrp[handle];
+
+	if (groupnum == 255) return(lseek(filehan[handle],offset,whence));
+	if (groupfil[groupnum] != -1)
+	{
+		switch(whence)
+		{
+			case SEEK_SET: filepos[handle] = offset; break;
+			case SEEK_END: i = filehan[handle];
+								filepos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
+								break;
+			case SEEK_CUR: filepos[handle] += offset; break;
+		}
+		return(filepos[handle]);
+	}
+	return(-1);
+#endif
+}
+
+long kfilelength(long handle)
+{
+#if (defined USE_PHYSICSFS)
+    return(PHYSFS_fileLength(filehan[handle]));
+#else
+	long i, groupnum;
+
+	groupnum = filegrp[handle];
+	if (groupnum == 255) return(filelength(filehan[handle]));
+	i = filehan[handle];
+	return(gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i]);
+#endif
+}
+
+void kclose(long handle)
+{
+#if (defined USE_PHYSICSFS)
+    if (filehan[handle] != NULL)
+    {
+        PHYSFS_close(filehan[handle]);
+        filehan[handle] = NULL;
+    } /* if */
+#else
+	if (handle < 0) return;
+	if (filegrp[handle] == 255) close(filehan[handle]);
+	filehan[handle] = -1;
+#endif
+}
+
+
+
+
+	/* Internal LZW variables */
+#define LZWSIZE 16384           /* Watch out for shorts! */
+static char *lzwbuf1, *lzwbuf4, *lzwbuf5;
+static unsigned char lzwbuflock[5];
+static short *lzwbuf2, *lzwbuf3;
+
+void kdfread(void *buffer, size_t dasizeof, size_t count, long fil)
+{
+	size_t i, j;
+	long k, kgoal;
+	short leng;
+	char *ptr;
+
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
+	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[0]);
+	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[1]);
+	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[2]);
+	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock[3]);
+	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[4]);
+
+	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
+	ptr = (char *)buffer;
+
+	kread(fil,&leng,2); kread(fil,lzwbuf5,(long)leng);
+	k = 0;
+	kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
+
+	copybufbyte(lzwbuf4,ptr,(long)dasizeof);
+	k += (long)dasizeof;
+
+	for(i=1;i<count;i++)
+	{
+		if (k >= kgoal)
+		{
+			kread(fil,&leng,2); kread(fil,lzwbuf5,(long)leng);
+			k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
+		}
+		for(j=0;j<dasizeof;j++) ptr[j+dasizeof] = (unsigned char) ((ptr[j]+lzwbuf4[j+k])&255);
+		k += dasizeof;
+		ptr += dasizeof;
+	}
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
+}
+
+void dfread(void *buffer, size_t dasizeof, size_t count, FILE *fil)
+{
+	size_t i, j;
+	long k, kgoal;
+	short leng;
+	char *ptr;
+
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
+	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[0]);
+	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[1]);
+	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[2]);
+	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock[3]);
+	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[4]);
+
+	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
+	ptr = (char *)buffer;
+
+	fread(&leng,2,1,fil); fread(lzwbuf5,(long)leng,1,fil);
+	k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
+
+	copybufbyte(lzwbuf4,ptr,(long)dasizeof);
+	k += (long)dasizeof;
+
+	for(i=1;i<count;i++)
+	{
+		if (k >= kgoal)
+		{
+			fread(&leng,2,1,fil); fread(lzwbuf5,(long)leng,1,fil);
+			k = 0; kgoal = uncompress(lzwbuf5,(long)leng,lzwbuf4);
+		}
+		for(j=0;j<dasizeof;j++) ptr[j+dasizeof] = (unsigned char) ((ptr[j]+lzwbuf4[j+k])&255);
+		k += dasizeof;
+		ptr += dasizeof;
+	}
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
+}
+
+void dfwrite(void *buffer, size_t dasizeof, size_t count, FILE *fil)
+{
+	size_t i, j, k;
+	short leng;
+	char *ptr;
+
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 200;
+	if (lzwbuf1 == NULL) allocache((long *)&lzwbuf1,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[0]);
+	if (lzwbuf2 == NULL) allocache((long *)&lzwbuf2,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[1]);
+	if (lzwbuf3 == NULL) allocache((long *)&lzwbuf3,(LZWSIZE+(LZWSIZE>>4))*2,&lzwbuflock[2]);
+	if (lzwbuf4 == NULL) allocache((long *)&lzwbuf4,LZWSIZE,&lzwbuflock[3]);
+	if (lzwbuf5 == NULL) allocache((long *)&lzwbuf5,LZWSIZE+(LZWSIZE>>4),&lzwbuflock[4]);
+
+	if (dasizeof > LZWSIZE) { count *= dasizeof; dasizeof = 1; }
+	ptr = (char *)buffer;
+
+	copybufbyte(ptr,lzwbuf4,(long)dasizeof);
+	k = dasizeof;
+
+	if (k > LZWSIZE-dasizeof)
+	{
+		leng = (short)compress(lzwbuf4,k,lzwbuf5); k = 0;
+		fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
+	}
+
+	for(i=1;i<count;i++)
+	{
+		for(j=0;j<dasizeof;j++) lzwbuf4[j+k] = (unsigned char) ((ptr[j+dasizeof]-ptr[j])&255);
+		k += dasizeof;
+		if (k > LZWSIZE-dasizeof)
+		{
+			leng = (short)compress(lzwbuf4,k,lzwbuf5); k = 0;
+			fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
+		}
+		ptr += dasizeof;
+	}
+	if (k > 0)
+	{
+		leng = (short)compress(lzwbuf4,k,lzwbuf5);
+		fwrite(&leng,2,1,fil); fwrite(lzwbuf5,(long)leng,1,fil);
+	}
+	lzwbuflock[0] = lzwbuflock[1] = lzwbuflock[2] = lzwbuflock[3] = lzwbuflock[4] = 1;
+}
+
+long compress(char *lzwinbuf, long uncompleng, char *lzwoutbuf)
+{
+	long i, addr, newaddr, addrcnt, zx, *longptr;
+	long bytecnt1, bitcnt, numbits, oneupnumbits;
+	short *shortptr;
+
+	for(i=255;i>=0;i--) { lzwbuf1[i] = (char) i; lzwbuf3[i] = (short) ((i+1)&255); }
+	clearbuf((void *) FP_OFF(lzwbuf2),256>>1,0xffffffff);
+	clearbuf((void *) FP_OFF(lzwoutbuf),((uncompleng+15)+3)>>2,0L);
+
+	addrcnt = 256; bytecnt1 = 0; bitcnt = (4<<3);
+	numbits = 8; oneupnumbits = (1<<8);
+	do
+	{
+		addr = lzwinbuf[bytecnt1];
+		do
+		{
+			bytecnt1++;
+			if (bytecnt1 == uncompleng) break;
+			if (lzwbuf2[addr] < 0) {lzwbuf2[addr] = (short) addrcnt; break;}
+			newaddr = lzwbuf2[addr];
+			while (lzwbuf1[newaddr] != lzwinbuf[bytecnt1])
+			{
+				zx = lzwbuf3[newaddr];
+				if (zx < 0) {lzwbuf3[newaddr] = (short) addrcnt; break;}
+				newaddr = zx;
+			}
+			if (lzwbuf3[newaddr] == addrcnt) break;
+			addr = newaddr;
+		} while (addr >= 0);
+		lzwbuf1[addrcnt] = lzwinbuf[bytecnt1];
+		lzwbuf2[addrcnt] = -1;
+		lzwbuf3[addrcnt] = -1;
+
+		longptr = (long *)&lzwoutbuf[bitcnt>>3];
+		longptr[0] |= (addr<<(bitcnt&7));
+		bitcnt += numbits;
+		if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
+			bitcnt--;
+
+		addrcnt++;
+		if (addrcnt > oneupnumbits) { numbits++; oneupnumbits <<= 1; }
+	} while ((bytecnt1 < uncompleng) && (bitcnt < (uncompleng<<3)));
+
+	longptr = (long *)&lzwoutbuf[bitcnt>>3];
+	longptr[0] |= (addr<<(bitcnt&7));
+	bitcnt += numbits;
+	if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
+		bitcnt--;
+
+	shortptr = (short *)lzwoutbuf;
+	shortptr[0] = (short)uncompleng;
+	if (((bitcnt+7)>>3) < uncompleng)
+	{
+		shortptr[1] = (short)addrcnt;
+		return((bitcnt+7)>>3);
+	}
+	shortptr[1] = (short)0;
+	for(i=0;i<uncompleng;i++) lzwoutbuf[i+4] = lzwinbuf[i];
+	return(uncompleng+4);
+}
+
+long uncompress(char *lzwinbuf, long compleng, char *lzwoutbuf)
+{
+	long strtot, currstr, numbits, oneupnumbits;
+	long i, dat, leng, bitcnt, outbytecnt, *longptr;
+	short *shortptr;
+
+	shortptr = (short *)lzwinbuf;
+	strtot = (long)shortptr[1];
+	if (strtot == 0)
+	{
+		copybuf((void *)(FP_OFF(lzwinbuf)+4),(void *)(FP_OFF(lzwoutbuf)),((compleng-4)+3)>>2);
+		return((long)shortptr[0]); /* uncompleng */
+	}
+	for(i=255;i>=0;i--) { lzwbuf2[i] = (short) i; lzwbuf3[i] = (short) i; }
+	currstr = 256; bitcnt = (4<<3); outbytecnt = 0;
+	numbits = 8; oneupnumbits = (1<<8);
+	do
+	{
+		longptr = (long *)&lzwinbuf[bitcnt>>3];
+		dat = ((longptr[0]>>(bitcnt&7)) & (oneupnumbits-1));
+		bitcnt += numbits;
+		if ((dat&((oneupnumbits>>1)-1)) > ((currstr-1)&((oneupnumbits>>1)-1)))
+			{ dat &= ((oneupnumbits>>1)-1); bitcnt--; }
+
+		lzwbuf3[currstr] = (short) dat;
+
+		for(leng=0;dat>=256;leng++,dat=lzwbuf3[dat])
+			lzwbuf1[leng] = (char) lzwbuf2[dat];
+
+		lzwoutbuf[outbytecnt++] = (char) dat;
+		for(i=leng-1;i>=0;i--) lzwoutbuf[outbytecnt++] = lzwbuf1[i];
+
+		lzwbuf2[currstr-1] = (short) dat; lzwbuf2[currstr] = (short) dat;
+		currstr++;
+		if (currstr > oneupnumbits) { numbits++; oneupnumbits <<= 1; }
+	} while (currstr < strtot);
+	return((long)shortptr[0]); /* uncompleng */
+}
+
+
+long TCkopen4load(const char *filename, int readfromGRP)
+{
+	char fullfilename[512];
+	long result = 0;
+ 
+	if(game_dir[0] != '\0' && !readfromGRP)
+	{
+		sprintf(fullfilename, "%s\\%s", game_dir, filename);		
+		if (!SafeFileExists(fullfilename)) // try root
+			sprintf(fullfilename, "%s", filename);
+	}
+	else
+	{
+		sprintf(fullfilename, "%s", filename);
+	}
+
+	result = kopen4load(fullfilename, readfromGRP);
+
+	if(g_CV_DebugFileAccess != 0)
+	{
+		printf("FILE ACCESS: [read] File: (%s) Result: %d, clock: %d\n", fullfilename, result, totalclock);
+	}
+
+	return result;
+}
--- /dev/null
+++ b/Engine/src/cache1d.h
@@ -1,0 +1,54 @@
+/*
+ * Cache1d declarations.
+ *
+ *  Written by Ryan C. Gordon. (icculus@clutteredmind.org)
+ *
+ * Please do NOT harrass Ken Silverman about any code modifications
+ *  (including this file) to BUILD.
+ */
+
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+#ifndef _INCLUDE_CACHE1D_H_
+#define _INCLUDE_CACHE1D_H_
+
+#define MAXGROUPFILES 4     /* Warning: Fix groupfil if this is changed */
+#define MAXOPENFILES 64     /* Warning: Fix filehan if this is changed  */
+
+void initcache(long dacachestart, long dacachesize);
+void allocache (long *newhandle, long newbytes, unsigned char *newlockptr);
+void suckcache (long *suckptr);
+void agecache(void);
+void reportandexit(char *errormessage);
+long initgroupfile(const char *filename);
+void uninitgroupfile(void);
+unsigned short crc16(char *data_p, unsigned short length);
+unsigned int crc32_update(unsigned char *buf, unsigned int length, unsigned int crc_to_update);
+long kopen4load(const char *filename, int readfromGRP);
+long kread(long handle, void *buffer, long leng);
+int kread8(long handle, char *buffer);
+int kread16(long handle, short *buffer);
+int kread32(long handle, long *buffer);
+long klseek(long handle, long offset, long whence);
+long kfilelength(long handle);
+void kclose(long handle);
+void kdfread(void *buffer, size_t dasizeof, size_t count, long fil);
+void dfread(void *buffer, size_t dasizeof, size_t count, FILE *fil);
+void dfwrite(void *buffer, size_t dasizeof, size_t count, FILE *fil);
+long compress(char *lzwinbuf, long uncompleng, char *lzwoutbuf);
+long uncompress(char *lzwinbuf, long compleng, char *lzwoutbuf);
+
+extern char game_dir[512];
+extern long TCkopen4load(const char *filename, int readfromGRP);
+
+#endif  /* !defined _INCLUDE_CACHE1D_H_ */
+
+/* end of cache1d.h ... */
+
+
+
--- /dev/null
+++ b/Engine/src/display.h
@@ -1,0 +1,172 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file IS NOT A PART OF Ken Silverman's original release
+ */
+
+#ifndef _INCLUDE_DISPLAY_H_
+#define _INCLUDE_DISPLAY_H_
+
+#include "platform.h"
+
+#if (defined PLATFORM_SUPPORTS_SDL)
+#include "SDL.h"
+
+/* environment variables names... */
+/* These are only honored by the SDL driver. */
+#define BUILD_NOMOUSEGRAB    "BUILD_NOMOUSEGRAB"
+#define BUILD_WINDOWED       "BUILD_WINDOWED"
+#define BUILD_SDLDEBUG       "BUILD_SDLDEBUG"
+#define BUILD_RENDERER       "BUILD_RENDERER"
+#define BUILD_GLLIBRARY      "BUILD_GLLIBRARY"
+#define BUILD_USERSCREENRES  "BUILD_USERSCREENRES"
+#define BUILD_MAXSCREENRES   "BUILD_MAXSCREENRES"
+#define BUILD_HALLOFMIRRORS  "BUILD_HALLOFMIRRORS"
+#define BUILD_GLDUMP         "BUILD_GLDUMP"
+#define BUILD_SDLJOYSTICK    "BUILD_SDLJOYSTICK"
+
+/* This part sucks. */
+#if (defined __WATCOMC__)
+#pragma aux (__cdecl) SDL_Init;
+#pragma aux (__cdecl) SDL_PumpEvents;
+#pragma aux (__cdecl) SDL_PollEvent;
+#pragma aux (__cdecl) SDL_GetMouseState;
+#pragma aux (__cdecl) SDL_WM_GrabInput;
+#pragma aux (__cdecl) SDL_GetError;
+#pragma aux (__cdecl) SDL_SetEventFilter;
+#pragma aux (__cdecl) SDL_WM_SetCaption;
+#pragma aux (__cdecl) SDL_ClearError;
+#pragma aux (__cdecl) SDL_SetVideoMode;
+#pragma aux (__cdecl) SDL_ClearError;
+#pragma aux (__cdecl) SDL_Quit;
+#pragma aux (__cdecl) SDL_QuitSubSystem;
+#pragma aux (__cdecl) SDL_GetTicks;
+#pragma aux (__cdecl) SDL_GetVideoInfo;
+#pragma aux (__cdecl) SDL_ListModes;
+#pragma aux (__cdecl) SDL_SetColors;
+#pragma aux (__cdecl) SDL_ShowCursor;
+#pragma aux (__cdecl) SDL_LockSurface;
+#pragma aux (__cdecl) SDL_UnlockSurface;
+#pragma aux (__cdecl) SDL_FillRect;
+#pragma aux (__cdecl) SDL_Delay;
+#pragma aux (__cdecl) SDL_AddTimer;
+#pragma aux (__cdecl) SDL_RemoveTimer;
+#pragma aux (__cdecl) SDL_Flip;
+#pragma aux (__cdecl) SDL_UpdateRect;
+#pragma aux (__cdecl) SDL_GetVideoSurface;
+#pragma aux (__cdecl) SDL_VideoDriverName;
+#pragma aux (__cdecl) SDL_SetPalette;
+#pragma aux (__cdecl) SDL_VideoModeOK;
+#pragma aux (__cdecl) SDL_GetClipRect;
+#pragma aux (__cdecl) SDL_SetClipRect;
+#pragma aux (__cdecl) SDL_WM_ToggleFullScreen;
+#pragma aux (__cdecl) SDL_Linked_Version;
+#endif  /* defined __WATCOMC__ */
+
+#endif  /* defined PLATFORM_SUPPORTS_SDL */
+
+/* set these in your _platform_init() implementation. */
+extern int _argc;
+extern char **_argv;
+
+/* !!! gads, look at all the namespace polution... */
+extern long xres, yres, bytesperline, imageSize, maxpages;
+extern char *screen, vesachecked;
+extern long buffermode, origbuffermode, linearmode;
+extern char permanentupdate, vgacompatible;
+extern char moustat;
+extern long *horizlookup, *horizlookup2, horizycent;
+extern long oxdimen, oviewingrange, oxyaspect;
+extern long curbrightness;
+extern long qsetmode;
+extern long frameplace, frameoffset, pageoffset, ydim16;
+extern char textfont[1024], smalltextfont[1024];
+extern char pow2char[8];
+extern volatile long stereomode, visualpage, activepage, whiteband, blackband;
+extern long searchx, searchy;
+extern long wx1, wy1, wx2, wy2, ydimen;
+extern long xdimen, xdimenrecip, halfxdimen, xdimenscale, xdimscale;
+
+/*
+ * !!! used to be static. If we ever put the original setgamemode() back, this
+ * !!! can be made static again.  --ryan.
+ * !!! (Actually, most of these extern declarations used to be static...rcg.)
+ */
+extern unsigned char permanentlock;
+
+// defined in the game project:
+extern long BFullScreen;  // defined in Game\src\config.c
+extern long ScreenMode;
+
+
+/* these need to be implemented by YOUR driver. */
+void _platform_init(int argc, char **argv, const char *title, const char *icon);
+void _idle(void);
+void _handle_events(void);
+void *_getVideoBase(void);
+void initkeys(void);
+void uninitkeys(void);
+void _nextpage(void);
+void _uninitengine(void);
+void _joystick_init(void);
+void _joystick_deinit(void);
+int _joystick_update(void);
+int _joystick_axis(int axis);
+int _joystick_hat(int hat);
+int _joystick_button(int button);
+
+/*
+ * VESA replacement code: The Unix (not-actually-VESA) version of this is
+ *  originally using SDL (Simple Directmedia Layer: http://www.libsdl.org/),
+ *  and is stored in sdl_driver.c, but there's no reason another driver
+ *  couldn't be dropped in, so long as it implements these functions. Please
+ *  reference sdl_driver.c and ves2.h (the original code) for all the nuances
+ *  and global variables that need to get set up correctly.
+ */
+void getvalidvesamodes(void);
+int VBE_getPalette(long start, long num, char *dapal);
+int VBE_setPalette(long start, long num, char *palettebuffer);
+int setvesa(long x, long y);
+void uninitvesa(void);
+void setvmode(int mode);
+unsigned char readpixel(long offset);
+void drawpixel(long offset, Uint8 pixel);
+void drawpixels(long offset, Uint16 pixels);
+void drawpixelses(long offset, Uint32 pixelses);
+void drawpixel16(long offset);
+void fillscreen16 (long input1, long input2, long input3);
+void limitrate(void);
+void setactivepage(long dapagenum);
+void clear2dscreen(void);
+void _updateScreenRect(long x, long y, long w, long h);
+
+/* mouse/keystuff stuff. Also implemented in sdl_driver.c ... */
+int setupmouse(void);
+void readmousexy(short *x, short *y);
+void readmousebstatus(short *bstatus);
+void __interrupt __far keyhandler(void);
+unsigned char _readlastkeyhit(void);
+
+/* timer krap. */
+int inittimer(int);
+void uninittimer(void);
+
+/* this is implemented in the game, and your driver calls it. */
+void __interrupt __far timerhandler(void);
+
+/* resolution inits. sdl_driver.c ... */
+int _setgamemode(char davidoption, long daxdim, long daydim);
+
+unsigned long getticks();
+
+void drawline16(long XStart, long YStart, long XEnd, long YEnd, char Color);
+void setcolor16(int i1);
+
+int using_opengl(void);
+
+#endif  /* _INCLUDE_DISPLAY_H_ */
+
+/* end of display.h ... */
+
+
--- /dev/null
+++ b/Engine/src/enet/LICENSE
@@ -1,0 +1,7 @@
+Copyright (c) 2002 Lee Salzman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+++ b/Engine/src/enet/README
@@ -1,0 +1,16 @@
+Please visit the ENet homepage at http://enet.cubik.org for installation
+and usage instructions.
+
+If you obtained this package from CVS, the quick description on how to build
+is:
+
+# Generate the build system.
+
+aclocal && automake -a -c --foreign && autoconf
+
+# Compile and install the library.
+
+./configure && make && make install
+
+See pyenet/readme.txt for further information on Ling Lo's Python wrapper for
+ENet.
--- /dev/null
+++ b/Engine/src/enet/api.txt
@@ -1,0 +1,290 @@
+enet.h - The file that should be included to use the ENet API.
+
+enet_uint8 - unsigned 8 bit integer
+enet_uint16 - unsigned 16 bit integer
+enet_uint32 - unsigned 32 bit integer
+
+typedef struct
+{  
+  enet_uint32 host; 
+  enet_uint16 port; 
+} ENetAddress;
+      
+  Portable internet address structure. The host must be specified in network 
+byte-order, and the port must be in host byte-order. The constant ENET_HOST_ANY
+may be used to specify the default server host.
+
+typedef struct
+{
+  enet_uint32 flags;
+  enet_uint8 * data;
+  size_t dataLength;
+} ENetPacket;
+
+  An ENet data packet that may be sent to or received from a peer. The shown fields
+should only be read and never modified. The data field contains the allocated data
+for the packet. The dataLength fields specifies the length of the allocated data.
+The flags field is either 0 (specifying no flags), or a bitwise-or of any
+combination of the following flags:
+  
+  ENET_PACKET_FLAG_RELIABLE -   
+  
+      Specifies that the packet must be received by the target peer and that resend
+    attempts should be made should delivery of the packet fail.
+
+typedef struct
+{
+  ENetAddress address;
+  void * data;
+  size_t channelCount;
+  enet_uint32 incomingBandwidth;
+  enet_uint32 outgoingBandwidth;
+  enet_uint32 roundTripTime;
+  enet_uint32 packetLoss;
+} ENetPeer;
+
+  An ENet peer which data packets may be sent or received from. No fields should be
+modified unless otherwise specified. The address field contains the internet address 
+of the peer. The data fields may be used to associate any desired data with the peer 
+and may be freely modified. The channelCount field tells the number of channels that
+have been allocated for use to communnicate with the peer. The incomingBandwidth field
+specifies the downstream bandwidth of the client in bytes per second. The 
+outgoingBandwidth field specifies the upstream bandwidth of the client in bytes per
+second. The roundTripTime field tells the mean round trip time from the sending of
+a reliable packet until the receipt of its acknowledgement in milliseconds. The
+packetLoss field tells the mean packet loss of reliable packets as a ratio with
+respect to the constant ENET_PEER_PACKET_LOSS_SCALE.
+
+typedef enum
+{
+   ENET_EVENT_TYPE_NONE,
+   ENET_EVENT_TYPE_CONNECT,
+   ENET_EVENT_TYPE_DISCONNECT,
+   ENET_EVENT_TYPE_RECEIVE
+} ENetEventType;
+
+typedef struct _ENetEvent
+{
+   ENetEventType        type;
+   ENetPeer *           peer;
+   enet_uint8                channelID;
+   ENetPacket *         packet;
+} ENetEvent;
+
+  An ENet event as returned by enet_host_service. The type field contains the type
+of the event, which may be any one of the following:
+  
+  ENET_EVENT_TYPE_NONE - No event occurred within the specified time limit.
+  ENET_EVENT_TYPE_CONNECT - 
+    
+    A connection request initiated by enet_host_connect has completed. The peer field
+  contains the peer which successfully connected.
+
+  ENET_EVENT_TYPE_DISCONNECT -
+    
+    A peer has disconnected. This event is generated on successful completion of a
+  disconnect iniated by enet_peer_disconnect, if a peer has timed out, or if a
+  connection request initialized by enet_host_connect has timed out. The peer field
+  contains the peer which disconnected.
+
+  ENET_EVENT_TYPE_RECEIVE -
+    
+    A packet has been received from a peer. The peer field specifies the peer which
+  send the packet. The channelID field specifies the channel number upon which the
+  packet was received. The packet field contains the packet that was destroyed; this
+  packet must be destroyed with enet_packet_destroy after use.
+
+typedef struct
+{
+  ENetAddress address;
+  enet_uint32 incomingBandwidth;
+  enet_uint32 outgoingBandwidth;
+  ENetPeer * peers;
+  size_t peerCount;
+} ENetHost;
+
+  An ENet host for communicating with peers. No fields should be modified. The address 
+field tells the internet address of the host. The incomingBandwidth field tells the downstream
+bandwidth of the host. The outgoingBandwidth field specifies the upstream bandwidth of the host.
+The peers field contains an array of the peers that have been allocated for this host. The 
+peerCount field specifies the number of peers that have been allocated for this host.
+
+unsigned ENET_HOST_TO_NET_8 (unsigned);
+unsigned ENET_HOST_TO_NET_16 (unsigned);
+unsigned ENET_HOST_TO_NET_32 (unsigned);
+
+  Macros that convert from host byte-order to network byte-order (big
+endian) for unsigned integers of 8 bits, 16 bits, and 32 bits repectively. 
+
+unsigned ENET_NET_TO_HOST_8 (unsigned);
+unsigned ENET_NET_TO_HOST_16 (unsigned);
+unsigned ENET_NET_TO_HOST_32 (unsigned);
+
+  Macros that convert from network byte-order (big endian) to host 
+byte-order for unsigned integers of 8 bits, 16 bits, and 32 bits repectively.
+
+enet_uint32 enet_time_get (void);
+
+  Returns the wall-time in milliseconds. Its initial value is unspecified unless
+otherwise set.
+
+void enet_time_set (enet_uint32);
+
+  Sets the current wall-time in milliseconds.
+
+int enet_initialize (void);
+
+  Initialize ENet for use. Must be called prior to using any functions in ENet.
+Returns 0 on success and -1 on failure.
+
+void enet_deinitialize (void);
+
+  Clean-up ENet after use. Should be called when a program that has initialized
+and used ENet exits.
+
+int enet_address_set_host (ENetAddress * address, const char * hostName);
+
+  Attempts to resolve the host named by the parameter hostName and sets the host
+field in the address parameter if successful. Returns 0 on success and -1 on
+failure.
+
+int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
+
+  Attempts to do a reverse lookup of the host field in the address parameter.
+If successful, the name of the host is placed in the string described by
+hostName and nameLength. The host name is always null-delimited and will
+not exceed nameLength in length. Returns 0 on success and -1 on failure.
+
+ENetPacket * enet_packet_create (const void * dataContents, size_t dataLength, enet_uint32 flags);
+
+  Creates a packet that may be sent to a peer. The dataContents parameter
+specifies the initial contents of the packet's data; the packet's data will
+remain uninitialized if dataContents is NULL. The dataLength parameter specifies
+the size of the data that is allocated for this packet. The flags parameter
+specifies flags for this packet as described for the ENetPacket structure.
+Returns the packet on success and NULL on failure.
+
+void enet_packet_destroy (ENetPacket * packet);
+
+  Destroys the packet and deallocates its data.
+
+int enet_packet_resize (ENetPacket * packet, size_t dataLength);
+
+  Attempts to resize the data in the packet to the length specified in the
+dataLength parameter. Returns 0 on success and -1 on failure.
+
+ENetHost * enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth);
+
+  Creates a host for communicating with peers. The address parameter specifies 
+the address at which other peers may connect to this host; if the address parameter
+is NULL, then no peers may connect to the host. The peerCount parameter specifies
+the numbers of peers that should be allocated for the host; this limits the maximum
+number of peers that may connect to this host to peerCount. The incomingBandwidth
+parameter specifies the downstream bandwidth of the host in bytes per second; if
+the incomingBandwidth parameter is 0, ENet will assume the host has unlimited
+downstream bandwidth. The outgoingBandwidth parameter specifies the upstream bandwidth
+of the host in bytes per second; if the outgoingBandwidth parameter is 0, ENet will
+assume the host has unlimited upstream bandwidth. ENet will strategically drop packets
+on specific sides of a connection between hosts to ensure the host's bandwidth is not
+overwhelmed; the bandwidth parameters also determine the window size of a connection
+which limits the amount of reliable packets that may be in transit at any given time.
+Returns the host on success and NULL on failure.
+
+void enet_host_destroy (ENetHost * host);
+
+  Destroys the host and all resources associated with it.
+
+ENetPeer * enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount);
+  
+  Initiates a connection from the host specified in the host parameter to a foreign 
+host whose internet address is specified by the address parameter. The channelCount 
+parameter specifies the number of channels that should be allocated for communicating 
+with the foreign host. Returns a peer representing the foreign host on success and NULL
+on failure. The peer returned will have not completed the connection until enet_host_service
+notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
+
+int enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout);
+
+  Waits for events on the host specified by the host parameters and shuttles packets 
+between the host and its peers. The event parameter specifies an event structure
+where event details will be placed if one occurs. The timeout field specifies an
+amount of time in milliseconds that ENet should wait for events. Returns 1 if an
+event occured within the specified time limit, 0 if no event occurred within the 
+time limit, and -1 on failure. This function must be called frequently for adequate
+performance.
+
+void enet_host_flush (ENetHost * host);
+
+  Sends out any queued packets on the host specified in the host parameters to
+the designated peers. This function need only be used in circumstances where one
+wishes to send queued packets earlier than in a call to enet_host_service.
+
+void enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet);
+
+  Queues a packet to be sent to all peers on the host specified in the host parameter
+over the channel number identified by the channelID parameter.
+
+void enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth);
+
+  Adjusts the bandwidth limits of the host specified in the host parameter. The
+incomingBandwidth and outgoingBandwidth parameters are as specified in a call to 
+enet_host_create.
+
+int enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet);
+
+  Queues a packet to be sent to the peer specified by the peer parameter over the
+channel number identified by the channelID parameter. Returns 0 on success and -1
+on failure.
+
+ENetPacket * enet_peer_receive (ENetPeer * peer, enet_uint8 channelID);
+
+  Attempts to dequeue any incoming queued packets on the peer specified by the peer
+parameter on the channel number identified by the channelID parameter. Returns a packet
+if one is available and NULL if there are no available incoming queued packets.
+
+void enet_peer_ping (ENetPeer * peer);
+  
+  Sends a ping request to the peer specified by the peer parameter. Ping requests factor
+into the mean round trip time as designated by the roundTripTime field in the ENetPeer
+structure. ENet automatically pings all connected peer at an interval, however, this 
+function may be called to ensure more frequent ping requests.
+
+void enet_peer_reset (ENetPeer * peer);
+
+  Forcefully disconnects the peer specified by the peer parameter. The foreign host 
+represented by the peer is not notified of the disconnection and so will timeout on its
+connection to the local host.
+
+void enet_peer_disconnect (ENetPeer * peer);
+
+  Request a disconnection from the peer specified by the peer parameter. An
+ENET_EVENT_DISCONNECT event will be generated by enet_host_service once the
+disconnection is complete.
+
+void enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration);
+
+  Configures throttle parameter for the peer specified by the peer parameter.
+Unreliable packets are dropped by ENet in response to the varying conditions of
+the internet connection to the peer. The throttle represents a probability that
+an unreliable packet should not be dropped and thus sent by ENet to the peer.
+The lowest mean round trip time from the sending of a reliable packet to the 
+receipt of its acknowledgement is measured over an amount of time specified 
+by the interval parameter in milliseconds; the constant ENET_PEER_PACKET_THROTTLE_INTERVAL
+is the default value for this parameter. If a measured round trip time happens 
+to be signifigantly less than the mean round trip time measured over the interval, 
+then the throttle probability is increased to allow more traffic by an amount
+specified in the acceleration parameter which is in ratio to the
+ENET_PEER_PACKET_THROTTLE_SCALE constant. If a measured round trip time happens
+to be signifigantly greater than the mean round trip time measured over the interval,
+then the throttle probability is decreased to limit traffic by an amount specified
+in the deceleration parameter which is in ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+constant. When the throttle has a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable
+packets are dropped by ENET, and so 100% of all unreliable packets will be sent. When the 
+throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all
+unreliable packets will be sent. Intermediate values for the throttle represent intermediate
+probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits
+of the local and foreign host are taken into account to determine a sensible limit for
+the throttle probability above which it should not raise even in the best of conditions.
+
+
+
--- /dev/null
+++ b/Engine/src/enet/design.txt
@@ -1,0 +1,117 @@
+* Why ENet?
+
+    ENet evolved specifically as a UDP networking layer for the multiplayer 
+first person shooter Cube. Cube necessitated low latency communcation with
+data sent out very frequently, so TCP was an unsuitable choice due to its
+high latency and stream orientation. UDP, however, lacks many sometimes 
+necessary features from TCP such as reliability, sequencing, unrestricted
+packet sizes, and connection management. So UDP by itself was not suitable
+as a network protocol either. No suitable freely available networking 
+libraries existed at the time of ENet's creation to fill this niche.
+
+    UDP and TCP could have been used together in Cube to benefit somewhat
+from both of their features, however, the resulting combinations of protocols
+still leaves much to be desired. TCP lacks multiple streams of communication
+without resorting to opening many sockets and complicates delineation of 
+packets due to its buffering behavior. UDP lacks sequencing, connection 
+management, management of bandwidth resources, and imposes limitations on
+the size of packets. A significant investment is required to integrate these 
+two protocols, and the end result is worse off in features and performance 
+than the uniform protocol presented by ENet.
+
+    ENet thus attempts to address these issues and provide a single, uniform
+protocol layered over UDP to the developer with the best features of UDP and 
+TCP as well as some useful features neither provide, with a much cleaner 
+integration than any resulting from a mixture of UDP and TCP.
+
+* Connection management
+
+    ENet provides a simple connection interface over which to communicate
+with a foreign host. The liveness of the connection is actively monitored
+by pinging the foreign host at frequent intervals, and also monitors the
+network conditions from the local host to the foreign host such as the
+mean round trip time and packet loss in this fashion.
+
+* Sequencing
+    
+    Rather than a single byte stream that complicates the delineation 
+of packets, ENet presents connections as multiple, properly sequenced packet
+streams that simplify the transfer of various types of data.
+
+    ENet provides sequencing for all packets by assigning to each sent 
+packet a sequence number that is incremented as packets are sent. ENet 
+guarentees that no packet with a higher sequence number will be delivered 
+before a packet with a lower sequence number, thus ensuring packets are 
+delivered exactly in the order they are sent. 
+
+    For unreliable packets, ENet will simply discard the lower sequence 
+number packet if a packet with a higher sequence number has already been 
+delivered. This allows the packets to be dispatched immediately as they
+arrive, and reduce latency of unreliable packets to an absolute minimum.
+For reliable packets, if a higher sequence number packet arrives, but the 
+preceding packets in the sequence have not yet arrived, ENet will stall 
+delivery of the higher sequence number packets until its predecessors
+have arrived.
+
+* Channels
+
+    Since ENet will stall delivery of reliable packets to ensure proper
+sequencing, and consequently any packets of higher sequence number whether 
+reliable or unreliable, in the event the reliable packet's predecessors 
+have not yet arrived, this can introduce latency into the delivery of other 
+packets which may not need to be as strictly ordered with respect to the 
+packet that stalled their delivery.
+
+    To combat this latency and reduce the ordering restrictions on packets,
+ENet provides multiple channels of communication over a given connection.
+Each channel is independently sequenced, and so the delivery status of
+a packet in one channel will not stall the delivery of other packets
+in another channel.
+
+* Reliability
+
+    ENet provides optional reliability of packet delivery by ensuring the 
+foreign host acknowledges receipt of all reliable packets. ENet will attempt 
+to resend the packet up to a reasonable amount of times, if no acknowledgement
+of the packet's receipt happens within a specified timeout. Retry timeouts
+are progressive and become more lenient with every failed attempt to allow
+for temporary turbulence in network conditions.
+
+* Fragmentation and reassembly
+
+    ENet will send and deliver packets regardless of size. Large packets are
+fragmented into many smaller packets of suitable size, and reassembled on
+the foreign host to recover the original packet for delivery. The process
+is entirely transparent to the developer.
+
+* Aggregation
+
+    ENet aggregates all protocol commands, including acknowledgements and
+packet transfer, into larger protocol packets to ensure the proper utilization
+of the connection and to limit the opportunities for packet loss that might
+otherwise result in further delivery latency.
+
+* Adaptability
+
+    ENet provides an in-flight data window for reliable packets to ensure
+connections are not overwhelmed by volumes of packets. It also provides a
+static bandwidth allocation mechanism to ensure the total volume of packets
+sent and received to a host don't exceed the host's capabilities. Further,
+ENet also provides a dynamic throttle that responds to deviations from normal
+network connections to rectify various types of network congestion by further
+limiting the volume of packets sent.
+
+* Portability
+    
+    ENet works on Windows and any other Unix or Unix-like platform providing
+a BSD sockets interface. The library has a small and stable code base that
+can easily be extended to support other platforms and integrates easily.
+
+* Freedom
+
+    ENet demands no royalties and doesn't carry a viral license that would
+restrict you in how you might use it in your programs. ENet is licensed under
+a short-and-sweet MIT-style license, which gives you the freedom to do anything 
+you want with it (well, almost anything).
+
+
--- /dev/null
+++ b/Engine/src/enet/host.c
@@ -1,0 +1,390 @@
+/** 
+ @file host.c
+ @brief ENet host management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "memory.h"
+#include "enet.h"
+
+/** @defgroup host ENet host functions
+    @{
+*/
+
+/** Creates a host for communicating to peers.  
+
+    @param address   the address at which other peers may connect to this host.  If NULL, then no peers may connect to the host.
+    @param peerCount the maximum number of peers that should be allocated for the host.
+    @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+    @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+
+    @returns the host on success and NULL on failure
+
+    @remarks ENet will strategically drop packets on specific sides of a connection between hosts
+    to ensure the host's bandwidth is not overwhelmed.  The bandwidth parameters also determine
+    the window size of a connection which limits the amount of reliable packets that may be in transit
+    at any given time.
+*/
+ENetHost *
+enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    ENetHost * host = (ENetHost *) enet_malloc (sizeof (ENetHost));
+    ENetPeer * currentPeer;
+
+    host -> peers = (ENetPeer *) enet_calloc (peerCount, sizeof (ENetPeer));
+
+    host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM, address);
+    if (host -> socket == ENET_SOCKET_NULL)
+    {
+       enet_free (host -> peers);
+       enet_free (host);
+
+       return NULL;
+    }
+
+    if (address != NULL)
+      host -> address = * address;
+
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> bandwidthThrottleEpoch = 0;
+    host -> recalculateBandwidthLimits = 0;
+    host -> mtu = ENET_HOST_DEFAULT_MTU;
+    host -> peerCount = peerCount;
+    host -> lastServicedPeer = host -> peers;
+    host -> commandCount = 0;
+    host -> bufferCount = 0;
+    host -> receivedAddress.host = ENET_HOST_ANY;
+    host -> receivedAddress.port = 0;
+    host -> receivedDataLength = 0;
+     
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       currentPeer -> host = host;
+       currentPeer -> incomingPeerID = currentPeer - host -> peers;
+       currentPeer -> data = NULL;
+
+       enet_list_clear (& currentPeer -> acknowledgements);
+       enet_list_clear (& currentPeer -> sentReliableCommands);
+       enet_list_clear (& currentPeer -> sentUnreliableCommands);
+       enet_list_clear (& currentPeer -> outgoingReliableCommands);
+       enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
+
+       enet_peer_reset (currentPeer);
+    }
+ 
+    return host;
+}
+
+/** Destroys the host and all resources associated with it.
+    @param host pointer to the host to destroy
+*/
+void
+enet_host_destroy (ENetHost * host)
+{
+    ENetPeer * currentPeer;
+
+    enet_socket_destroy (host -> socket);
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       enet_peer_reset (currentPeer);
+    }
+
+    enet_free (host -> peers);
+    enet_free (host);
+}
+
+/** Initiates a connection to a foreign host.
+    @param host host seeking the connection
+    @param address destination for the connection
+    @param channelCount number of channels to allocate
+    @returns a peer representing the foreign host on success, NULL on failure
+    @remarks The peer returned will have not completed the connection until enet_host_service()
+    notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
+*/
+ENetPeer *
+enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount)
+{
+    ENetPeer * currentPeer;
+    ENetChannel * channel;
+    ENetProtocol command;
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+    else
+    if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+         break;
+    }
+
+    if (currentPeer >= & host -> peers [host -> peerCount])
+      return NULL;
+
+    currentPeer -> state = ENET_PEER_STATE_CONNECTING;
+    currentPeer -> address = * address;
+    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    currentPeer -> channelCount = channelCount;
+    currentPeer -> challenge = (enet_uint32) rand ();
+
+    if (host -> outgoingBandwidth == 0)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      currentPeer -> windowSize = (host -> outgoingBandwidth /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+         
+    for (channel = currentPeer -> channels;
+         channel < & currentPeer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+    }
+        
+    command.header.command = ENET_PROTOCOL_COMMAND_CONNECT;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    command.header.commandLength = sizeof (ENetProtocolConnect);
+    command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+    command.connect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu);
+    command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
+    command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+    command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+    command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+    command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+    
+    enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
+
+    return currentPeer;
+}
+
+/** Queues a packet to be sent to all peers associated with the host.
+    @param host host on which to broadcast the packet
+    @param channelID channel on which to broadcast
+    @param packet packet to broadcast
+*/
+void
+enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
+{
+    ENetPeer * currentPeer;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+         continue;
+
+       enet_peer_send (currentPeer, channelID, packet);
+    }
+
+    if (packet -> referenceCount == 0)
+      enet_packet_destroy (packet);
+}
+
+/** Adjusts the bandwidth limits of a host.
+    @param host host to adjust
+    @param incomingBandwidth new incoming bandwidth
+    @param outgoingBandwidth new outgoing bandwidth
+    @remarks the incoming and outgoing bandwidth parameters are identical in function to those
+    specified in enet_host_create().
+*/
+void
+enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> recalculateBandwidthLimits = 1;
+}
+
+void
+enet_host_bandwidth_throttle (ENetHost * host)
+{
+    enet_uint32 timeCurrent = enet_time_get (),
+           elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
+           peersTotal = 0,
+           dataTotal = 0,
+           peersRemaining,
+           bandwidth,
+           throttle = 0,
+           bandwidthLimit = 0;
+    int needsAdjustment;
+    ENetPeer * peer;
+    ENetProtocol command;
+
+    if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+      return;
+
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        if (peer -> state != ENET_PEER_STATE_CONNECTED)
+          continue;
+
+        ++ peersTotal;
+        dataTotal += peer -> outgoingDataTotal;
+    }
+
+    if (peersTotal == 0)
+      return;
+
+    peersRemaining = peersTotal;
+    needsAdjustment = 1;
+
+    if (host -> outgoingBandwidth == 0)
+      bandwidth = ~0;
+    else
+      bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
+
+    while (peersRemaining > 0 && needsAdjustment != 0)
+    {
+        needsAdjustment = 0;
+        
+        if (dataTotal < bandwidth)
+          throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+        else
+          throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+        for (peer = host -> peers;
+             peer < & host -> peers [host -> peerCount];
+             ++ peer)
+        {
+            enet_uint32 peerBandwidth;
+            
+            if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+                peer -> incomingBandwidth == 0 ||
+                peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+              continue;
+
+            peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
+            if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
+              continue;
+
+            peer -> packetThrottleLimit = (peerBandwidth * 
+                                            ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
+            
+            if (peer -> packetThrottleLimit == 0)
+              peer -> packetThrottleLimit = 1;
+            
+            if (peer -> packetThrottle > peer -> packetThrottleLimit)
+              peer -> packetThrottle = peer -> packetThrottleLimit;
+
+            peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
+
+            
+            needsAdjustment = 1;
+            -- peersRemaining;
+            bandwidth -= peerBandwidth;
+            dataTotal -= peerBandwidth;
+        }
+    }
+
+    if (peersRemaining > 0)
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+            peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+          continue;
+
+        peer -> packetThrottleLimit = throttle;
+
+        if (peer -> packetThrottle > peer -> packetThrottleLimit)
+          peer -> packetThrottle = peer -> packetThrottleLimit;
+    }
+    
+    if (host -> recalculateBandwidthLimits)
+    {
+       host -> recalculateBandwidthLimits = 0;
+
+       peersRemaining = peersTotal;
+       bandwidth = host -> incomingBandwidth;
+       needsAdjustment = 1;
+
+       if (bandwidth == 0)
+         bandwidthLimit = 0;
+       else
+       while (peersRemaining > 0 && needsAdjustment != 0)
+       {
+           needsAdjustment = 0;
+           bandwidthLimit = bandwidth / peersRemaining;
+
+           for (peer = host -> peers;
+                peer < & host -> peers [host -> peerCount];
+                ++ peer)
+           {
+               if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+                   peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+                 continue;
+
+               if (peer -> outgoingBandwidth > 0 &&
+                   bandwidthLimit > peer -> outgoingBandwidth)
+                 continue;
+
+               peer -> incomingBandwidthThrottleEpoch = timeCurrent;
+ 
+               needsAdjustment = 1;
+               -- peersRemaining;
+               bandwidth -= peer -> outgoingBandwidth;
+           }
+       }
+
+       for (peer = host -> peers;
+            peer < & host -> peers [host -> peerCount];
+            ++ peer)
+       {
+           if (peer -> state != ENET_PEER_STATE_CONNECTED)
+             continue;
+
+           command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT;
+           command.header.channelID = 0xFF;
+           command.header.flags = 0;
+           command.header.commandLength = sizeof (ENetProtocolBandwidthLimit);
+           command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+
+           if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
+           else
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
+
+           enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+       } 
+    }
+
+    host -> bandwidthThrottleEpoch = timeCurrent;
+
+    for (peer = host -> peers;
+         peer < & host -> peers [host -> peerCount];
+         ++ peer)
+    {
+        peer -> incomingDataTotal = 0;
+        peer -> outgoingDataTotal = 0;
+    }
+}
+    
+/** @} */
--- /dev/null
+++ b/Engine/src/enet/include/enet.h
@@ -1,0 +1,432 @@
+/** 
+ @file  enet.h
+ @brief ENet public header file
+*/
+#ifndef __ENET_ENET_H__
+#define __ENET_ENET_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+#include "types.h"
+#include "protocol.h"
+#include "list.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include "unix.h"
+#endif
+
+#ifdef ENET_API
+#undef ENET_API
+#endif
+
+#if defined WIN32
+#if defined ENET_DLL
+#if defined ENET_BUILDING_LIB
+#define ENET_API __declspec( dllexport )
+#else
+#define ENET_API __declspec( dllimport )
+#endif /* ENET_BUILDING_LIB */
+#endif /* ENET_DLL */
+#endif /* WIN32 */
+
+#ifndef ENET_API
+#define ENET_API extern
+#endif
+
+typedef enum
+{
+   ENET_SOCKET_TYPE_STREAM   = 1,
+   ENET_SOCKET_TYPE_DATAGRAM = 2
+} ENetSocketType;
+
+typedef enum
+{
+   ENET_SOCKET_WAIT_NONE    = 0,
+   ENET_SOCKET_WAIT_SEND    = (1 << 0),
+   ENET_SOCKET_WAIT_RECEIVE = (1 << 1)
+} ENetSocketWait;
+
+enum
+{
+   ENET_HOST_ANY = 0
+};
+
+/**
+ * Portable internet address structure. 
+ *
+ * The host must be specified in network byte-order, and the port must be in host 
+ * byte-order. The constant ENET_HOST_ANY may be used to specify the default 
+ * server host.
+ */
+typedef struct _ENetAddress
+{
+   enet_uint32 host;  /**< may use ENET_HOST_ANY to specify default server host */
+   enet_uint16 port;
+} ENetAddress;
+
+/**
+ * Packet flag bit constants.
+ *
+ * The host must be specified in network byte-order, and the port must be in
+ * host byte-order. The constant ENET_HOST_ANY may be used to specify the
+ * default server host.
+ 
+   @sa ENetPacket
+*/
+typedef enum
+{
+   /** packet must be received by the target peer and resend attempts should be
+     * made until the packet is delivered */
+   ENET_PACKET_FLAG_RELIABLE = (1 << 0)
+} ENetPacketFlag;
+
+/**
+ * ENet packet structure.
+ *
+ * An ENet data packet that may be sent to or received from a peer. The shown 
+ * fields should only be read and never modified. The data field contains the 
+ * allocated data for the packet. The dataLength fields specifies the length 
+ * of the allocated data.  The flags field is either 0 (specifying no flags), 
+ * or a bitwise-or of any combination of the following flags:
+ *
+ *    ENET_PACKET_FLAG_RELIABLE - packet must be received by the ta
+
+   @sa ENetPacketFlag
+ */
+typedef struct _ENetPacket
+{
+   size_t               referenceCount;  /**< internal use only */
+   enet_uint32          flags;           /**< bitwise or of ENetPacketFlag constants */
+   enet_uint8 *         data;            /**< allocated data for packet */
+   size_t               dataLength;      /**< length of data */
+} ENetPacket;
+
+typedef struct _ENetAcknowledgement
+{
+   ENetListNode acknowledgementList;
+   enet_uint32  sentTime;
+   ENetProtocol command;
+} ENetAcknowledgement;
+
+typedef struct _ENetOutgoingCommand
+{
+   ENetListNode outgoingCommandList;
+   enet_uint32  reliableSequenceNumber;
+   enet_uint32  unreliableSequenceNumber;
+   enet_uint32  sentTime;
+   enet_uint32  roundTripTimeout;
+   enet_uint32  roundTripTimeoutLimit;
+   enet_uint32  fragmentOffset;
+   enet_uint16  fragmentLength;
+   ENetProtocol command;
+   ENetPacket * packet;
+} ENetOutgoingCommand;
+
+typedef struct _ENetIncomingCommand
+{  
+   ENetListNode     incomingCommandList;
+   enet_uint32      reliableSequenceNumber;
+   enet_uint32      unreliableSequenceNumber;
+   ENetProtocol     command;
+   enet_uint32      fragmentCount;
+   enet_uint32      fragmentsRemaining;
+   enet_uint32 *    fragments;
+   ENetPacket *     packet;
+} ENetIncomingCommand;
+
+typedef enum
+{
+   ENET_PEER_STATE_DISCONNECTED                = 0,
+   ENET_PEER_STATE_CONNECTING                  = 1,
+   ENET_PEER_STATE_ACKNOWLEDGING_CONNECT       = 2,
+   ENET_PEER_STATE_CONNECTED                   = 3,
+   ENET_PEER_STATE_DISCONNECTING               = 4,
+   ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT    = 5,
+   ENET_PEER_STATE_ZOMBIE                      = 6
+} ENetPeerState;
+
+#ifndef ENET_BUFFER_MAXIMUM
+#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
+#endif
+
+enum
+{
+   ENET_HOST_RECEIVE_BUFFER_SIZE          = 256 * 1024,
+   ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL  = 1000,
+   ENET_HOST_DEFAULT_MTU                  = 1400,
+
+   ENET_PEER_DEFAULT_ROUND_TRIP_TIME      = 500,
+   ENET_PEER_DEFAULT_PACKET_THROTTLE      = 32,
+   ENET_PEER_PACKET_THROTTLE_SCALE        = 32,
+   ENET_PEER_PACKET_THROTTLE_COUNTER      = 7, 
+   ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_INTERVAL     = 5000,
+   ENET_PEER_PACKET_LOSS_SCALE            = (1 << 16),
+   ENET_PEER_PACKET_LOSS_INTERVAL         = 10000,
+   ENET_PEER_WINDOW_SIZE_SCALE            = 64 * 1024,
+   ENET_PEER_TIMEOUT_LIMIT                = 32,
+   ENET_PEER_PING_INTERVAL                = 500
+};
+
+typedef struct _ENetChannel
+{
+   enet_uint32  outgoingReliableSequenceNumber;
+   enet_uint32  outgoingUnreliableSequenceNumber;
+   enet_uint32  incomingReliableSequenceNumber;
+   enet_uint32  incomingUnreliableSequenceNumber;
+   ENetList     incomingReliableCommands;
+   ENetList     incomingUnreliableCommands;
+} ENetChannel;
+
+/**
+ * An ENet peer which data packets may be sent or received from. 
+ *
+ * No fields should be modified unless otherwise specified. 
+ */
+typedef struct _ENetPeer
+{ 
+   struct _ENetHost * host;
+   enet_uint16   outgoingPeerID;
+   enet_uint16   incomingPeerID;
+   enet_uint32   challenge;
+   ENetAddress   address;            /**< Internet address of the peer */
+   void *        data;               /**< Application private data, may be freely modified */
+   ENetPeerState state;
+   ENetChannel * channels;
+   size_t        channelCount;       /**< Number of channels allocated for communication with peer */
+   enet_uint32   incomingBandwidth;  /**< Downstream bandwidth of the client in bytes/second */
+   enet_uint32   outgoingBandwidth;  /**< Upstream bandwidth of the client in bytes/second */
+   enet_uint32   incomingBandwidthThrottleEpoch;
+   enet_uint32   outgoingBandwidthThrottleEpoch;
+   enet_uint32   incomingDataTotal;
+   enet_uint32   outgoingDataTotal;
+   enet_uint32   lastSendTime;
+   enet_uint32   lastReceiveTime;
+   enet_uint32   nextTimeout;
+   enet_uint32   packetLossEpoch;
+   enet_uint32   packetsSent;
+   enet_uint32   packetsLost;
+   enet_uint32   packetLoss;          /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
+   enet_uint32   packetLossVariance;
+   enet_uint32   packetThrottle;
+   enet_uint32   packetThrottleLimit;
+   enet_uint32   packetThrottleCounter;
+   enet_uint32   packetThrottleEpoch;
+   enet_uint32   packetThrottleAcceleration;
+   enet_uint32   packetThrottleDeceleration;
+   enet_uint32   packetThrottleInterval;
+   enet_uint32   lastRoundTripTime;
+   enet_uint32   lowestRoundTripTime;
+   enet_uint32   lastRoundTripTimeVariance;
+   enet_uint32   highestRoundTripTimeVariance;
+   enet_uint32   roundTripTime;            /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
+   enet_uint32   roundTripTimeVariance;
+   enet_uint16   mtu;
+   enet_uint32   windowSize;
+   enet_uint32   reliableDataInTransit;
+   enet_uint32   outgoingReliableSequenceNumber;
+   ENetList      acknowledgements;
+   ENetList      sentReliableCommands;
+   ENetList      sentUnreliableCommands;
+   ENetList      outgoingReliableCommands;
+   ENetList      outgoingUnreliableCommands;
+} ENetPeer;
+
+/** An ENet host for communicating with peers.
+  *
+  * No fields should be modified.
+
+    @sa enet_host_create()
+    @sa enet_host_destroy()
+    @sa enet_host_connect()
+    @sa enet_host_service()
+    @sa enet_host_flush()
+    @sa enet_host_broadcast()
+    @sa enet_host_bandwidth_limit()
+    @sa enet_host_bandwidth_throttle()
+  */
+typedef struct _ENetHost
+{
+   ENetSocket         socket;
+   ENetAddress        address;                     /**< Internet address of the host */
+   enet_uint32        incomingBandwidth;           /**< downstream bandwidth of the host */
+   enet_uint32        outgoingBandwidth;           /**< upstream bandwidth of the host */
+   enet_uint32        bandwidthThrottleEpoch;
+   enet_uint32        mtu;
+   int                recalculateBandwidthLimits;
+   ENetPeer *         peers;                       /**< array of peers allocated for this host */
+   size_t             peerCount;                   /**< number of peers allocated for this host */
+   ENetPeer *         lastServicedPeer;
+   size_t             packetSize;
+   ENetProtocol       commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
+   size_t             commandCount;
+   ENetBuffer         buffers [ENET_BUFFER_MAXIMUM];
+   size_t             bufferCount;
+   ENetAddress        receivedAddress;
+   enet_uint8         receivedData [ENET_PROTOCOL_MAXIMUM_MTU];
+   size_t             receivedDataLength;
+} ENetHost;
+
+/**
+ * An ENet event type, as specified in @ref ENetEvent.
+ */
+typedef enum
+{
+   /** no event occurred within the specified time limit */
+   ENET_EVENT_TYPE_NONE       = 0,  
+
+   /** a connection request initiated by enet_host_connect has completed.  
+     * The peer field contains the peer which successfully connected. 
+     */
+   ENET_EVENT_TYPE_CONNECT    = 1,  
+
+   /** a peer has disconnected.  This event is generated on a successful 
+     * completion of a disconnect initiated by enet_pper_disconnect, if 
+     * a peer has timed out, or if a connection request intialized by 
+     * enet_host_connect has timed out.  The peer field contains the peer 
+     * which disconnected. 
+     */
+   ENET_EVENT_TYPE_DISCONNECT = 2,  
+
+   /** a packet has been received from a peer.  The peer field specifies the
+     * peer which sent the packet.  The channelID field specifies the channel
+     * number upon which the packet was received.  The packet field contains
+     * the packet that was received; this packet must be destroyed with
+     * enet_packet_destroy after use.
+     */
+   ENET_EVENT_TYPE_RECEIVE    = 3
+} ENetEventType;
+
+/**
+ * An ENet event as returned by enet_host_service().
+   
+   @sa enet_host_service
+ */
+typedef struct _ENetEvent 
+{
+   ENetEventType        type;      /**< type of the event */
+   ENetPeer *           peer;      /**< peer that generated a connect, disconnect or receive event */
+   enet_uint8           channelID;
+   ENetPacket *         packet;
+} ENetEvent;
+
+/** @defgroup global ENet global functions
+    @{ 
+*/
+
+/** 
+  Initializes ENet globally.  Must be called prior to using any functions in
+  ENet.
+  @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize (void);
+
+/** 
+  Shuts down ENet globally.  Should be called when a program that has
+  initialized ENet exits.
+*/
+ENET_API void enet_deinitialize (void);
+
+/** @} */
+
+/** @defgroup private ENet private implementation functions */
+
+/**
+  Returns the time in milliseconds.  Its initial value is unspecified
+  unless otherwise set.
+  */
+ENET_API enet_uint32 enet_time_get_raw (void);
+
+/** @} */
+
+/** @defgroup private ENet private implementation functions */
+
+/**
+  Returns the wall-time in milliseconds.  Its initial value is unspecified
+  unless otherwise set.
+  */
+ENET_API enet_uint32 enet_time_get (void);
+/**
+  Sets the current wall-time in milliseconds.
+  */
+ENET_API void enet_time_set (enet_uint32);
+
+/** @defgroup socket ENet socket functions
+    @{
+    @ingroup private
+*/
+extern ENetSocket enet_socket_create (ENetSocketType, const ENetAddress *);
+extern ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
+extern int        enet_socket_connect (ENetSocket, const ENetAddress *);
+extern int        enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
+extern int        enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
+extern int        enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
+extern void       enet_socket_destroy (ENetSocket);
+
+/** @} */
+
+/** @defgroup Address ENet address functions
+    @{
+*/
+/** Attempts to resolve the host named by the parameter hostName and sets
+    the host field in the address parameter if successful.
+    @param address destination to store resolved address
+    @param hostName host name to lookup
+    @retval 0 on success
+    @retval < 0 on failure
+    @returns the address of the given hostName in address on success
+*/
+extern int enet_address_set_host (ENetAddress *address, const char *hostName );
+
+/** Attempts to do a reserve lookup of the host field in the address parameter.
+    @param address    address used for reverse lookup
+    @param hostName   destination for name, must not be NULL
+    @param nameLength maximum length of hostName.
+    @returns the null-terminated name of the host in hostName on success
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+extern int enet_address_get_host (const ENetAddress *address, char *hostName, size_t nameLength );
+
+/** @} */
+
+ENET_API ENetPacket * enet_packet_create (const void *dataContents, size_t dataLength, enet_uint32 flags);
+ENET_API void         enet_packet_destroy (ENetPacket *packet );
+ENET_API int          enet_packet_resize  (ENetPacket *packet, size_t dataLength );
+
+ENET_API ENetHost * enet_host_create (const ENetAddress *address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth );
+ENET_API void       enet_host_destroy (ENetHost *host );
+ENET_API ENetPeer * enet_host_connect (ENetHost *host, const ENetAddress *address, size_t channelCount );
+ENET_API int        enet_host_service (ENetHost *, ENetEvent *, enet_uint32);
+ENET_API void       enet_host_flush (ENetHost *);
+ENET_API void       enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *);
+ENET_API void       enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
+extern   void       enet_host_bandwidth_throttle (ENetHost *);
+
+ENET_API int                 enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
+ENET_API ENetPacket *        enet_peer_receive (ENetPeer *, enet_uint8);
+ENET_API void                enet_peer_ping (ENetPeer *);
+ENET_API void                enet_peer_reset (ENetPeer *);
+ENET_API void                enet_peer_disconnect (ENetPeer *);
+ENET_API void                enet_peer_disconnect_now (ENetPeer *);
+ENET_API void                enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+extern int                   enet_peer_throttle (ENetPeer *, enet_uint32);
+extern void                  enet_peer_reset_queues (ENetPeer *);
+extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
+extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32);
+extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint32);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ENET_ENET_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/list.h
@@ -1,0 +1,42 @@
+/** 
+ @file  list.h
+ @brief ENet list management 
+*/
+#ifndef __ENET_LIST_H__
+#define __ENET_LIST_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetListNode
+{
+   struct _ENetListNode * next;
+   struct _ENetListNode * previous;
+} ENetListNode;
+
+typedef ENetListNode * ENetListIterator;
+
+typedef struct _ENetList
+{
+   ENetListNode sentinel;
+} ENetList;
+
+extern void enet_list_clear (ENetList *);
+
+extern ENetListIterator enet_list_insert (ENetListIterator, void *);
+extern void * enet_list_remove (ENetListIterator);
+
+extern size_t enet_list_size (ENetList *);
+
+#define enet_list_begin(list) ((list) -> sentinel.next)
+#define enet_list_end(list) (& (list) -> sentinel)
+
+#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
+
+#define enet_list_next(iterator) ((iterator) -> next)
+#define enet_list_previous(iterator) ((iterator) -> previous)
+
+#define enet_list_front(list) ((void *) (list) -> sentinel.next)
+#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
+
+#endif /* __ENET_LIST_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/memory.h
@@ -1,0 +1,22 @@
+/** 
+ @file  memory.h
+ @brief ENet memory management
+*/
+#ifndef __ENET_MEMORY_H__
+#define __ENET_MEMORY_H__
+
+#include <stdlib.h>
+
+/** @defgroup memory ENet internal memory management
+    @{
+    @ingroup private
+*/
+extern void * enet_malloc (size_t);
+extern void * enet_realloc (void *, size_t);
+extern void * enet_calloc (size_t, size_t);
+extern void   enet_free (void *);
+
+/** @} */
+
+#endif /* __ENET_MEMORY_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/protocol.h
@@ -1,0 +1,157 @@
+/** 
+ @file  protocol.h
+ @brief ENet protocol
+*/
+#ifndef __ENET_PROTOCOL_H__
+#define __ENET_PROTOCOL_H__
+
+#include "types.h"
+
+enum
+{
+   ENET_PROTOCOL_MINIMUM_MTU             = 576,
+   ENET_PROTOCOL_MAXIMUM_MTU             = 4096,
+   ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
+   ENET_PROTOCOL_MINIMUM_WINDOW_SIZE     = 4096,
+   ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE     = 32768,
+   ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT   = 1,
+   ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT   = 255
+};
+
+typedef enum
+{
+   ENET_PROTOCOL_COMMAND_NONE               = 0,
+   ENET_PROTOCOL_COMMAND_ACKNOWLEDGE        = 1,
+   ENET_PROTOCOL_COMMAND_CONNECT            = 2,
+   ENET_PROTOCOL_COMMAND_VERIFY_CONNECT     = 3,
+   ENET_PROTOCOL_COMMAND_DISCONNECT         = 4,
+   ENET_PROTOCOL_COMMAND_PING               = 5,
+   ENET_PROTOCOL_COMMAND_SEND_RELIABLE      = 6,
+   ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE    = 7,
+   ENET_PROTOCOL_COMMAND_SEND_FRAGMENT      = 8,
+   ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT    = 9,
+   ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 10
+} ENetProtocolCommand;
+
+typedef enum
+{
+   ENET_PROTOCOL_FLAG_ACKNOWLEDGE = (1 << 0)
+} ENetProtocolFlag;
+
+typedef struct
+{
+   enet_uint16 peerID;
+   enet_uint8 flags;
+   enet_uint8 commandCount;
+   enet_uint32 sentTime;
+   enet_uint32 challenge;
+} ENetProtocolHeader;
+
+typedef struct
+{
+   enet_uint8 command;
+   enet_uint8 channelID;
+   enet_uint8 flags;
+   enet_uint8 reserved;
+   enet_uint32 commandLength;
+   enet_uint32 reliableSequenceNumber;
+} ENetProtocolCommandHeader;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 receivedReliableSequenceNumber;
+   enet_uint32 receivedSentTime;
+} ENetProtocolAcknowledge;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint16 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolConnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint16 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolVerifyConnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+} ENetProtocolBandwidthLimit;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENetProtocolThrottleConfigure;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolDisconnect;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolPing;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+} ENetProtocolSendReliable;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 unreliableSequenceNumber;
+} ENetProtocolSendUnreliable;
+
+typedef struct
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 startSequenceNumber;
+   enet_uint32 fragmentCount;
+   enet_uint32 fragmentNumber;
+   enet_uint32 totalLength;
+   enet_uint32 fragmentOffset;
+} ENetProtocolSendFragment;
+
+typedef union
+{
+   ENetProtocolCommandHeader header;
+   ENetProtocolAcknowledge acknowledge;
+   ENetProtocolConnect connect;
+   ENetProtocolVerifyConnect verifyConnect;
+   ENetProtocolDisconnect disconnect;
+   ENetProtocolPing ping;
+   ENetProtocolSendReliable sendReliable;
+   ENetProtocolSendUnreliable sendUnreliable;
+   ENetProtocolSendFragment sendFragment;
+   ENetProtocolBandwidthLimit bandwidthLimit;
+   ENetProtocolThrottleConfigure throttleConfigure;
+} ENetProtocol;
+
+#endif /* __ENET_PROTOCOL_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/time.h
@@ -1,0 +1,18 @@
+/** 
+ @file  time.h
+ @brief ENet time constants and macros
+*/
+#ifndef __ENET_TIME_H__
+#define __ENET_TIME_H__
+
+#define ENET_TIME_OVERFLOW 86400000
+
+#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
+#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
+
+#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
+
+#endif /* __ENET_TIME_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/types.h
@@ -1,0 +1,13 @@
+/** 
+ @file  types.h
+ @brief type definitions for ENet
+*/
+#ifndef __ENET_TYPES_H__
+#define __ENET_TYPES_H__
+
+typedef unsigned char enet_uint8;       /**< unsigned 8-bit type  */
+typedef unsigned short enet_uint16;     /**< unsigned 16-bit type */
+typedef unsigned int enet_uint32;      /**< unsigned 32-bit type */
+
+#endif /* __ENET_TYPES_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/unix.h
@@ -1,0 +1,32 @@
+/** 
+ @file  unix.h
+ @brief ENet Unix header
+*/
+#ifndef __ENET_UNIX_H__
+#define __ENET_UNIX_H__
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+typedef int ENetSocket;
+
+enum
+{
+    ENET_SOCKET_NULL = -1
+};
+
+#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
+#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
+#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
+
+typedef struct
+{
+    void * data;
+    size_t dataLength;
+} ENetBuffer;
+
+#endif /* __ENET_UNIX_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/utility.h
@@ -1,0 +1,12 @@
+/** 
+ @file  utility.h
+ @brief ENet utility header
+*/
+#ifndef __ENET_UTILITY_H__
+#define __ENET_UTILITY_H__
+
+#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#endif /* __ENET_UTILITY_H__ */
+
--- /dev/null
+++ b/Engine/src/enet/include/win32.h
@@ -1,0 +1,32 @@
+/** 
+ @file  win32.h
+ @brief ENet Win32 header
+*/
+#ifndef __ENET_WIN32_H__
+#define __ENET_WIN32_H__
+
+#include <stdlib.h>
+#include <winsock2.h>
+
+typedef SOCKET ENetSocket;
+
+enum
+{
+    ENET_SOCKET_NULL = INVALID_SOCKET
+};
+
+#define ENET_HOST_TO_NET_16(value) (htons (value))
+#define ENET_HOST_TO_NET_32(value) (htonl (value))
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value))
+#define ENET_NET_TO_HOST_32(value) (ntohl (value))
+
+typedef struct
+{
+    size_t dataLength;
+    void * data;
+} ENetBuffer;
+
+#endif /* __ENET_WIN32_H__ */
+
+
--- /dev/null
+++ b/Engine/src/enet/list.c
@@ -1,0 +1,57 @@
+/** 
+ @file list.c
+ @brief ENet linked list functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "list.h"
+
+/** 
+    @defgroup list ENet linked list utility functions
+    @ingroup private
+    @{
+*/
+void
+enet_list_clear (ENetList * list)
+{
+   list -> sentinel.next = & list -> sentinel;
+   list -> sentinel.previous = & list -> sentinel;
+}
+
+ENetListIterator
+enet_list_insert (ENetListIterator position, void * data)
+{
+   ENetListIterator result = (ENetListIterator) data;
+
+   result -> previous = position -> previous;
+   result -> next = position;
+
+   result -> previous -> next = result;
+   position -> previous = result;
+
+   return result;
+}
+
+void *
+enet_list_remove (ENetListIterator position)
+{
+   position -> previous -> next = position -> next;
+   position -> next -> previous = position -> previous;
+
+   return position;
+}
+
+size_t
+enet_list_size (ENetList * list)
+{
+   size_t size = 0;
+   ENetListIterator position;
+
+   for (position = enet_list_begin (list);
+        position != enet_list_end (list);
+        position = enet_list_next (position))
+     ++ size;
+   
+   return size;
+}
+
+/** @} */
--- /dev/null
+++ b/Engine/src/enet/memory.c
@@ -1,0 +1,48 @@
+/** 
+ @file memory.c
+ @brief ENet memory management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "types.h"
+#include "memory.h"
+
+void *
+enet_malloc (size_t size)
+{
+   void * memory = malloc (size);
+
+   if (memory == NULL)
+     abort ();
+
+   return memory;
+}
+
+void *
+enet_realloc (void * memory, size_t size)
+{
+   memory = realloc (memory, size);
+
+   if (size > 0 &&
+       memory == NULL)
+     abort ();
+
+   return memory;
+}
+
+void *
+enet_calloc (size_t elements, size_t size)
+{
+   void * memory = calloc (elements, size);
+
+   if (memory == NULL)
+     abort ();
+
+   return memory;
+}
+
+void
+enet_free (void * memory)
+{
+   free (memory);
+}
+
--- /dev/null
+++ b/Engine/src/enet/packet.c
@@ -1,0 +1,73 @@
+/** 
+ @file  packet.c
+ @brief ENet packet management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "memory.h"
+#include "enet.h"
+
+/** @defgroup Packet ENet packet functions 
+    @{ 
+*/
+
+/** Creates a packet that may be sent to a peer.
+    @param dataContents initial contents of the packet's data; the packet's data will remain uninitialized if dataContents is NULL.
+    @param dataLength   size of the data allocated for this packet
+    @param flags        flags for this packet as described for the ENetPacket structure.
+    @returns the packet on success, NULL on failure
+*/
+ENetPacket *
+enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
+{
+    ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
+
+    packet -> data = (enet_uint8 *) enet_malloc (dataLength);
+
+    if (data != NULL)
+      memcpy (packet -> data, data, dataLength);
+
+    packet -> referenceCount = 0;
+    packet -> flags = flags;
+    packet -> dataLength = dataLength;
+
+    return packet;
+}
+
+/** Destroys the packet and deallocates its data.
+    @param packet packet to be destroyed
+*/
+void
+enet_packet_destroy (ENetPacket * packet)
+{
+    enet_free (packet -> data);
+    enet_free (packet);
+}
+
+/** Attempts to resize the data in the packet to length specified in the 
+    dataLength parameter 
+    @param packet packet to resize
+    @param dataLength new size for the packet data
+    @returns 0 on success, < 0 on failure
+*/
+int
+enet_packet_resize (ENetPacket * packet, size_t dataLength)
+{
+    enet_uint8 * newData;
+   
+    if (dataLength <= packet -> dataLength)
+    {
+       packet -> dataLength = dataLength;
+
+       return 0;
+    }
+
+    newData = (enet_uint8 *) enet_realloc (packet -> data, dataLength);
+
+    packet -> data = newData;
+    packet -> dataLength = dataLength;
+
+    return 0;
+}
+
+/** @} */
--- /dev/null
+++ b/Engine/src/enet/peer.c
@@ -1,0 +1,613 @@
+/** 
+ @file  peer.c
+ @brief ENet peer management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "memory.h"
+#include "enet.h"
+
+/** @defgroup peer ENet peer functions 
+    @{
+*/
+
+/** Configures throttle parameter for a peer.
+
+    Unreliable packets are dropped by ENet in response to the varying conditions
+    of the Internet connection to the peer.  The throttle represents a probability
+    that an unreliable packet should not be dropped and thus sent by ENet to the peer.
+    The lowest mean round trip time from the sending of a reliable packet to the
+    receipt of its acknowledgement is measured over an amount of time specified by
+    the interval parameter in milliseconds.  If a measured round trip time happens to
+    be significantly less than the mean round trip time measured over the interval, 
+    then the throttle probability is increased to allow more traffic by an amount
+    specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+    constant.  If a measured round trip time happens to be significantly greater than
+    the mean round trip time measured over the interval, then the throttle probability
+    is decreased to limit traffic by an amount specified in the deceleration parameter, which
+    is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant.  When the throttle has
+    a value of ENET_PEER_PACKET_THROTTLE_SCALE, on unreliable packets are dropped by 
+    ENet, and so 100% of all unreliable packets will be sent.  When the throttle has a
+    value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable
+    packets will be sent.  Intermediate values for the throttle represent intermediate
+    probabilities between 0% and 100% of unreliable packets being sent.  The bandwidth
+    limits of the local and foreign hosts are taken into account to determine a 
+    sensible limit for the throttle probability above which it should not raise even in
+    the best of conditions.
+
+    @param peer peer to configure 
+    @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL.
+    @param acceleration rate at which to increase the throttle probability as mean RTT declines
+    @param deceleration rate at which to decrease the throttle probability as mean RTT increases
+*/
+void
+enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration)
+{
+    ENetProtocol command;
+
+    peer -> packetThrottleInterval = interval;
+    peer -> packetThrottleAcceleration = acceleration;
+    peer -> packetThrottleDeceleration = deceleration;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE;
+    command.header.channelID = 0xFF;
+    command.header.flags = 0;
+    command.header.commandLength = sizeof (ENetProtocolThrottleConfigure);
+
+    command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval);
+    command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration);
+    command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration);
+
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+int
+enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
+{
+    if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance)
+    {
+        peer -> packetThrottle = peer -> packetThrottleLimit;
+    }
+    else
+    if (rtt < peer -> lastRoundTripTime)
+    {
+        peer -> packetThrottle += peer -> packetThrottleAcceleration;
+
+        if (peer -> packetThrottle > peer -> packetThrottleLimit)
+          peer -> packetThrottle = peer -> packetThrottleLimit;
+
+        return 1;
+    }
+    else
+    if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
+    {
+        if (peer -> packetThrottle > peer -> packetThrottleDeceleration)
+          peer -> packetThrottle -= peer -> packetThrottleDeceleration;
+        else
+          peer -> packetThrottle = 0;
+
+        return -1;
+    }
+
+    return 0;
+}
+
+/** Queues a packet to be sent.
+    @param peer destination for the packet
+    @param channelID channel on which to send
+    @param packet packet to send
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+int
+enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
+{
+   ENetChannel * channel = & peer -> channels [channelID];
+   ENetProtocol command;
+   size_t fragmentLength;
+
+   if (peer -> state != ENET_PEER_STATE_CONNECTED)
+     return -1;
+
+   fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
+
+   if (packet -> dataLength > fragmentLength)
+   {
+      enet_uint32 fragmentCount = ENET_HOST_TO_NET_32 ((packet -> dataLength + fragmentLength - 1) / fragmentLength),
+             startSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingReliableSequenceNumber + 1),
+             fragmentNumber,
+             fragmentOffset;
+
+      packet -> flags |= ENET_PACKET_FLAG_RELIABLE;
+
+      for (fragmentNumber = 0,
+             fragmentOffset = 0;
+           fragmentOffset < packet -> dataLength;
+           ++ fragmentNumber,
+             fragmentOffset += fragmentLength)
+      {
+         command.header.command = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT;
+         command.header.channelID = channelID;
+         command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+         command.header.commandLength = sizeof (ENetProtocolSendFragment);
+         command.sendFragment.startSequenceNumber = startSequenceNumber;
+         command.sendFragment.fragmentCount = fragmentCount;
+         command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber);
+         command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength);
+         command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset);
+
+         if (packet -> dataLength - fragmentOffset < fragmentLength)
+           fragmentLength = packet -> dataLength - fragmentOffset;
+
+         enet_peer_queue_outgoing_command (peer, & command, packet, fragmentOffset, fragmentLength);
+      }
+
+      return 0;
+   }
+
+   command.header.channelID = channelID;
+
+   if (packet -> flags & ENET_PACKET_FLAG_RELIABLE)
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE;
+      command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+      command.header.commandLength = sizeof (ENetProtocolSendReliable);
+   }
+   else
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE;
+      command.header.flags = 0;
+      command.header.commandLength = sizeof (ENetProtocolSendUnreliable);
+      command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingUnreliableSequenceNumber + 1);
+   }
+
+   enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength);
+
+   return 0;
+}
+
+/** Attempts to dequeue any incoming queued packet.
+    @param peer peer to dequeue packets from
+    @param channelID channel on which to receive
+    @returns a pointer to the packet, or NULL if there are no available incoming queued packets
+*/
+ENetPacket *
+enet_peer_receive (ENetPeer * peer, enet_uint8 channelID)
+{
+   ENetChannel * channel = & peer -> channels [channelID];
+   ENetIncomingCommand * incomingCommand = NULL;
+   ENetPacket * packet;
+
+   if (enet_list_empty (& channel -> incomingUnreliableCommands) == 0)
+   {
+      incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingUnreliableCommands);
+
+      if (incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber)
+        incomingCommand = NULL;
+      else
+        channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber;
+   }
+
+   if (incomingCommand == NULL &&
+       enet_list_empty (& channel -> incomingReliableCommands) == 0)
+   {
+      do
+      {
+        incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingReliableCommands);
+
+        if (incomingCommand -> fragmentsRemaining > 0 ||
+            incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber + 1)
+          return NULL;
+
+        if (incomingCommand -> reliableSequenceNumber <= channel -> incomingReliableSequenceNumber)
+        {
+           -- incomingCommand -> packet -> referenceCount;
+
+           if (incomingCommand -> packet -> referenceCount == 0)
+             enet_packet_destroy (incomingCommand -> packet);
+
+           if (incomingCommand -> fragments != NULL)
+             enet_free (incomingCommand -> fragments);
+
+           enet_list_remove (& incomingCommand -> incomingCommandList);
+
+           enet_free (incomingCommand);
+
+           incomingCommand = NULL;
+        }
+      } while (incomingCommand == NULL &&
+               enet_list_empty (& channel -> incomingReliableCommands) == 0);
+
+      if (incomingCommand == NULL)
+        return NULL;
+
+      channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber;
+
+      if (incomingCommand -> fragmentCount > 0)
+        channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1;
+   }
+
+   if (incomingCommand == NULL)
+     return NULL;
+
+   enet_list_remove (& incomingCommand -> incomingCommandList);
+
+   packet = incomingCommand -> packet;
+
+   -- packet -> referenceCount;
+
+   if (incomingCommand -> fragments != NULL)
+     enet_free (incomingCommand -> fragments);
+
+   enet_free (incomingCommand);
+
+   return packet;
+}
+
+static void
+enet_peer_reset_outgoing_commands (ENetList * queue)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    while (enet_list_empty (queue) == 0)
+    {
+       outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue));
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          -- outgoingCommand -> packet -> referenceCount;
+
+          if (outgoingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (outgoingCommand -> packet);
+       }
+
+       enet_free (outgoingCommand);
+    }
+}
+
+static void
+enet_peer_reset_incoming_commands (ENetList * queue)
+{
+    ENetIncomingCommand * incomingCommand;
+
+    while (enet_list_empty (queue) == 0)
+    {
+       incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (queue));
+
+       if (incomingCommand -> packet != NULL)
+       {
+          -- incomingCommand -> packet -> referenceCount;
+
+          if (incomingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (incomingCommand -> packet);
+       }
+
+       enet_free (incomingCommand);
+    }
+}
+
+void
+enet_peer_reset_queues (ENetPeer * peer)
+{
+    ENetChannel * channel;
+
+    while (enet_list_empty (& peer -> acknowledgements) == 0)
+      enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
+
+    enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands);
+
+    if (peer -> channels != NULL && peer -> channelCount > 0)
+    {
+        for (channel = peer -> channels;
+             channel < & peer -> channels [peer -> channelCount];
+             ++ channel)
+        {
+            enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);
+            enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);
+        }
+
+        enet_free (peer -> channels);
+    }
+
+    peer -> channels = NULL;
+    peer -> channelCount = 0;
+}
+
+/** Forcefully disconnects a peer.
+    @param peer peer to forcefully disconnect
+    @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout
+    on its connection to the local host.
+*/
+void
+enet_peer_reset (ENetPeer * peer)
+{
+    peer -> outgoingPeerID = 0xFFFF;
+    peer -> challenge = 0;
+
+    peer -> address.host = ENET_HOST_ANY;
+    peer -> address.port = 0;
+
+    peer -> state = ENET_PEER_STATE_DISCONNECTED;
+
+    peer -> incomingBandwidth = 0;
+    peer -> outgoingBandwidth = 0;
+    peer -> incomingBandwidthThrottleEpoch = 0;
+    peer -> outgoingBandwidthThrottleEpoch = 0;
+    peer -> incomingDataTotal = 0;
+    peer -> outgoingDataTotal = 0;
+    peer -> lastSendTime = 0;
+    peer -> lastReceiveTime = 0;
+    peer -> nextTimeout = 0;
+    peer -> packetLossEpoch = 0;
+    peer -> packetsSent = 0;
+    peer -> packetsLost = 0;
+    peer -> packetLoss = 0;
+    peer -> packetLossVariance = 0;
+    peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;
+    peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;
+    peer -> packetThrottleCounter = 0;
+    peer -> packetThrottleEpoch = 0;
+    peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;
+    peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;
+    peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;
+    peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lastRoundTripTimeVariance = 0;
+    peer -> highestRoundTripTimeVariance = 0;
+    peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> roundTripTimeVariance = 0;
+    peer -> mtu = peer -> host -> mtu;
+    peer -> reliableDataInTransit = 0;
+    peer -> outgoingReliableSequenceNumber = 0;
+    peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    enet_peer_reset_queues (peer);
+}
+
+/** Sends a ping request to a peer.
+    @param peer destination for the ping request
+    @remarks ping requests factor into the mean round trip time as designated by the 
+    roundTripTime field in the ENetPeer structure.  Enet automatically pings all connected
+    peers at regular intervals, however, this function may be called to ensure more
+    frequent ping requests.
+*/
+void
+enet_peer_ping (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_PING;
+    command.header.channelID = 0xFF;
+    command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    command.header.commandLength = sizeof (ENetProtocolPing);
+   
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+/** Force an immediate disconnection from a peer.
+    @param peer peer to disconnect
+    @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not
+    guarenteed to receive the disconnect notification, and is reset immediately upon
+    return from this function.
+*/
+void
+enet_peer_disconnect_now (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state != ENET_PEER_STATE_DISCONNECTED)
+      return;
+
+    if (peer -> state != ENET_PEER_STATE_ZOMBIE &&
+        peer -> state != ENET_PEER_STATE_DISCONNECTING)
+    {
+        enet_peer_reset_queues (peer);
+
+        command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+        command.header.channelID = 0xFF;
+        command.header.flags = 0;
+        command.header.commandLength = sizeof (ENetProtocolDisconnect);
+
+        enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+        enet_host_flush (peer -> host);
+    }
+
+    enet_peer_reset (peer);
+}
+
+/** Request a disconnection from a peer.
+    @param peer peer to request a disconnection
+    @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+    once the disconnection is complete.
+*/
+void
+enet_peer_disconnect (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTING ||
+        peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+        peer -> state == ENET_PEER_STATE_ZOMBIE)
+      return;
+
+    enet_peer_reset_queues (peer);
+
+    command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+    command.header.channelID = 0xFF;
+    command.header.flags = 0;
+    command.header.commandLength = sizeof (ENetProtocolDisconnect);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED)
+      command.header.flags |= ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED)
+      peer -> state = ENET_PEER_STATE_DISCONNECTING;
+    else
+    {
+        enet_host_flush (peer -> host);
+        enet_peer_reset (peer);
+    }
+}
+
+ENetAcknowledgement *
+enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint32 sentTime)
+{
+    ENetAcknowledgement * acknowledgement;
+
+    peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge);
+
+    acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement));
+
+    acknowledgement -> sentTime = sentTime;
+    acknowledgement -> command = * command;
+    
+    enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement);
+    
+    return acknowledgement;
+}
+
+ENetOutgoingCommand *
+enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length)
+{
+    ENetChannel * channel = & peer -> channels [command -> header.channelID];
+    ENetOutgoingCommand * outgoingCommand;
+
+    peer -> outgoingDataTotal += command -> header.commandLength + length;
+
+    outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+
+    if (command -> header.channelID == 0xFF)
+    {
+       ++ peer -> outgoingReliableSequenceNumber;
+
+       outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+    {
+       ++ channel -> outgoingReliableSequenceNumber;
+       
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    {
+       ++ channel -> outgoingUnreliableSequenceNumber;
+        
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+    }
+   
+    outgoingCommand -> sentTime = 0;
+    outgoingCommand -> roundTripTimeout = 0;
+    outgoingCommand -> roundTripTimeoutLimit = 0;
+    outgoingCommand -> fragmentOffset = offset;
+    outgoingCommand -> fragmentLength = length;
+    outgoingCommand -> packet = packet;
+    outgoingCommand -> command = * command;
+    outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_32 (outgoingCommand -> reliableSequenceNumber);
+
+    if (packet != NULL)
+      ++ packet -> referenceCount;
+
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+      enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand);
+    else
+      enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand);
+
+    return outgoingCommand;
+}
+
+ENetIncomingCommand *
+enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 fragmentCount)
+{
+    ENetChannel * channel = & peer -> channels [command -> header.channelID];
+    enet_uint32 unreliableSequenceNumber = 0;
+    ENetIncomingCommand * incomingCommand;
+    ENetListIterator currentCommand;
+
+    if (command -> header.command == ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE)
+      unreliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendUnreliable.unreliableSequenceNumber);
+
+    if (unreliableSequenceNumber == 0)
+    {
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if (incomingCommand -> reliableSequenceNumber <= command -> header.reliableSequenceNumber)
+          {
+             if (incomingCommand -> reliableSequenceNumber < command -> header.reliableSequenceNumber)
+               break;
+
+             goto freePacket;
+          }
+       }
+    }
+    else
+    {
+       if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+         goto freePacket;
+
+       if (unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+         goto freePacket;
+
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber)
+          {
+             if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber)
+               break;
+
+             goto freePacket;
+          }
+       }
+    }
+        
+    incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand));
+
+    incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber;
+    incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber;
+    incomingCommand -> command = * command;
+    incomingCommand -> fragmentCount = fragmentCount;
+    incomingCommand -> fragmentsRemaining = fragmentCount;
+    incomingCommand -> packet = packet;
+
+    if (fragmentCount > 0)
+      incomingCommand -> fragments = (enet_uint32 *) enet_calloc ((fragmentCount + 31) / 32, sizeof (enet_uint32));
+    else
+      incomingCommand -> fragments = NULL;
+
+    if (packet != NULL)
+      ++ packet -> referenceCount;
+
+    enet_list_insert (enet_list_next (currentCommand), incomingCommand);
+
+    return incomingCommand;
+
+freePacket:
+    if (packet != NULL)
+    {
+       if (packet -> referenceCount == 0)
+         enet_packet_destroy (packet);
+    }
+
+    return NULL;
+}
+
+/** @} */
--- /dev/null
+++ b/Engine/src/enet/protocol.c
@@ -1,0 +1,1259 @@
+/** 
+ @file  protocol.c
+ @brief ENet protocol functions
+*/
+#include <stdio.h>
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "utility.h"
+#include "memory.h"
+#include "time.h"
+#include "enet.h"
+
+static enet_uint32 timeCurrent;
+
+static int
+enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    ENetPeer * currentPeer = host -> lastServicedPeer;
+    ENetChannel * channel;
+
+    do
+    {
+       ++ currentPeer;
+       
+       if (currentPeer >= & host -> peers [host -> peerCount])
+         currentPeer = host -> peers;
+
+       if (currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+       {
+           host -> recalculateBandwidthLimits = 1;
+
+           event -> type = ENET_EVENT_TYPE_DISCONNECT;
+           event -> peer = currentPeer;
+
+           enet_peer_reset (currentPeer);
+
+           host -> lastServicedPeer = currentPeer;
+
+           return 1;
+       }
+
+       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+         continue;
+
+       for (channel = currentPeer -> channels;
+            channel < & currentPeer -> channels [currentPeer -> channelCount];
+            ++ channel)
+       {
+           if (enet_list_empty (& channel -> incomingReliableCommands) &&
+               enet_list_empty (& channel -> incomingUnreliableCommands))
+             continue;
+
+           event -> packet = enet_peer_receive (currentPeer, channel - currentPeer -> channels);
+           if (event -> packet == NULL)
+             continue;
+             
+           event -> type = ENET_EVENT_TYPE_RECEIVE;
+           event -> peer = currentPeer;
+           event -> channelID = (enet_uint8) (channel - currentPeer -> channels);
+
+           host -> lastServicedPeer = currentPeer;
+
+           return 1;
+       }
+    } while (currentPeer != host -> lastServicedPeer);
+
+    return 0;
+}
+
+static void
+enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    while (enet_list_empty (& peer -> sentUnreliableCommands) == 0)
+    {
+        outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
+        
+        enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+        if (outgoingCommand -> packet != NULL)
+        {
+           -- outgoingCommand -> packet -> referenceCount;
+
+           if (outgoingCommand -> packet -> referenceCount == 0)
+             enet_packet_destroy (outgoingCommand -> packet);
+        }
+
+        enet_free (outgoingCommand);
+    }
+}
+
+static ENetProtocolCommand
+enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint32 reliableSequenceNumber, enet_uint8 channelID)
+{
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+    ENetProtocolCommand commandNumber;
+
+    for (currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+         currentCommand != enet_list_end (& peer -> sentReliableCommands);
+         currentCommand = enet_list_next (currentCommand))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+        
+       if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+           outgoingCommand -> command.header.channelID == channelID)
+         break;
+    }
+
+    if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
+      return ENET_PROTOCOL_COMMAND_NONE;
+
+    commandNumber = outgoingCommand -> command.header.command;
+
+    enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+    if (outgoingCommand -> packet != NULL)
+    {
+       peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+       -- outgoingCommand -> packet -> referenceCount;
+
+       if (outgoingCommand -> packet -> referenceCount == 0)
+         enet_packet_destroy (outgoingCommand -> packet);
+    }
+
+    enet_free (outgoingCommand);
+
+    if (enet_list_empty (& peer -> sentReliableCommands))
+      return commandNumber;
+    
+    outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands);
+    
+    peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+
+    return commandNumber;
+} 
+
+static ENetPeer *
+enet_protocol_handle_connect (ENetHost * host, const ENetProtocolHeader * header, const ENetProtocol * command)
+{
+    enet_uint16 mtu;
+    enet_uint32 windowSize;
+    ENetChannel * channel;
+    size_t channelCount;
+    ENetPeer * currentPeer;
+    ENetProtocol verifyCommand;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolConnect))
+      return NULL;
+
+    channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT ||
+        channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      return NULL;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+          break;
+
+        if (currentPeer -> address.host == host -> receivedAddress.host &&
+            currentPeer -> address.port == host -> receivedAddress.port &&
+            currentPeer -> challenge == header -> challenge)
+        {
+            enet_peer_reset (currentPeer);
+            break;
+        }
+    }
+
+    if (currentPeer >= & host -> peers [host -> peerCount])
+      return NULL;
+
+    currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
+    currentPeer -> challenge = header -> challenge;
+    currentPeer -> address = host -> receivedAddress;
+    currentPeer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
+    currentPeer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
+    currentPeer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
+    currentPeer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval);
+    currentPeer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration);
+    currentPeer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration);
+    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    currentPeer -> channelCount = channelCount;
+
+    for (channel = currentPeer -> channels;
+         channel < & currentPeer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+    }
+
+    mtu = ENET_NET_TO_HOST_16 (command -> connect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    currentPeer -> mtu = mtu;
+
+    if (host -> outgoingBandwidth == 0 &&
+        currentPeer -> incomingBandwidth == 0)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      currentPeer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (host -> incomingBandwidth == 0)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) *
+                     ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize))
+      windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT;
+    verifyCommand.header.channelID = 0xFF;
+    verifyCommand.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE;
+    verifyCommand.header.commandLength = sizeof (ENetProtocolVerifyConnect);
+    verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+    verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu);
+    verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize);
+    verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+    verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+    verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+    verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+
+    enet_peer_queue_outgoing_command (currentPeer, & verifyCommand, NULL, 0, 0);
+
+    return currentPeer;
+}
+
+static void
+enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    ENetPacket * packet;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendReliable) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendReliable),
+                                 command -> header.commandLength - sizeof (ENetProtocolSendReliable),
+                                 ENET_PACKET_FLAG_RELIABLE);
+
+    enet_peer_queue_incoming_command (peer, command, packet, 0);
+}
+
+static void
+enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    ENetPacket * packet;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendUnreliable) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable),
+                                 command -> header.commandLength - sizeof (ENetProtocolSendUnreliable),
+                                 0);
+
+    enet_peer_queue_incoming_command (peer, command, packet, 0);
+}
+
+static void
+enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 fragmentNumber,
+           fragmentCount,
+           fragmentOffset,
+           fragmentLength,
+           startSequenceNumber,
+           totalLength;
+    ENetChannel * channel;
+    ENetListIterator currentCommand;
+    ENetIncomingCommand * startCommand;
+
+    if (command -> header.commandLength <= sizeof (ENetProtocolSendFragment) ||
+        command -> header.channelID >= peer -> channelCount ||
+        peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    startSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.startSequenceNumber);
+    fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+    fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+    fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+    totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+    fragmentLength = command -> header.commandLength - sizeof (ENetProtocolSendFragment);
+    
+    if (fragmentOffset >= totalLength ||
+        fragmentOffset + fragmentLength > totalLength ||
+        fragmentNumber >= fragmentCount)
+      return;
+ 
+    channel = & peer -> channels [command -> header.channelID];
+
+    if (startSequenceNumber <= channel -> incomingReliableSequenceNumber)
+      return;
+
+    for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+         currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+         currentCommand = enet_list_previous (currentCommand))
+    {
+       startCommand = (ENetIncomingCommand *) currentCommand;
+
+       if (startCommand -> command.header.command == ENET_PROTOCOL_COMMAND_SEND_FRAGMENT &&
+           startCommand -> command.sendFragment.startSequenceNumber == startSequenceNumber)
+         break;
+    }
+ 
+    if (currentCommand == enet_list_end (& channel -> incomingReliableCommands))
+    {
+       ENetProtocol hostCommand = * command;
+       
+       hostCommand.sendFragment.startSequenceNumber = startSequenceNumber;
+       hostCommand.sendFragment.fragmentNumber = fragmentNumber;
+       hostCommand.sendFragment.fragmentCount = fragmentCount;
+       hostCommand.sendFragment.fragmentOffset = fragmentOffset;
+       hostCommand.sendFragment.totalLength = totalLength;
+
+       startCommand = enet_peer_queue_incoming_command (peer, 
+                                                        & hostCommand, 
+                                                        enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_RELIABLE),
+                                                        fragmentCount);
+    }
+    else
+    if (totalLength != startCommand -> packet -> dataLength ||
+        fragmentCount != startCommand -> fragmentCount)
+      return;
+    
+    if ((startCommand -> fragments [fragmentNumber / 32] & (1 << fragmentNumber)) == 0)
+      -- startCommand -> fragmentsRemaining;
+
+    startCommand -> fragments [fragmentNumber / 32] |= (1 << fragmentNumber);
+
+    if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+      fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+    memcpy (startCommand -> packet -> data + fragmentOffset,
+            (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
+            fragmentLength);
+}
+
+static void
+enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolPing))
+      return;
+}
+
+static void
+enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolBandwidthLimit))
+      return;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth);
+
+    if (peer -> incomingBandwidth == 0 &&
+        host -> outgoingBandwidth == 0)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+                             ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+}
+
+static void
+enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolThrottleConfigure))
+      return;
+
+    peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval);
+    peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration);
+    peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration);
+}
+
+static void
+enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (command -> header.commandLength < sizeof (ENetProtocolDisconnect))
+      return;
+
+    enet_peer_reset_queues (peer);
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTED)
+      enet_peer_reset (peer);
+    else
+    if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE)
+      peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT;
+    else
+      peer -> state = ENET_PEER_STATE_ZOMBIE;
+}
+
+static int
+enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 roundTripTime,
+           receivedSentTime,
+           receivedReliableSequenceNumber;
+    ENetProtocolCommand commandNumber;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolAcknowledge))
+      return 0;
+
+    receivedSentTime = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedSentTime);
+
+    if (ENET_TIME_LESS (timeCurrent, receivedSentTime))
+      return 0;
+
+    peer -> lastReceiveTime = timeCurrent;
+
+    roundTripTime = ENET_TIME_DIFFERENCE (timeCurrent, receivedSentTime);
+
+    enet_peer_throttle (peer, roundTripTime);
+
+    peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4;
+
+    if (roundTripTime >= peer -> roundTripTime)
+    {
+       peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8;
+       peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4;
+    }
+    else
+    {
+       peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8;
+       peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4;
+    }
+
+    if (peer -> roundTripTime < peer -> lowestRoundTripTime)
+      peer -> lowestRoundTripTime = peer -> roundTripTime;
+
+    if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) 
+      peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+
+    if (peer -> packetThrottleEpoch == 0 ||
+        ENET_TIME_DIFFERENCE(timeCurrent, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval)
+    {
+        peer -> lastRoundTripTime = peer -> lowestRoundTripTime;
+        peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance;
+        peer -> lowestRoundTripTime = peer -> roundTripTime;
+        peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+        peer -> packetThrottleEpoch = timeCurrent;
+    }
+
+    receivedReliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedReliableSequenceNumber);
+
+    commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID);
+
+    switch (peer -> state)
+    {
+    case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT)
+         return 0;
+
+       host -> recalculateBandwidthLimits = 1;
+
+       peer -> state = ENET_PEER_STATE_CONNECTED;
+
+       event -> type = ENET_EVENT_TYPE_CONNECT;
+       event -> peer = peer;
+
+       return 1;
+
+    case ENET_PEER_STATE_DISCONNECTING:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT)
+         return 0;
+
+       host -> recalculateBandwidthLimits = 1;
+
+       event -> type = ENET_EVENT_TYPE_DISCONNECT;
+       event -> peer = peer;
+
+       enet_peer_reset (peer);
+
+       return 1;
+
+    default:
+       break;
+    }
+   
+    return 0;
+}
+
+static void
+enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint16 mtu;
+    enet_uint32 windowSize;
+
+    if (command -> header.commandLength < sizeof (ENetProtocolVerifyConnect) ||
+        peer -> state != ENET_PEER_STATE_CONNECTING)
+      return;
+
+    if (ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount) != peer -> channelCount ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration)
+    {
+        peer -> state = ENET_PEER_STATE_ZOMBIE;
+
+        return;
+    }
+
+    peer -> state = ENET_PEER_STATE_CONNECTED;
+
+    peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID);
+
+    mtu = ENET_NET_TO_HOST_16 (command -> verifyConnect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else 
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    if (mtu < peer -> mtu)
+      peer -> mtu = mtu;
+
+    windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (windowSize < peer -> windowSize)
+      peer -> windowSize = windowSize;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth);
+
+    host -> recalculateBandwidthLimits = 1;
+
+    peer -> state = ENET_PEER_STATE_CONNECTED;
+
+    event -> type = ENET_EVENT_TYPE_CONNECT;
+    event -> peer = peer;
+}
+
+static int
+enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    ENetProtocolHeader * header;
+    ENetProtocol * command;
+    ENetPeer * peer;
+    enet_uint8 * currentData;
+    size_t commandCount;
+
+    if (host -> receivedDataLength < sizeof (ENetProtocolHeader))
+      return 0;
+
+    header = (ENetProtocolHeader *) host -> receivedData;
+
+    header -> peerID = ENET_NET_TO_HOST_16 (header -> peerID);
+    header -> sentTime = ENET_NET_TO_HOST_32 (header -> sentTime);
+
+    if (header -> peerID == 0xFFFF)
+      peer = NULL;
+    else
+    if (header -> peerID >= host -> peerCount)
+      return 0;
+    else
+    {
+       peer = & host -> peers [header -> peerID];
+
+       if (peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+           peer -> state == ENET_PEER_STATE_ZOMBIE || 
+           host -> receivedAddress.host != peer -> address.host ||
+           header -> challenge != peer -> challenge)
+         return 0;
+       else
+         peer -> address.port = host -> receivedAddress.port;
+    }
+
+    if (peer != NULL)
+      peer -> incomingDataTotal += host -> receivedDataLength;
+
+    commandCount = header -> commandCount;
+    currentData = host -> receivedData + sizeof (ENetProtocolHeader);
+  
+    while (commandCount > 0 &&
+           currentData < & host -> receivedData [host -> receivedDataLength])
+    {
+       command = (ENetProtocol *) currentData;
+
+       if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength])
+         return 0;
+
+       command -> header.commandLength = ENET_NET_TO_HOST_32 (command -> header.commandLength);
+
+       if (currentData + command -> header.commandLength > & host -> receivedData [host -> receivedDataLength])
+         return 0;
+
+       -- commandCount;
+       currentData += command -> header.commandLength;
+
+       if (peer == NULL)
+       {
+          if (command -> header.command != ENET_PROTOCOL_COMMAND_CONNECT)
+            return 0;
+       }
+         
+       command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> header.reliableSequenceNumber);
+
+       switch (command -> header.command)
+       {
+       case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE:
+          enet_protocol_handle_acknowledge (host, event, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_CONNECT:
+          peer = enet_protocol_handle_connect (host, header, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT:
+          enet_protocol_handle_verify_connect (host, event, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_DISCONNECT:
+          enet_protocol_handle_disconnect (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_PING:
+          enet_protocol_handle_ping (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+          enet_protocol_handle_send_reliable (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+          enet_protocol_handle_send_unreliable (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+          enet_protocol_handle_send_fragment (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT:
+          enet_protocol_handle_bandwidth_limit (host, peer, command);
+
+          break;
+
+       case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE:
+          enet_protocol_handle_throttle_configure (host, peer, command);
+
+          break;
+
+       default:
+          break;
+       }
+
+       if (peer != NULL &&
+           (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) != 0)
+       {
+           switch (peer -> state)
+           {
+           case ENET_PEER_STATE_DISCONNECTING:
+              break;
+
+           case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT:
+              if (command -> header.command != ENET_PROTOCOL_COMMAND_DISCONNECT)
+                break;
+
+           default:   
+              enet_peer_queue_acknowledgement (peer, command, header -> sentTime);        
+
+              break;
+           }
+       }
+    }
+
+    if (event -> type != ENET_EVENT_TYPE_NONE)
+      return 1;
+
+    return 0;
+}
+ 
+static int
+enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    for (;;)
+    {
+       int receivedLength;
+       ENetBuffer buffer;
+
+       buffer.data = host -> receivedData;
+       buffer.dataLength = sizeof (host -> receivedData);
+
+       receivedLength = enet_socket_receive (host -> socket,
+                                             & host -> receivedAddress,
+                                             & buffer,
+                                             1);
+
+       if (receivedLength < 0)
+         return -1;
+
+       if (receivedLength == 0)
+         return 0;
+
+       host -> receivedDataLength = receivedLength;
+       
+       switch (enet_protocol_handle_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+       
+       case -1:
+          return -1;
+
+       default:
+          break;
+       }
+    }
+
+    return -1;
+}
+
+static void
+enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetAcknowledgement * acknowledgement;
+    ENetListIterator currentAcknowledgement;
+  
+    currentAcknowledgement = enet_list_begin (& peer -> acknowledgements);
+         
+    while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements))
+    {
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
+         break;
+
+       acknowledgement = (ENetAcknowledgement *) currentAcknowledgement;
+ 
+       if (peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT &&
+           acknowledgement -> command.header.command != ENET_PROTOCOL_COMMAND_DISCONNECT)
+         continue;
+
+       currentAcknowledgement = enet_list_next (currentAcknowledgement);
+
+       buffer -> data = command;
+       buffer -> dataLength = sizeof (ENetProtocolAcknowledge);
+
+       host -> packetSize += buffer -> dataLength;
+ 
+       command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE;
+       command -> header.channelID = acknowledgement -> command.header.channelID;
+       command -> header.flags = 0;
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (sizeof (ENetProtocolAcknowledge));
+       command -> acknowledge.receivedReliableSequenceNumber = ENET_HOST_TO_NET_32 (acknowledgement -> command.header.reliableSequenceNumber);
+       command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_32 (acknowledgement -> sentTime);
+  
+       if (acknowledgement -> command.header.command == ENET_PROTOCOL_COMMAND_DISCONNECT)
+         peer -> state = ENET_PEER_STATE_ZOMBIE;
+
+       enet_list_remove (& acknowledgement -> acknowledgementList);
+       enet_free (acknowledgement);
+
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static void
+enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands);
+    
+    while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength ||
+           (outgoingCommand -> packet != NULL &&
+             peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength + 
+                                                         outgoingCommand -> packet -> dataLength))
+         break;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER;
+          peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE;
+          
+          if (peer -> packetThrottleCounter > peer -> packetThrottle)
+          {
+             -- outgoingCommand -> packet -> referenceCount;
+
+             if (outgoingCommand -> packet -> referenceCount == 0)
+               enet_packet_destroy (outgoingCommand -> packet);
+         
+             enet_list_remove (& outgoingCommand -> outgoingCommandList);
+             enet_free (outgoingCommand);
+           
+             continue;
+          }
+       }
+
+       buffer -> data = command;
+       buffer -> dataLength = outgoingCommand -> command.header.commandLength;
+      
+       host -> packetSize += buffer -> dataLength;
+
+       * command = outgoingCommand -> command;
+       
+       enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          ++ buffer;
+          
+          buffer -> data = outgoingCommand -> packet -> data;
+          buffer -> dataLength = outgoingCommand -> packet -> dataLength;
+
+          command -> header.commandLength += buffer -> dataLength;
+
+          host -> packetSize += buffer -> dataLength;
+
+          enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
+       }
+       else
+         enet_free (outgoingCommand);
+
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength);
+
+       ++ command;
+       ++ buffer;
+    } 
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static int
+enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+
+    while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (ENET_TIME_DIFFERENCE (timeCurrent, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)
+         continue;
+
+       if (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit)
+       {
+          event -> type = ENET_EVENT_TYPE_DISCONNECT;
+          event -> peer = peer;
+
+          enet_peer_reset (peer);
+
+          return 1;
+       }
+
+       if (outgoingCommand -> packet != NULL)
+         peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+          
+       ++ peer -> packetsLost;
+
+       outgoingCommand -> roundTripTimeout *= 2;
+
+       enet_list_insert (enet_list_begin (& peer -> outgoingReliableCommands),
+                         enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+       if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
+           enet_list_empty (& peer -> sentReliableCommands) == 0)
+       {
+          outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+          peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+       }
+    }
+    
+    return 0;
+}
+
+static void
+enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+
+    currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
+    
+    while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength)
+         break;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          if ((enet_uint16) (peer -> mtu - host -> packetSize) < 
+			  (enet_uint16) (outgoingCommand -> command.header.commandLength + outgoingCommand -> fragmentLength) 
+			  ||
+              peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > peer -> windowSize)
+            break;
+       }
+       
+       if (outgoingCommand -> roundTripTimeout == 0)
+       {
+          outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
+          outgoingCommand -> roundTripTimeoutLimit = ENET_PEER_TIMEOUT_LIMIT * outgoingCommand -> roundTripTimeout;
+       }
+
+       if (enet_list_empty (& peer -> sentReliableCommands))
+         peer -> nextTimeout = timeCurrent + outgoingCommand -> roundTripTimeout;
+
+       enet_list_insert (enet_list_end (& peer -> sentReliableCommands),
+                         enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+       outgoingCommand -> sentTime = timeCurrent;
+
+       buffer -> data = command;
+       buffer -> dataLength = outgoingCommand -> command.header.commandLength;
+
+       host -> packetSize += buffer -> dataLength;
+
+       * command = outgoingCommand -> command;
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          ++ buffer;
+          
+          buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
+          buffer -> dataLength = outgoingCommand -> fragmentLength;
+
+          command -> header.commandLength += outgoingCommand -> fragmentLength;
+
+          host -> packetSize += outgoingCommand -> fragmentLength;
+
+          peer -> reliableDataInTransit += outgoingCommand -> fragmentLength;
+       }
+
+       command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength);
+
+       ++ peer -> packetsSent;
+        
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static int
+enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
+{
+    size_t packetsSent = 1;
+    ENetProtocolHeader header;
+    ENetPeer * currentPeer;
+    int sentLength;
+    
+    while (packetsSent > 0)
+    for (currentPeer = host -> peers,
+           packetsSent = 0;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
+            currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+          continue;
+
+        host -> commandCount = 0;
+        host -> bufferCount = 1;
+        host -> packetSize = sizeof (ENetProtocolHeader);
+
+        if (enet_list_empty (& currentPeer -> acknowledgements) == 0)
+          enet_protocol_send_acknowledgements (host, currentPeer);
+     
+        if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol))
+        {
+            if (checkForTimeouts != 0 &&
+                enet_list_empty (& currentPeer -> sentReliableCommands) == 0 &&
+                ENET_TIME_GREATER_EQUAL (timeCurrent, currentPeer -> nextTimeout) &&
+                enet_protocol_check_timeouts (host, currentPeer, event) == 1)
+              return 1;
+        }
+        if (enet_list_empty (& currentPeer -> outgoingReliableCommands) == 0)
+          enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
+        else
+        if (enet_list_empty (& currentPeer -> sentReliableCommands) &&
+            ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> lastReceiveTime) >= ENET_PEER_PING_INTERVAL &&
+            currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
+        { 
+            enet_peer_ping (currentPeer);
+            enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
+        }
+                      
+        if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol) &&
+            enet_list_empty (& currentPeer -> outgoingUnreliableCommands) == 0)
+          enet_protocol_send_unreliable_outgoing_commands (host, currentPeer);
+
+        if (host -> commandCount == 0)
+          continue;
+
+        if (currentPeer -> packetLossEpoch == 0)
+          currentPeer -> packetLossEpoch = timeCurrent;
+        else
+        if (ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL &&
+            currentPeer -> packetsSent > 0)
+        {
+           enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
+
+#ifdef ENET_DEBUG
+#ifdef WIN32
+           printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands));
+#else
+           fprintf (stderr, "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands));
+#endif
+#endif
+          
+           currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4;
+
+           if (packetLoss >= currentPeer -> packetLoss)
+           {
+              currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8;
+              currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4;
+           }
+           else
+           {
+              currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8;
+              currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4;
+           }
+
+           currentPeer -> packetLossEpoch = timeCurrent;
+           currentPeer -> packetsSent = 0;
+           currentPeer -> packetsLost = 0;
+        }
+
+        header.peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID);
+        header.flags = 0;
+        header.commandCount = host -> commandCount;
+        header.sentTime = ENET_HOST_TO_NET_32 (timeCurrent);
+        header.challenge = currentPeer -> challenge;
+
+        host -> buffers -> data = & header;
+        host -> buffers -> dataLength = sizeof (ENetProtocolHeader);
+
+        currentPeer -> lastSendTime = timeCurrent;
+
+        ++ packetsSent;
+
+        sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
+
+        enet_protocol_remove_sent_unreliable_commands (currentPeer);
+
+        if (sentLength < 0)
+          return -1;
+    }
+   
+    return 0;
+}
+
+/** Sends any queued packets on the host specified to its designated peers.
+
+    @param host   host to flush
+    @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service().
+    @ingroup host
+*/
+void
+enet_host_flush (ENetHost * host)
+{
+    timeCurrent = enet_time_get ();
+
+    enet_protocol_send_outgoing_commands (host, NULL, 0);
+}
+
+/** Waits for events on the host specified and shuttles packets between
+    the host and its peers.
+
+    @param host    host to service
+    @param event   an event structure where event details will be placed if one occurs
+    @param timeout number of milliseconds that ENet should wait for events
+    @retval > 1 if an event occurred within the specified time limit
+    @retval 0 if no event occurred
+    @retval < 1 on failure
+    @remarks enet_host_service should be called fairly regularly for adequate performance
+    @ingroup host
+*/
+int
+enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
+{
+    enet_uint32 waitCondition;
+
+    event -> type = ENET_EVENT_TYPE_NONE;
+    event -> peer = NULL;
+    event -> packet = NULL;
+
+    switch (enet_protocol_dispatch_incoming_commands (host, event))
+    {
+    case 1:
+       return 1;
+
+    case -1:
+       perror ("Error dispatching incoming packets");
+
+       return -1;
+
+    default:
+       break;
+    }
+   
+    timeCurrent = enet_time_get ();
+    
+    timeout += timeCurrent;
+
+    do
+    {
+       if (ENET_TIME_DIFFERENCE (timeCurrent, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+         enet_host_bandwidth_throttle (host);
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error sending outgoing packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_receive_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error receiving incoming packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error sending outgoing packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_dispatch_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+          perror ("Error dispatching incoming packets");
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       timeCurrent = enet_time_get ();
+
+       if (ENET_TIME_GREATER_EQUAL (timeCurrent, timeout))
+         return 0;
+
+       waitCondition = ENET_SOCKET_WAIT_RECEIVE;
+
+       if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, timeCurrent)) != 0)
+         return -1;
+       
+       timeCurrent = enet_time_get ();
+    } while (waitCondition == ENET_SOCKET_WAIT_RECEIVE);
+
+    return 0; 
+}
+
--- /dev/null
+++ b/Engine/src/enet/tutorial.txt
@@ -1,0 +1,325 @@
+* Using ENet
+
+    Before using ENet, you must call enet_initialize() to initialize the
+library. Upon program exit, you should call enet_deinitialize() so that
+the library may clean up any used resources.
+
+i.e.
+
+int 
+main (int argc, char ** argv) 
+{
+    if (enet_initialize () != 0)
+    {
+        fprintf (stderror, "An error occurred while initializing ENet.\n");
+        return EXIT_FAILURE;
+    }
+    atexit (enet_deinitialize);
+    ...
+    ...
+    ...
+}
+        
+* Creating an ENet server
+
+    Servers in ENet are constructed with enet_host_create(). You must specify
+an address on which to receive data and new connections, as well as the maximum
+allowable numbers of connected peers. You may optionally specify the incoming
+and outgoing bandwidth of the server in bytes per second so that ENet may try
+to statically manage bandwidth resources among connected peers in addition to
+its dynamic throttling algorithm; specifying 0 for these two options will cause 
+ENet to rely entirely upon its dynamic throttling algorithm to manage 
+bandwidth.
+
+    When done with a host, the host may be destroyed with enet_host_destroy().
+All connected peers to the host will be reset, and the resources used by
+the host will be freed.
+
+i.e.
+
+    ENetAddress address;
+    ENetHost * server;
+
+    /* Bind the server to the default localhost.
+     * A specific host address can be specified by
+     * enet_address_set_host (& address, "x.x.x.x");
+     */
+    address.host = ENET_HOST_ANY;
+    /* Bind the server to port 1234. */
+    address.port = 1234;
+
+    server = enet_host_create (& address /* the address to bind the server host to */, 
+                32 /* allow up to 32 clients and/or outgoing connections */,
+                0 /* assume any amount of incoming bandwidth */,
+                0 /* assume any amount of outgoing bandwidth */);
+    if (server == NULL)
+    {
+        fprintf (stderr, 
+                 "An error occurred while trying to create an ENet server host.\n");
+        exit (EXIT_FAILURE);
+    }
+    ...
+    ...
+    ...
+    enet_host_destroy(server);
+
+* Creating an ENet client
+
+    Clients in ENet are similarly constructed with enet_host_create() when no
+address is specified to bind the host to. Bandwidth may be specified for the 
+client host as in the above example. The peer count controls the maximum number
+of connections to other server hosts that may be simultaneously open.
+
+i.e.
+
+    ENetHost * client;
+
+    clienet = enet_host_create (NULL /* create a client host */,
+                1 /* only allow 1 outgoing connection */,
+                57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
+                14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
+
+    if (client == NULL)
+    {
+        fprintf (stderr, 
+                 "An error occurred while trying to create an ENet client host.\n");
+        exit (EXIT_FAILURE);
+    }
+    ...
+    ...
+    ...
+    enet_host_destroy(client);
+
+* Managing an ENet host
+
+    ENet uses a polled event model to notify the programmer of significant 
+events. ENet hosts are polled for events with enet_host_service(), where an
+optional timeout value in milliseconds may be specified to control how long
+ENet will poll; if a timeout of 0 is specified, enet_host_service() will
+return immediately if there are no events to dispatch. enet_host_service()
+will return 1 if an event was dispatched within the specified timeout.
+
+    Currently there are only four types of significant events in ENet:
+
+An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred
+within the specified time limit. enet_host_service() will return 0
+with this event.
+
+An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client
+host has connected to the server host or when an attempt to establish a 
+connection with a foreign host has succeeded. Only the "peer" field of the 
+event structure is valid for this event and contains the newly connected peer.
+
+An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received
+from a connected peer. The "peer" field contains the peer the packet was 
+received from, "channelID" is the channel on which the packet was sent, and 
+"packet" is the packet that was sent. The packet contained in the "packet" 
+field must be destroyed with enet_packet_destroy() when you are done 
+inspecting its contents.
+
+An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer
+has either explicitly disconnected or timed out. Only the "peer" field of the
+event structure is valid for this event and contains the peer that 
+disconnected. Only the "data" field of the peer is still valid on a 
+disconnect event and must be explicitly reset.
+
+i.e.
+
+    ENetEvent event;
+    
+    /* Wait up to 1000 milliseconds for an event. */
+    while (enet_host_service (client, & event, 1000) > 0)
+    {
+        switch (event.type)
+        {
+        case ENET_EVENT_TYPE_CONNECT:
+            printf ("A new client connected from %x:%u.\n", 
+                    event.peer -> address.host,
+                    event.peer -> address.port);
+
+            /* Store any relevant client information here. */
+            event.peer -> data = "Client information";
+
+            break;
+
+        case ENET_EVENT_TYPE_RECEIVE:
+            printf ("A packet of length %u containing %s was received from %s on channel %u.\n",
+                    event.packet -> dataLength,
+                    event.packet -> data,
+                    event.peer -> data,
+                    event.channelID);
+
+            /* Clean up the packet now that we're done using it. */
+            enet_packet_destroy (event.packet);
+            
+            break;
+           
+        case ENET_EVENT_TYPE_DISCONNECT:
+            printf ("%s disconected.\n", event.peer -> data);
+
+            /* Reset the peer's client information. */
+
+            event.peer -> data = NULL;
+        }
+    }
+    ...
+    ...
+    ...
+
+* Sending a packet to an ENet peer            
+
+    Packets in ENet are created with enet_packet_create(), where the size of
+the packet must be specified. Optionally, initial data may be specified to 
+copy into the packet.
+
+    Certain flags may also be supplied to enet_packet_create() to control 
+various packet features:
+
+ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable delivery.
+A reliable packet is guarenteed to be delivered, and a number of retry attempts
+will be made until an acknowledgement is received from the foreign host the
+packet is sent to. If a certain number of retry attempts is reached without
+any acknowledgement, ENet will assume the peer has disconnected and forcefully
+reset the connection. If this flag is not specified, the packet is assumed
+an unreliable packet, and no retry attempts will be made nor acknowledgements
+generated.
+
+    A packet may be resized (extended or truncated) with enet_packet_resize().
+
+    A packet is sent to a foreign host with enet_peer_send(). enet_peer_send()
+accepts a channel id over which to send the packet to a given peer. Once the
+packet is handed over to ENet with enet_peer_send(), ENet will handle its
+deallocation and enet_packet_destroy() should not be used upon it.
+
+    One may also use enet_host_broadcast() to send a packet to all connected
+peers on a given host over a specified channel id, as with enet_peer_send().
+
+    Queued packets will be sent on a call to enet_host_service().
+Alternatively, enet_host_flush() will send out queued packets without
+dispatching any events.
+
+i.e.
+
+    /* Create a reliable packet of size 7 containing "packet\0" */
+    ENetPacket * packet = enet_packet_create ("packet", 
+                                              strlen ("packet") + 1, 
+                                              ENET_PACKET_FLAG_RELIABLE);
+
+    /* Extend the packet so and append the string "foo", so it now
+     * contains "packetfoo\0"
+     *
+    enet_packet_resize (packet, strlen ("packetfoo") + 1);
+    strcpy (& packet -> data [strlen ("packet")], "foo");
+    
+    /* Send the packet to the peer over channel id 3.
+     * One could also broadcast the packet by
+     * enet_host_broadcast (host, 3, packet);
+     */
+    enet_peer_send (peer, 3, packet);
+    ...
+    ...
+    ...
+    /* One could just use enet_host_service() instead. */
+    enet_host_flush (host);
+
+* Disconnecting an ENet peer
+
+    Peers may be gently disconnected with enet_peer_disconnect(). A disconnect
+request will be sent to the foreign host, and ENet will wait for an 
+acknowledgement from the foreign host before finally disconnecting. An
+event of type ENET_EVENT_TYPE_DISCONNECT will be generated once the
+disconnection succeeds. Normally timeouts apply to the disconnect
+acknowledgement, and so if no acknowledgement is received after a length
+of time the peer will be forcefully disconnected.
+
+    enet_peer_reset() will forcefully disconnect a peer. The foreign host
+will get no notification of a disconnect and will time out on the foreign
+host. No event is generated.
+
+i.e.
+    ENetEvent event;
+    
+    enet_peer_disconnect (& client -> peers [0]);
+
+    /* Allow up to 3 seconds for the disconnect to succeed
+     * and drop any packets received packets.
+     */
+    while (enet_host_service (client, & event, 3000) > 0)
+    {
+        switch (event.type)
+        {
+        case ENET_EVENT_TYPE_RECEIVE:
+            enet_packet_destroy (event.packet);
+            break;
+
+        case ENET_EVENT_TYPE_DISCONNECT:
+            puts ("Disconnection succeeded.");
+            return;
+        ...
+        ...
+        ...
+        }
+    }
+    
+    /* We've arrived here, so the disconnect attempt didn't succeed yet.
+     * Force the connection down.
+     */
+    enet_peer_reset (& client -> peers [0]);
+    ...
+    ...
+    ...
+
+* Connecting to an ENet host
+
+    A connection to a foregin host is initiated with enet_host_connect().
+It accepts the address of a foreign host to connect to, and the number of
+channels that should be allocated for communication. If N channels are
+allocated for use, their channel ids will be numbered 0 through N-1.
+A peer representing the connection attempt is returned, or NULL if there
+were no available peers over which to initiate the connection. When the 
+connection attempt succeeds, an event of type ENET_EVENT_TYPE_CONNECT will 
+be generated. If the connection attempt times out or otherwise fails, an
+event of type ENET_EVENT_TYPE_DISCONNECT will be generated.
+
+i.e.
+    ENetAddress address;
+    ENetEvent event;
+    ENetPeer *peer;
+
+    /* Connect to some.server.net:1234. */
+    enet_address_set_host (& address, "some.server.net");
+    address.port = 1234;
+
+    /* Initiate the connection, allocating the two channels 0 and 1. */
+    peer = enet_host_connect (client, & address, 2);    
+    
+    if (peer == NULL)
+    {
+       fprintf (stderr, 
+                "No available peers for initiating an ENet connection.\n");
+       exit (EXIT_FAILURE);
+    }
+    
+    /* Wait up to 5 seconds for the connection attempt to succeed.
+    if (enet_host_service (client, & event, 5000) > 0 &&
+        event.type == ENET_EVENT_TYPE_CONNECT)
+    {
+        puts ("Connection to some.server.net:1234 succeeded.");
+        ...
+        ...
+        ...
+    }
+    else
+    {
+        /* Either the 5 seconds are up or a disconnect event was
+         * received. Reset the peer in the event the 5 seconds
+         * had run out without any significant event.
+         */
+        enet_peer_reset (peer);
+
+        puts ("Connection to some.server.net:1234 failed.");
+    }
+    ...
+    ...
+    ...
+
--- /dev/null
+++ b/Engine/src/enet/unix.c
@@ -1,0 +1,367 @@
+/** 
+ @file  unix.c
+ @brief ENet Unix system specific functions
+*/
+#ifndef WIN32
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#define ENET_BUILDING_LIB 1
+#include "enet/enet.h"
+
+#ifdef HAS_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef HAS_POLL
+#include <sys/poll.h>
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+
+    return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+    
+    timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYNAME_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+#ifdef linux
+    gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    hostEntry = gethostbyname (name);
+#endif
+
+    if (hostEntry == NULL ||
+        hostEntry -> h_addrtype != AF_INET)
+      return -1;
+
+    address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+    struct in_addr in;
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYADDR_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+    in.s_addr = address -> host;
+
+#ifdef linux
+    gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    in.s_addr = address -> host;
+
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+#endif
+
+    if (hostEntry == NULL)
+      return -1;
+
+    strncpy (name, hostEntry -> h_name, nameLength);
+
+    return 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type, const ENetAddress * address)
+{
+    ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+    int receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE;
+#ifndef HAS_FCNTL
+    int nonBlocking = 1;
+#endif
+    struct sockaddr_in sin;
+
+    if (newSocket == ENET_SOCKET_NULL)
+      return ENET_SOCKET_NULL;
+
+    if (type == ENET_SOCKET_TYPE_DATAGRAM)
+    {
+#ifdef HAS_FCNTL
+        fcntl (newSocket, F_SETFL, O_NONBLOCK | fcntl (newSocket, F_GETFL));
+#else
+        ioctl (newSocket, FIONBIO, & nonBlocking);
+#endif
+
+        setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int));
+    }
+    
+    if (address == NULL)
+      return newSocket;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    if (bind (newSocket, 
+              (struct sockaddr *) & sin,
+              sizeof (struct sockaddr_in)) == -1 ||
+        (type == ENET_SOCKET_TYPE_STREAM &&
+          listen (newSocket, SOMAXCONN) == -1))
+    {
+       close (newSocket);
+
+       return ENET_SOCKET_NULL;
+    }
+
+    return newSocket;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    int result;
+    struct sockaddr_in sin;
+    socklen_t sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+    
+    if (result == -1)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+} 
+    
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    close (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int sentLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = (struct iovec *) buffers;
+    msgHdr.msg_iovlen = bufferCount;
+
+    sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
+    
+    if (sentLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int recvLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = (struct iovec *) buffers;
+    msgHdr.msg_iovlen = bufferCount;
+
+    recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
+
+    if (recvLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+#ifdef HAS_MSGHDR_FLAGS
+    if (msgHdr.msg_flags & MSG_TRUNC)
+      return -1;
+#endif
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return recvLength;
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+#ifdef HAS_POLL
+    struct pollfd pollSocket;
+    int pollCount;
+    
+    pollSocket.fd = socket;
+    pollSocket.events = 0;
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      pollSocket.events |= POLLOUT;
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      pollSocket.events |= POLLIN;
+
+    pollCount = poll (& pollSocket, 1, timeout);
+
+    if (pollCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (pollCount == 0)
+      return 0;
+
+    if (pollSocket.revents & POLLOUT)
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (pollSocket.revents & POLLIN)
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#else
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#endif
+}
+
+#endif
+
--- /dev/null
+++ b/Engine/src/enet/win32.c
@@ -1,0 +1,309 @@
+/** 
+ @file  win32.c
+ @brief ENet Win32 system specific functions
+*/
+#ifdef WIN32
+
+#include <time.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <string.h>
+
+
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    WORD versionRequested = MAKEWORD (1, 1);
+    WSADATA wsaData;
+   
+    if (WSAStartup (versionRequested, & wsaData))
+       return -1;
+
+    if (LOBYTE (wsaData.wVersion) != 1||
+        HIBYTE (wsaData.wVersion) != 1)
+    {
+       WSACleanup ();
+       
+       return -1;
+    }
+
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+    WSACleanup ();
+}
+
+enet_uint32 
+enet_time_get_raw (void)
+{
+	SYSTEMTIME lp;
+	GetSystemTime(&lp);
+/*
+	time_t mytime;
+	time(&mytime);
+*/
+	return (enet_uint32)( (lp.wSecond << 16)|(lp.wMilliseconds<<0) );
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    return (enet_uint32) GetTickCount () - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    timeBase = (enet_uint32) GetTickCount () - newTimeBase;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+    struct hostent * hostEntry;
+
+    hostEntry = gethostbyname (name);
+    if (hostEntry == NULL ||
+        hostEntry -> h_addrtype != AF_INET)
+      return -1;
+
+    address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+    struct in_addr in;
+    struct hostent * hostEntry;
+    
+    in.s_addr = address -> host;
+    
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+    if (hostEntry == NULL)
+      return -1;
+
+    strncpy (name, hostEntry -> h_name, nameLength);
+
+    return 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type, const ENetAddress * address)
+{
+    ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+    int nonBlocking = 1,
+        receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE;
+    struct sockaddr_in sin;
+
+    if (newSocket == ENET_SOCKET_NULL)
+      return ENET_SOCKET_NULL;
+
+    if (type == ENET_SOCKET_TYPE_DATAGRAM)
+    {
+        ioctlsocket (newSocket, FIONBIO, & nonBlocking);
+
+        setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int));
+    }
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    
+    if (address != NULL)
+    {
+       sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+       sin.sin_addr.s_addr = address -> host;
+    }
+    else
+    {
+       sin.sin_port = 0;
+       sin.sin_addr.s_addr = INADDR_ANY;
+    }
+
+    if (bind (newSocket,    
+              (struct sockaddr *) & sin,
+              sizeof (struct sockaddr_in)) == SOCKET_ERROR ||
+        (type == ENET_SOCKET_TYPE_STREAM &&
+          address != NULL &&
+          listen (newSocket, SOMAXCONN) == SOCKET_ERROR))
+    {
+       closesocket (newSocket);
+
+       return ENET_SOCKET_NULL;
+    }
+
+    return newSocket;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    int result;
+    struct sockaddr_in sin;
+    int sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+
+    if (result == -1)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    closesocket (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct sockaddr_in sin;
+    DWORD sentLength;
+
+    if (address != NULL)
+    {
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+    }
+
+    if (WSASendTo (socket, 
+                   (LPWSABUF) buffers,
+                   (DWORD) bufferCount,
+                   & sentLength,
+                   0,
+                   address != NULL ? (struct sockaddr *) & sin : 0,
+                   address != NULL ? sizeof (struct sockaddr_in) : 0,
+                   NULL,
+                   NULL) == SOCKET_ERROR)
+    {
+       if (WSAGetLastError () == WSAEWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return (int) sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    DWORD sinLength = sizeof (struct sockaddr_in),
+          flags = 0,
+          recvLength;
+    struct sockaddr_in sin;
+
+    if (WSARecvFrom (socket,
+                     (LPWSABUF) buffers,
+                     (DWORD) bufferCount,
+                     & recvLength,
+                     & flags,
+                     address != NULL ? (struct sockaddr *) & sin : NULL,
+                     address != NULL ? & sinLength : NULL,
+                     NULL,
+                     NULL) == SOCKET_ERROR)
+    {
+       switch (WSAGetLastError ())
+       {
+       case WSAEWOULDBLOCK:
+       case WSAECONNRESET:
+          return 0;
+       }
+
+       return -1;
+    }
+
+    if (flags & MSG_PARTIAL)
+      return -1;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return (int) recvLength;
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+    
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+    
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+} 
+
+#endif
+
--- /dev/null
+++ b/Engine/src/engine.c
@@ -1,0 +1,8505 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+/* SUPERBUILD define is in engine.h ... */
+
+#define ENGINE
+
+/* set this to something non-zero to get loadtile() debugging info on stderr. */
+#define BUILD_CACHEDEBUG 0
+
+#include <string.h>
+
+#if !PLATFORM_MACOSX
+#include <malloc.h>
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if (defined USE_OPENGL)
+#include "buildgl.h"
+#endif
+
+#include "pragmas.h"
+
+#include "platform.h"
+
+#include "build.h"
+#include "cache1d.h"
+
+#include "engine.h"
+
+long stereowidth = 23040, stereopixelwidth = 28, ostereopixelwidth = -1;
+volatile long stereomode = 0, visualpage, activepage, whiteband, blackband;
+volatile char oa1, o3c2, ortca, ortcb, overtbits, laststereoint;
+
+#include "display.h"
+
+#define MAXCLIPNUM 512
+#define MAXPERMS 512
+#define MAXTILEFILES 256
+#define MAXYSAVES ((MAXXDIM*MAXSPRITES)>>7)
+#define MAXNODESPERLINE 42   /* Warning: This depends on MAXYSAVES & MAXYDIM! */
+#define MAXWALLSB 2048
+#define MAXCLIPDIST 1024
+
+#ifdef PLATFORM_DOS
+	/* MUST CALL MALLOC THIS WAY TO FORCE CALLS TO KMALLOC! */
+void *kmalloc(size_t size) { return(malloc(size)); }
+void *kkmalloc(size_t size);
+#pragma aux kkmalloc =\
+	"call kmalloc",\
+	parm [eax]\
+
+	/* MUST CALL FREE THIS WAY TO FORCE CALLS TO KFREE! */
+void kfree(void *buffer) { free(buffer); }
+void kkfree(void *buffer);
+#pragma aux kkfree =\
+	"call kfree",\
+	parm [eax]\
+
+#endif
+
+#ifdef SUPERBUILD
+	/* MUST CALL LOADVOXEL THIS WAY BECAUSE WATCOM STINKS! */
+
+/* !!! wtf does this do?! --ryan. */
+static void loadvoxel(long voxindex)
+{
+    voxindex = 0;  /* prevent compiler whining. */
+}
+
+#if ((defined __WATCOMC__) && (defined PLATFORM_DOS))
+void kloadvoxel(long voxindex);
+#pragma aux kloadvoxel =\
+	"call loadvoxel",\
+	parm [eax]\
+
+#else
+#define kloadvoxel(a) (loadvoxel(a))
+#endif
+
+	/* These variables need to be copied into BUILD */
+#define MAXXSIZ 128
+#define MAXYSIZ 128
+#define MAXZSIZ 200
+#define MAXVOXELS 512
+#define MAXVOXMIPS 5
+long voxoff[MAXVOXELS][MAXVOXMIPS], voxlock[MAXVOXELS][MAXVOXMIPS];
+static long ggxinc[MAXXSIZ+1], ggyinc[MAXXSIZ+1];
+static long lowrecip[1024], nytooclose, nytoofar;
+static unsigned long distrecip[16384];
+#endif
+
+/* used to be static. --ryan. */
+char moustat = 0;
+
+long transarea = 0, totalarea = 0, beforedrawrooms = 1;
+
+/* used to be static. --ryan. */
+long oxdimen = -1, oviewingrange = -1, oxyaspect = -1;
+
+/* used to be static. --ryan. */
+long curbrightness = 0;
+
+	/* Textured Map variables */
+static char globalpolytype;
+static short *dotp1[MAXYDIM], *dotp2[MAXYDIM];
+
+static unsigned char tempbuf[MAXWALLS];
+
+long ebpbak, espbak;
+long slopalookup[16384];
+
+/*
+ * !!! used to be static. If we ever put the original setgamemode() back, this
+ * !!! can be made static again.  --ryan.
+ */
+unsigned char permanentlock = 255;
+long artversion, mapversion;
+char *pic = NULL;
+char picsiz[MAXTILES], tilefilenum[MAXTILES];
+long lastageclock;
+long tilefileoffs[MAXTILES];
+
+long artsize = 0, cachesize = 0;
+
+static short radarang[1280], radarang2[MAXXDIM+1];
+static unsigned short sqrtable[4096], shlookup[4096+256];
+char pow2char[8] = {1,2,4,8,16,32,64,-128};
+long pow2long[32] =
+{
+	1L,2L,4L,8L,
+	16L,32L,64L,128L,
+	256L,512L,1024L,2048L,
+	4096L,8192L,16384L,32768L,
+	65536L,131072L,262144L,524288L,
+	1048576L,2097152L,4194304L,8388608L,
+	16777216L,33554432L,67108864L,134217728L,
+	268435456L,536870912L,1073741824L,2147483647L,
+};
+long reciptable[2048], fpuasm;
+
+char kensmessage[128];
+
+
+
+/* rcg02132001 Cygwin support. */
+#if (defined C_IDENTIFIERS_UNDERSCORED)
+#define SYM_sqrtable   "_sqrtable"
+#define SYM_walock     "_walock"
+#define SYM_shlookup   "_shlookup"
+#define SYM_pow2char   "_pow2char"
+#define SYM_gotpic     "_gotpic"
+#define SYM_dmval      "_dmval"
+#else
+#define SYM_sqrtable   "sqrtable"
+#define SYM_walock     "walock"
+#define SYM_shlookup   "shlookup"
+#define SYM_pow2char   "pow2char"
+#define SYM_gotpic     "gotpic"
+#define SYM_dmval      "dmval"
+#endif
+
+char britable[16][64];
+char textfont[1024], smalltextfont[1024];
+
+static long xb1[MAXWALLSB], yb1[MAXWALLSB], xb2[MAXWALLSB], yb2[MAXWALLSB];
+static long rx1[MAXWALLSB], ry1[MAXWALLSB], rx2[MAXWALLSB], ry2[MAXWALLSB];
+static short p2[MAXWALLSB], thesector[MAXWALLSB], thewall[MAXWALLSB];
+
+static short bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
+
+static short smost[MAXYSAVES], smostcnt;
+static short smoststart[MAXWALLSB];
+static char smostwalltype[MAXWALLSB];
+static long smostwall[MAXWALLSB], smostwallcnt = -1L;
+
+static short maskwall[MAXWALLSB], maskwallcnt;
+static long spritesx[MAXSPRITESONSCREEN];
+static long spritesy[MAXSPRITESONSCREEN+1];
+static long spritesz[MAXSPRITESONSCREEN];
+static spritetype *tspriteptr[MAXSPRITESONSCREEN];
+
+short umost[MAXXDIM+1], dmost[MAXXDIM+1];
+static short bakumost[MAXXDIM+1], bakdmost[MAXXDIM+1];
+short uplc[MAXXDIM+1], dplc[MAXXDIM+1];
+static short uwall[MAXXDIM+1], dwall[MAXXDIM+1];
+static long swplc[MAXXDIM+1], lplc[MAXXDIM+1];
+static long swall[MAXXDIM+1], lwall[MAXXDIM+4];
+long xdimen = -1, xdimenrecip, halfxdimen, xdimenscale, xdimscale;
+long wx1, wy1, wx2, wy2, ydimen;
+long viewoffset;
+
+static long rxi[8], ryi[8], rzi[8], rxi2[8], ryi2[8], rzi2[8];
+static long xsi[8], ysi[8];
+
+/* used to be static. --ryan. */
+long *horizlookup=0, *horizlookup2=0, horizycent;
+
+long globalposx, globalposy, globalposz, globalhoriz;
+short globalang, globalcursectnum;
+long globalpal, cosglobalang, singlobalang;
+long cosviewingrangeglobalang, sinviewingrangeglobalang;
+char *globalpalwritten;
+long globaluclip, globaldclip, globvis = 0;
+long globalvisibility, globalhisibility, globalpisibility, globalcisibility;
+char globparaceilclip, globparaflorclip;
+
+long xyaspect, viewingrangerecip;
+
+long asm1, asm2, asm3, asm4;
+long vplce[4], vince[4], palookupoffse[4], bufplce[4];
+char globalxshift, globalyshift;
+long globalxpanning, globalypanning, globalshade;
+short globalpicnum, globalshiftval;
+long globalzd, globalbufplc, globalyscale, globalorientation;
+long globalx1, globaly1, globalx2, globaly2, globalx3, globaly3, globalzx;
+long globalx, globaly, globalz;
+
+static short sectorborder[256], sectorbordercnt;
+static char tablesloaded = 0;
+long pageoffset, ydim16, qsetmode = 0;
+long startposx, startposy, startposz;
+short startang, startsectnum;
+short pointhighlight, linehighlight, highlightcnt;
+static long lastx[MAXYDIM];
+char *transluc = NULL, paletteloaded = 0;
+
+#define FASTPALGRIDSIZ 8
+static long rdist[129], gdist[129], bdist[129];
+static char colhere[((FASTPALGRIDSIZ+2)*(FASTPALGRIDSIZ+2)*(FASTPALGRIDSIZ+2))>>3];
+static char colhead[(FASTPALGRIDSIZ+2)*(FASTPALGRIDSIZ+2)*(FASTPALGRIDSIZ+2)];
+static long colnext[256];
+static char coldist[8] = {0,1,2,3,4,3,2,1};
+static long colscan[27];
+
+static short clipnum, hitwalls[4];
+long hitscangoalx = (1<<29)-1, hitscangoaly = (1<<29)-1;
+
+typedef struct { long x1, y1, x2, y2; } linetype;
+static linetype clipit[MAXCLIPNUM];
+static short clipsectorlist[MAXCLIPNUM], clipsectnum;
+static short clipobjectval[MAXCLIPNUM];
+
+typedef struct
+{
+	long sx, sy, z;
+	short a, picnum;
+	signed char dashade;
+	unsigned char dapalnum, dastat, pagesleft;
+	long cx1, cy1, cx2, cy2;
+} permfifotype;
+static permfifotype permfifo[MAXPERMS];
+static long permhead = 0, permtail = 0;
+
+short numscans, numhits, numbunches;
+
+short editstatus = 0;
+short searchit;
+long searchx = -1, searchy;                     /* search input  */
+short searchsector, searchwall, searchstat;     /* search output */
+
+static char artfilename[20];
+static long numtilefiles, artfil = -1, artfilnum, artfilplc;
+
+static char inpreparemirror = 0;
+static long mirrorsx1, mirrorsy1, mirrorsx2, mirrorsy2;
+
+long totalclocklock;
+
+unsigned short mapCRC;
+
+#include "a.h"
+
+static __inline long nsqrtasm(unsigned long param)
+{
+    unsigned short *shlookup_a = (unsigned short*)shlookup;
+    unsigned short *sqrtable_a = (unsigned short*)sqrtable;
+    unsigned short cx;
+
+    if (param & 0xff000000)
+	    cx = shlookup_a[(param>>24)+4096];
+    else
+	    cx = shlookup_a[param>>12];
+
+    param = param >> (cx&0xff);
+    param = ((param&0xffff0000)|sqrtable_a[param]);
+    param = param >> ((cx&0xff00)>>8);
+
+    return param;
+}
+
+static __inline long krecipasm(long i)
+{ // Ken did this
+	float f = (float)i; i = *(long *)&f;
+	return((reciptable[(i>>12)&2047]>>(((i-0x3f800000)>>23)&31))^(i>>31));
+}
+
+
+static __inline void setgotpic(long tilenume)
+{
+	if (walock[tilenume] < 200) walock[tilenume] = 199;
+	gotpic[tilenume>>3] |= pow2char[tilenume&7];
+}
+
+
+static __inline long getclipmask(long a, long b, long c, long d)
+{ // Ken did this
+	d = ((a<0)*8) + ((b<0)*4) + ((c<0)*2) + (d<0);
+	return(((d<<4)^0xf0)|d);
+}
+
+//
+// krecip
+//
+long krecip(long num)
+{
+	return(krecipasm(num));
+}
+
+unsigned short _swap16(unsigned short D)
+{
+	return((D<<8)|(D>>8));
+}
+
+unsigned int _swap32(unsigned int D)
+{
+	return((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24));
+}
+
+
+static void scansector (short sectnum)
+{
+	walltype *wal, *wal2;
+	spritetype *spr;
+	long xs, ys, x1, y1, x2, y2, xp1, yp1, xp2=0, yp2=0, templong;
+	short z, zz, startwall, endwall, numscansbefore, scanfirst, bunchfrst;
+	short nextsectnum;
+
+	if (sectnum < 0) return;
+
+	if (automapping) show2dsector[sectnum>>3] |= pow2char[sectnum&7];
+
+	sectorborder[0] = sectnum, sectorbordercnt = 1;
+	do
+	{
+		sectnum = sectorborder[--sectorbordercnt];
+
+		for(z=headspritesect[sectnum];z>=0;z=nextspritesect[z])
+		{
+			spr = &sprite[z];
+			if ((((spr->cstat&0x8000) == 0) || (showinvisibility)) &&
+				  (spr->xrepeat > 0) && (spr->yrepeat > 0) &&
+				  (spritesortcnt < MAXSPRITESONSCREEN))
+			{
+				xs = spr->x-globalposx; ys = spr->y-globalposy;
+				if ((spr->cstat&48) || (xs*cosglobalang+ys*singlobalang > 0))
+				{
+					copybufbyte(spr,&tsprite[spritesortcnt],sizeof(spritetype));
+					tsprite[spritesortcnt++].owner = z;
+				}
+			}
+		}
+
+		gotsector[sectnum>>3] |= pow2char[sectnum&7];
+
+		bunchfrst = numbunches;
+		numscansbefore = numscans;
+
+		startwall = sector[sectnum].wallptr;
+		endwall = startwall + sector[sectnum].wallnum;
+		scanfirst = numscans;
+		for(z=startwall,wal=&wall[z];z<endwall;z++,wal++)
+		{
+			nextsectnum = wal->nextsector;
+
+			wal2 = &wall[wal->point2];
+			x1 = wal->x-globalposx; y1 = wal->y-globalposy;
+			x2 = wal2->x-globalposx; y2 = wal2->y-globalposy;
+
+			if ((nextsectnum >= 0) && ((wal->cstat&32) == 0))
+				if ((gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0)
+				{
+					templong = x1*y2-x2*y1;
+					if (((unsigned)templong+262144) < 524288)
+						if (mulscale5(templong,templong) <= (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
+							sectorborder[sectorbordercnt++] = nextsectnum;
+				}
+
+			if ((z == startwall) || (wall[z-1].point2 != z))
+			{
+				xp1 = dmulscale6(y1,cosglobalang,-x1,singlobalang);
+				yp1 = dmulscale6(x1,cosviewingrangeglobalang,y1,sinviewingrangeglobalang);
+			}
+			else
+			{
+				xp1 = xp2;
+				yp1 = yp2;
+			}
+			xp2 = dmulscale6(y2,cosglobalang,-x2,singlobalang);
+			yp2 = dmulscale6(x2,cosviewingrangeglobalang,y2,sinviewingrangeglobalang);
+			if ((yp1 < 256) && (yp2 < 256)) goto skipitaddwall;
+
+				/* If wall's NOT facing you */
+			if (dmulscale32(xp1,yp2,-xp2,yp1) >= 0) goto skipitaddwall;
+
+			if (xp1 >= -yp1)
+			{
+				if ((xp1 > yp1) || (yp1 == 0)) goto skipitaddwall;
+				xb1[numscans] = halfxdimen + scale(xp1,halfxdimen,yp1);
+				if (xp1 >= 0) xb1[numscans]++;   /* Fix for SIGNED divide */
+				if (xb1[numscans] >= xdimen) xb1[numscans] = xdimen-1;
+				yb1[numscans] = yp1;
+			}
+			else
+			{
+				if (xp2 < -yp2) goto skipitaddwall;
+				xb1[numscans] = 0;
+				templong = yp1-yp2+xp1-xp2;
+				if (templong == 0) goto skipitaddwall;
+				yb1[numscans] = yp1 + scale(yp2-yp1,xp1+yp1,templong);
+			}
+			if (yb1[numscans] < 256) goto skipitaddwall;
+
+			if (xp2 <= yp2)
+			{
+				if ((xp2 < -yp2) || (yp2 == 0)) goto skipitaddwall;
+				xb2[numscans] = halfxdimen + scale(xp2,halfxdimen,yp2) - 1;
+				if (xp2 >= 0) xb2[numscans]++;   /* Fix for SIGNED divide */
+				if (xb2[numscans] >= xdimen) xb2[numscans] = xdimen-1;
+				yb2[numscans] = yp2;
+			}
+			else
+			{
+				if (xp1 > yp1) goto skipitaddwall;
+				xb2[numscans] = xdimen-1;
+				templong = xp2-xp1+yp1-yp2;
+				if (templong == 0) goto skipitaddwall;
+				yb2[numscans] = yp1 + scale(yp2-yp1,yp1-xp1,templong);
+			}
+			if ((yb2[numscans] < 256) || (xb1[numscans] > xb2[numscans])) goto skipitaddwall;
+
+				/* Made it all the way! */
+			thesector[numscans] = sectnum; thewall[numscans] = z;
+			rx1[numscans] = xp1; ry1[numscans] = yp1;
+			rx2[numscans] = xp2; ry2[numscans] = yp2;
+			p2[numscans] = numscans+1;
+			numscans++;
+skipitaddwall:
+
+			if ((wall[z].point2 < z) && (scanfirst < numscans))
+				p2[numscans-1] = scanfirst, scanfirst = numscans;
+		}
+
+		for(z=numscansbefore;z<numscans;z++)
+			if ((wall[thewall[z]].point2 != thewall[p2[z]]) || (xb2[z] >= xb1[p2[z]]))
+				bunchfirst[numbunches++] = p2[z], p2[z] = -1;
+
+		for(z=bunchfrst;z<numbunches;z++)
+		{
+			for(zz=bunchfirst[z];p2[zz]>=0;zz=p2[zz]);
+			bunchlast[z] = zz;
+		}
+	} while (sectorbordercnt > 0);
+}
+
+
+static void prepwall(long z, walltype *wal)
+{
+	long i, l=0, ol=0, splc, sinc, x, topinc, top, botinc, bot, walxrepeat;
+
+	walxrepeat = (wal->xrepeat<<3);
+
+		/* lwall calculation */
+	i = xb1[z]-halfxdimen;
+	topinc = -(ry1[z]>>2);
+	botinc = ((ry2[z]-ry1[z])>>8);
+	top = mulscale5(rx1[z],xdimen)+mulscale2(topinc,i);
+	bot = mulscale11(rx1[z]-rx2[z],xdimen)+mulscale2(botinc,i);
+
+	splc = mulscale19(ry1[z],xdimscale);
+	sinc = mulscale16(ry2[z]-ry1[z],xdimscale);
+
+	x = xb1[z];
+	if (bot != 0)
+	{
+		l = divscale12(top,bot);
+		swall[x] = mulscale21(l,sinc)+splc;
+		l *= walxrepeat;
+		lwall[x] = (l>>18);
+	}
+	while (x+4 <= xb2[z])
+	{
+		top += topinc; bot += botinc;
+		if (bot != 0)
+		{
+			ol = l; l = divscale12(top,bot);
+			swall[x+4] = mulscale21(l,sinc)+splc;
+			l *= walxrepeat;
+			lwall[x+4] = (l>>18);
+		}
+		i = ((ol+l)>>1);
+		lwall[x+2] = (i>>18);
+		lwall[x+1] = ((ol+i)>>19);
+		lwall[x+3] = ((l+i)>>19);
+		swall[x+2] = ((swall[x]+swall[x+4])>>1);
+		swall[x+1] = ((swall[x]+swall[x+2])>>1);
+		swall[x+3] = ((swall[x+4]+swall[x+2])>>1);
+		x += 4;
+	}
+	if (x+2 <= xb2[z])
+	{
+		top += (topinc>>1); bot += (botinc>>1);
+		if (bot != 0)
+		{
+			ol = l; l = divscale12(top,bot);
+			swall[x+2] = mulscale21(l,sinc)+splc;
+			l *= walxrepeat;
+			lwall[x+2] = (l>>18);
+		}
+		lwall[x+1] = ((l+ol)>>19);
+		swall[x+1] = ((swall[x]+swall[x+2])>>1);
+		x += 2;
+	}
+	if (x+1 <= xb2[z])
+	{
+		bot += (botinc>>2);
+		if (bot != 0)
+		{
+			l = divscale12(top+(topinc>>2),bot);
+			swall[x+1] = mulscale21(l,sinc)+splc;
+			lwall[x+1] = mulscale18(l,walxrepeat);
+		}
+	}
+
+	if (lwall[xb1[z]] < 0) lwall[xb1[z]] = 0;
+	if ((lwall[xb2[z]] >= walxrepeat) && (walxrepeat)) lwall[xb2[z]] = walxrepeat-1;
+	if (wal->cstat&8)
+	{
+		walxrepeat--;
+		for(x=xb1[z];x<=xb2[z];x++) lwall[x] = walxrepeat-lwall[x];
+	}
+}
+
+
+static int getpalookup(long davis, long dashade)
+{
+	return(min(max(dashade+(davis>>8),0),numpalookups-1));
+}
+
+
+static void hline (long xr, long yp)
+{
+	long xl, r, s;
+
+	xl = lastx[yp]; if (xl > xr) return;
+	r = horizlookup2[yp-globalhoriz+horizycent];
+	asm1 = globalx1*r;
+	asm2 = globaly2*r;
+	s = ((long)getpalookup((long)mulscale16(r,globvis),globalshade)<<8);
+
+	hlineasm4(xr-xl,0L,s,globalx2*r+globalypanning,globaly1*r+globalxpanning,
+		ylookup[yp]+xr+frameoffset);
+}
+
+
+static void slowhline (long xr, long yp)
+{
+	long xl, r;
+
+	xl = lastx[yp]; if (xl > xr) return;
+	r = horizlookup2[yp-globalhoriz+horizycent];
+	asm1 = globalx1*r;
+	asm2 = globaly2*r;
+
+	asm3 = (long)globalpalwritten + ((long)getpalookup((long)mulscale16(r,globvis),globalshade)<<8);
+	if (!(globalorientation&256))
+	{
+		mhline(globalbufplc,globaly1*r+globalxpanning-asm1*(xr-xl),(xr-xl)<<16,0L,
+			globalx2*r+globalypanning-asm2*(xr-xl),ylookup[yp]+xl+frameoffset);
+		return;
+	}
+	thline(globalbufplc,globaly1*r+globalxpanning-asm1*(xr-xl),(xr-xl)<<16,0L,
+		globalx2*r+globalypanning-asm2*(xr-xl),ylookup[yp]+xl+frameoffset);
+	transarea += (xr-xl);
+}
+
+
+static int animateoffs(short tilenum, short fakevar)
+{
+	long i, k, offs;
+
+	offs = 0;
+	i = (totalclocklock>>((picanm[tilenum]>>24)&15));
+	if ((picanm[tilenum]&63) > 0)
+	{
+		switch(picanm[tilenum]&192)
+		{
+			case 64:
+				k = (i%((picanm[tilenum]&63)<<1));
+				if (k < (picanm[tilenum]&63))
+					offs = k;
+				else
+					offs = (((picanm[tilenum]&63)<<1)-k);
+				break;
+			case 128:
+				offs = (i%((picanm[tilenum]&63)+1));
+					break;
+			case 192:
+				offs = -(i%((picanm[tilenum]&63)+1));
+		}
+	}
+	return(offs);
+}
+
+
+/* renders non-parallaxed ceilings. --ryan. */
+static void ceilscan (long x1, long x2, long sectnum)
+{
+	long i, j, ox, oy, x, y1, y2, twall, bwall;
+	sectortype *sec;
+
+	sec = &sector[sectnum];
+	if (palookup[sec->ceilingpal] != globalpalwritten)
+	{
+		globalpalwritten = palookup[sec->ceilingpal];
+		setpalookupaddress(globalpalwritten);
+	}
+
+	globalzd = sec->ceilingz-globalposz;
+	if (globalzd > 0) return;
+	globalpicnum = sec->ceilingpicnum;
+	if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+	setgotpic(globalpicnum);
+	if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) return;
+	if (picanm[globalpicnum]&192) globalpicnum += animateoffs((short)globalpicnum,(short)sectnum);
+
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+	globalbufplc = waloff[globalpicnum];
+
+	globalshade = (long)sec->ceilingshade;
+	globvis = globalcisibility;
+	if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+	globalorientation = (long)sec->ceilingstat;
+
+
+	if ((globalorientation&64) == 0)
+	{
+		globalx1 = singlobalang; globalx2 = singlobalang;
+		globaly1 = cosglobalang; globaly2 = cosglobalang;
+		globalxpanning = (globalposx<<20);
+		globalypanning = -(globalposy<<20);
+	}
+	else
+	{
+		j = sec->wallptr;
+		ox = wall[wall[j].point2].x - wall[j].x;
+		oy = wall[wall[j].point2].y - wall[j].y;
+		i = nsqrtasm(ox*ox+oy*oy); if (i == 0) i = 1024; else i = 1048576/i;
+		globalx1 = mulscale10(dmulscale10(ox,singlobalang,-oy,cosglobalang),i);
+		globaly1 = mulscale10(dmulscale10(ox,cosglobalang,oy,singlobalang),i);
+		globalx2 = -globalx1;
+		globaly2 = -globaly1;
+
+		ox = ((wall[j].x-globalposx)<<6); oy = ((wall[j].y-globalposy)<<6);
+		i = dmulscale14(oy,cosglobalang,-ox,singlobalang);
+		j = dmulscale14(ox,cosglobalang,oy,singlobalang);
+		ox = i; oy = j;
+		globalxpanning = globalx1*ox - globaly1*oy;
+		globalypanning = globaly2*ox + globalx2*oy;
+	}
+	globalx2 = mulscale16(globalx2,viewingrangerecip);
+	globaly1 = mulscale16(globaly1,viewingrangerecip);
+	globalxshift = (8-(picsiz[globalpicnum]&15));
+	globalyshift = (8-(picsiz[globalpicnum]>>4));
+	if (globalorientation&8) { globalxshift++; globalyshift++; }
+
+	if ((globalorientation&0x4) > 0)
+	{
+		i = globalxpanning; globalxpanning = globalypanning; globalypanning = i;
+		i = globalx2; globalx2 = -globaly1; globaly1 = -i;
+		i = globalx1; globalx1 = globaly2; globaly2 = i;
+	}
+	if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalxpanning = -globalxpanning;
+	if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalypanning = -globalypanning;
+	globalx1 <<= globalxshift; globaly1 <<= globalxshift;
+	globalx2 <<= globalyshift;  globaly2 <<= globalyshift;
+	globalxpanning <<= globalxshift; globalypanning <<= globalyshift;
+	globalxpanning += (((long)sec->ceilingxpanning)<<24);
+	globalypanning += (((long)sec->ceilingypanning)<<24);
+	globaly1 = (-globalx1-globaly1)*halfxdimen;
+	globalx2 = (globalx2-globaly2)*halfxdimen;
+
+	sethlinesizes(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4,globalbufplc);
+
+	globalx2 += globaly2*(x1-1);
+	globaly1 += globalx1*(x1-1);
+	globalx1 = mulscale16(globalx1,globalzd);
+	globalx2 = mulscale16(globalx2,globalzd);
+	globaly1 = mulscale16(globaly1,globalzd);
+	globaly2 = mulscale16(globaly2,globalzd);
+	globvis = klabs(mulscale10(globvis,globalzd));
+
+	if (!(globalorientation&0x180))
+	{
+		y1 = umost[x1]; y2 = y1;
+		for(x=x1;x<=x2;x++)
+		{
+			twall = umost[x]-1; bwall = min(uplc[x],dmost[x]);
+			if (twall < bwall-1)
+			{
+				if (twall >= y2)
+				{
+					while (y1 < y2-1) hline(x-1,++y1);
+					y1 = twall;
+				}
+				else
+				{
+					while (y1 < twall) hline(x-1,++y1);
+					while (y1 > twall) lastx[y1--] = x;
+				}
+				while (y2 > bwall) hline(x-1,--y2);
+				while (y2 < bwall) lastx[y2++] = x;
+			}
+			else
+			{
+				while (y1 < y2-1) hline(x-1,++y1);
+				if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
+				y1 = umost[x+1]; y2 = y1;
+			}
+			globalx2 += globaly2; globaly1 += globalx1;
+		}
+		while (y1 < y2-1) hline(x2,++y1);
+		faketimerhandler();
+		return;
+	}
+
+	switch(globalorientation&0x180)
+	{
+		case 128:
+			msethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+		case 256:
+			settransnormal();
+			tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+		case 384:
+			settransreverse();
+			tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+	}
+
+	y1 = umost[x1]; y2 = y1;
+	for(x=x1;x<=x2;x++)
+	{
+		twall = umost[x]-1; bwall = min(uplc[x],dmost[x]);
+		if (twall < bwall-1)
+		{
+			if (twall >= y2)
+			{
+				while (y1 < y2-1) slowhline(x-1,++y1);
+				y1 = twall;
+			}
+			else
+			{
+				while (y1 < twall) slowhline(x-1,++y1);
+				while (y1 > twall) lastx[y1--] = x;
+			}
+			while (y2 > bwall) slowhline(x-1,--y2);
+			while (y2 < bwall) lastx[y2++] = x;
+		}
+		else
+		{
+			while (y1 < y2-1) slowhline(x-1,++y1);
+			if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
+			y1 = umost[x+1]; y2 = y1;
+		}
+		globalx2 += globaly2; globaly1 += globalx1;
+	}
+	while (y1 < y2-1) slowhline(x2,++y1);
+	faketimerhandler();
+}
+
+
+/* renders non-parallaxed floors. --ryan. */
+static void florscan (long x1, long x2, long sectnum)
+{
+	long i, j, ox, oy, x, y1, y2, twall, bwall;
+	sectortype *sec;
+
+	sec = &sector[sectnum];
+	if (palookup[sec->floorpal] != globalpalwritten)
+	{
+		globalpalwritten = palookup[sec->floorpal];
+		setpalookupaddress(globalpalwritten);
+	}
+
+	globalzd = globalposz-sec->floorz;
+	if (globalzd > 0) return;
+	globalpicnum = sec->floorpicnum;
+	if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+	setgotpic(globalpicnum);
+	if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) return;
+	if (picanm[globalpicnum]&192) globalpicnum += animateoffs((short)globalpicnum,(short)sectnum);
+
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+	globalbufplc = waloff[globalpicnum];
+
+	globalshade = (long)sec->floorshade;
+	globvis = globalcisibility;
+	if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+	globalorientation = (long)sec->floorstat;
+
+
+	if ((globalorientation&64) == 0)
+	{
+		globalx1 = singlobalang; globalx2 = singlobalang;
+		globaly1 = cosglobalang; globaly2 = cosglobalang;
+		globalxpanning = (globalposx<<20);
+		globalypanning = -(globalposy<<20);
+	}
+	else
+	{
+		j = sec->wallptr;
+		ox = wall[wall[j].point2].x - wall[j].x;
+		oy = wall[wall[j].point2].y - wall[j].y;
+		i = nsqrtasm(ox*ox+oy*oy); if (i == 0) i = 1024; else i = 1048576/i;
+		globalx1 = mulscale10(dmulscale10(ox,singlobalang,-oy,cosglobalang),i);
+		globaly1 = mulscale10(dmulscale10(ox,cosglobalang,oy,singlobalang),i);
+		globalx2 = -globalx1;
+		globaly2 = -globaly1;
+
+		ox = ((wall[j].x-globalposx)<<6); oy = ((wall[j].y-globalposy)<<6);
+		i = dmulscale14(oy,cosglobalang,-ox,singlobalang);
+		j = dmulscale14(ox,cosglobalang,oy,singlobalang);
+		ox = i; oy = j;
+		globalxpanning = globalx1*ox - globaly1*oy;
+		globalypanning = globaly2*ox + globalx2*oy;
+	}
+	globalx2 = mulscale16(globalx2,viewingrangerecip);
+	globaly1 = mulscale16(globaly1,viewingrangerecip);
+	globalxshift = (8-(picsiz[globalpicnum]&15));
+	globalyshift = (8-(picsiz[globalpicnum]>>4));
+	if (globalorientation&8) { globalxshift++; globalyshift++; }
+
+	if ((globalorientation&0x4) > 0)
+	{
+		i = globalxpanning; globalxpanning = globalypanning; globalypanning = i;
+		i = globalx2; globalx2 = -globaly1; globaly1 = -i;
+		i = globalx1; globalx1 = globaly2; globaly2 = i;
+	}
+	if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalxpanning = -globalxpanning;
+	if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalypanning = -globalypanning;
+	globalx1 <<= globalxshift; globaly1 <<= globalxshift;
+	globalx2 <<= globalyshift;  globaly2 <<= globalyshift;
+	globalxpanning <<= globalxshift; globalypanning <<= globalyshift;
+	globalxpanning += (((long)sec->floorxpanning)<<24);
+	globalypanning += (((long)sec->floorypanning)<<24);
+	globaly1 = (-globalx1-globaly1)*halfxdimen;
+	globalx2 = (globalx2-globaly2)*halfxdimen;
+
+	sethlinesizes(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4,globalbufplc);
+
+	globalx2 += globaly2*(x1-1);
+	globaly1 += globalx1*(x1-1);
+	globalx1 = mulscale16(globalx1,globalzd);
+	globalx2 = mulscale16(globalx2,globalzd);
+	globaly1 = mulscale16(globaly1,globalzd);
+	globaly2 = mulscale16(globaly2,globalzd);
+	globvis = klabs(mulscale10(globvis,globalzd));
+
+	if (!(globalorientation&0x180))
+	{
+		y1 = max(dplc[x1],umost[x1]); y2 = y1;
+		for(x=x1;x<=x2;x++)
+		{
+			twall = max(dplc[x],umost[x])-1; bwall = dmost[x];
+			if (twall < bwall-1)
+			{
+				if (twall >= y2)
+				{
+					while (y1 < y2-1) hline(x-1,++y1);
+					y1 = twall;
+				}
+				else
+				{
+					while (y1 < twall) hline(x-1,++y1);
+					while (y1 > twall) lastx[y1--] = x;
+				}
+				while (y2 > bwall) hline(x-1,--y2);
+				while (y2 < bwall) lastx[y2++] = x;
+			}
+			else
+			{
+				while (y1 < y2-1) hline(x-1,++y1);
+				if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
+				y1 = max(dplc[x+1],umost[x+1]); y2 = y1;
+			}
+			globalx2 += globaly2; globaly1 += globalx1;
+		}
+		while (y1 < y2-1) hline(x2,++y1);
+		faketimerhandler();
+		return;
+	}
+
+	switch(globalorientation&0x180)
+	{
+		case 128:
+			msethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+		case 256:
+			settransnormal();
+			tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+		case 384:
+			settransreverse();
+			tsethlineshift(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4);
+			break;
+	}
+
+	y1 = max(dplc[x1],umost[x1]); y2 = y1;
+	for(x=x1;x<=x2;x++)
+	{
+		twall = max(dplc[x],umost[x])-1; bwall = dmost[x];
+		if (twall < bwall-1)
+		{
+			if (twall >= y2)
+			{
+				while (y1 < y2-1) slowhline(x-1,++y1);
+				y1 = twall;
+			}
+			else
+			{
+				while (y1 < twall) slowhline(x-1,++y1);
+				while (y1 > twall) lastx[y1--] = x;
+			}
+			while (y2 > bwall) slowhline(x-1,--y2);
+			while (y2 < bwall) lastx[y2++] = x;
+		}
+		else
+		{
+			while (y1 < y2-1) slowhline(x-1,++y1);
+			if (x == x2) { globalx2 += globaly2; globaly1 += globalx1; break; }
+			y1 = max(dplc[x+1],umost[x+1]); y2 = y1;
+		}
+		globalx2 += globaly2; globaly1 += globalx1;
+	}
+	while (y1 < y2-1) slowhline(x2,++y1);
+	faketimerhandler();
+}
+
+
+/*
+ * renders walls and parallaxed skies/floors. Look at parascan() for the
+ *  higher level of parallaxing.
+ *
+ *    x1 == offset of leftmost pixel of wall. 0 is left of surface.
+ *    x2 == offset of rightmost pixel of wall. 0 is left of surface.
+ *
+ *  apparently, walls are always vertical; there are sloping functions
+ *   (!!!) er...elsewhere. Only the sides need be vertical, as the top and
+ *   bottom of the polygon will need to be angled as the camera perspective
+ *   shifts (user spins in a circle, etc.)
+ *
+ *  uwal is an array of the upper most pixels, and dwal are the lower most.
+ *   This must be a list, as the top and bottom of the polygon are not
+ *   necessarily horizontal lines.
+ *
+ *   So, the screen coordinate of the top left of a wall is specified by
+ *   uwal[x1], the bottom left by dwal[x1], the top right by uwal[x2], and
+ *   the bottom right by dwal[x2]. Every physical point on the edge of the
+ *   wall in between is specified by traversing those arrays, one pixel per
+ *   element.
+ *
+ *  --ryan.
+ */
+static void wallscan(long x1, long x2,
+                     short *uwal, short *dwal,
+                     long *swal, long *lwal)
+{
+	long i, x, xnice, ynice, fpalookup;
+	long y1ve[4], y2ve[4], u4, d4, z, tsizx, tsizy;
+	char bad;
+
+	tsizx = tilesizx[globalpicnum];
+	tsizy = tilesizy[globalpicnum];
+	setgotpic(globalpicnum);
+	if ((tsizx <= 0) || (tsizy <= 0)) return;
+	if ((uwal[x1] > ydimen) && (uwal[x2] > ydimen)) return;
+	if ((dwal[x1] < 0) && (dwal[x2] < 0)) return;
+
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+
+	xnice = (pow2long[picsiz[globalpicnum]&15] == tsizx);
+	if (xnice) tsizx--;
+	ynice = (pow2long[picsiz[globalpicnum]>>4] == tsizy);
+	if (ynice) tsizy = (picsiz[globalpicnum]>>4);
+
+	fpalookup = (long)FP_OFF(palookup[globalpal]);
+
+	setupvlineasm(globalshiftval);
+
+	x = x1;
+	while ((umost[x] > dmost[x]) && (x <= x2)) x++;
+
+	for(;(x<=x2)&&((x+frameoffset)&3);x++)
+	{
+		y1ve[0] = max(uwal[x],umost[x]);
+		y2ve[0] = min(dwal[x],dmost[x]);
+		if (y2ve[0] <= y1ve[0]) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+
+		bufplce[0] = lwal[x] + globalxpanning;
+		if (bufplce[0] >= tsizx) { if (xnice == 0) bufplce[0] %= tsizx; else bufplce[0] &= tsizx; }
+		if (ynice == 0) bufplce[0] *= tsizy; else bufplce[0] <<= tsizy;
+
+		vince[0] = swal[x]*globalyscale;
+		vplce[0] = globalzd + vince[0]*(y1ve[0]-globalhoriz+1);
+
+		vlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0]+waloff[globalpicnum],x+frameoffset+ylookup[y1ve[0]]);
+	}
+	for(;x<=x2-3;x+=4)
+	{
+		bad = 0;
+		for(z=3;z>=0;z--)
+		{
+			y1ve[z] = max(uwal[x+z],umost[x+z]);
+			y2ve[z] = min(dwal[x+z],dmost[x+z])-1;
+			if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
+
+			i = lwal[x+z] + globalxpanning;
+			if (i >= tsizx) { if (xnice == 0) i %= tsizx; else i &= tsizx; }
+			if (ynice == 0) i *= tsizy; else i <<= tsizy;
+			bufplce[z] = waloff[globalpicnum]+i;
+
+			vince[z] = swal[x+z]*globalyscale;
+			vplce[z] = globalzd + vince[z]*(y1ve[z]-globalhoriz+1);
+		}
+		if (bad == 15) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+		palookupoffse[3] = fpalookup+(getpalookup((long)mulscale16(swal[x+3],globvis),globalshade)<<8);
+
+		if ((palookupoffse[0] == palookupoffse[3]) && ((bad&0x9) == 0))
+		{
+			palookupoffse[1] = palookupoffse[0];
+			palookupoffse[2] = palookupoffse[0];
+		}
+		else
+		{
+			palookupoffse[1] = fpalookup+(getpalookup((long)mulscale16(swal[x+1],globvis),globalshade)<<8);
+			palookupoffse[2] = fpalookup+(getpalookup((long)mulscale16(swal[x+2],globvis),globalshade)<<8);
+		}
+
+		u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
+		d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
+
+		if ((bad != 0) || (u4 >= d4))
+		{
+			if (!(bad&1)) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+x+frameoffset+0);
+			if (!(bad&2)) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+x+frameoffset+1);
+			if (!(bad&4)) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+x+frameoffset+2);
+			if (!(bad&8)) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+x+frameoffset+3);
+			continue;
+		}
+
+		if (u4 > y1ve[0]) vplce[0] = prevlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+x+frameoffset+0);
+		if (u4 > y1ve[1]) vplce[1] = prevlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+x+frameoffset+1);
+		if (u4 > y1ve[2]) vplce[2] = prevlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+x+frameoffset+2);
+		if (u4 > y1ve[3]) vplce[3] = prevlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+x+frameoffset+3);
+
+		if (d4 >= u4) vlineasm4(d4-u4+1,ylookup[u4]+x+frameoffset);
+
+		i = x+frameoffset+ylookup[d4+1];
+		if (y2ve[0] > d4) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
+		if (y2ve[1] > d4) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
+		if (y2ve[2] > d4) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
+		if (y2ve[3] > d4) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
+	}
+	for(;x<=x2;x++)
+	{
+		y1ve[0] = max(uwal[x],umost[x]);
+		y2ve[0] = min(dwal[x],dmost[x]);
+		if (y2ve[0] <= y1ve[0]) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+
+		bufplce[0] = lwal[x] + globalxpanning;
+		if (bufplce[0] >= tsizx) { if (xnice == 0) bufplce[0] %= tsizx; else bufplce[0] &= tsizx; }
+		if (ynice == 0) bufplce[0] *= tsizy; else bufplce[0] <<= tsizy;
+
+		vince[0] = swal[x]*globalyscale;
+		vplce[0] = globalzd + vince[0]*(y1ve[0]-globalhoriz+1);
+
+		vlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0]+waloff[globalpicnum],x+frameoffset+ylookup[y1ve[0]]);
+	}
+	faketimerhandler();
+}
+
+
+/* this renders masking sprites. See wallscan(). --ryan. */
+static void maskwallscan(long x1, long x2,
+                         short *uwal, short *dwal,
+                         long *swal, long *lwal)
+{
+	long i, x, startx, xnice, ynice, fpalookup;
+	long y1ve[4], y2ve[4], u4, d4, dax, z, p, tsizx, tsizy;
+	char bad;
+
+	tsizx = tilesizx[globalpicnum];
+	tsizy = tilesizy[globalpicnum];
+	setgotpic(globalpicnum);
+	if ((tsizx <= 0) || (tsizy <= 0)) return;
+	if ((uwal[x1] > ydimen) && (uwal[x2] > ydimen)) return;
+	if ((dwal[x1] < 0) && (dwal[x2] < 0)) return;
+
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+
+	startx = x1;
+
+	xnice = (pow2long[picsiz[globalpicnum]&15] == tsizx);
+	if (xnice) tsizx = (tsizx-1);
+	ynice = (pow2long[picsiz[globalpicnum]>>4] == tsizy);
+	if (ynice) tsizy = (picsiz[globalpicnum]>>4);
+
+	fpalookup = (long)FP_OFF(palookup[globalpal]);
+
+	setupmvlineasm(globalshiftval);
+
+	x = startx;
+	while ((startumost[x+windowx1] > startdmost[x+windowx1]) && (x <= x2)) x++;
+
+	p = x+frameoffset;
+
+	for(;(x<=x2)&&(p&3);x++,p++)
+	{
+		y1ve[0] = max(uwal[x],startumost[x+windowx1]-windowy1);
+		y2ve[0] = min(dwal[x],startdmost[x+windowx1]-windowy1);
+		if (y2ve[0] <= y1ve[0]) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+
+		bufplce[0] = lwal[x] + globalxpanning;
+		if (bufplce[0] >= tsizx) { if (xnice == 0) bufplce[0] %= tsizx; else bufplce[0] &= tsizx; }
+		if (ynice == 0) bufplce[0] *= tsizy; else bufplce[0] <<= tsizy;
+
+		vince[0] = swal[x]*globalyscale;
+		vplce[0] = globalzd + vince[0]*(y1ve[0]-globalhoriz+1);
+
+		mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0]+waloff[globalpicnum],p+ylookup[y1ve[0]]);
+	}
+	for(;x<=x2-3;x+=4,p+=4)
+	{
+		bad = 0;
+		for(z=3,dax=x+3;z>=0;z--,dax--)
+		{
+			y1ve[z] = max(uwal[dax],startumost[dax+windowx1]-windowy1);
+			y2ve[z] = min(dwal[dax],startdmost[dax+windowx1]-windowy1)-1;
+			if (y2ve[z] < y1ve[z]) { bad += pow2char[z]; continue; }
+
+			i = lwal[dax] + globalxpanning;
+			if (i >= tsizx) { if (xnice == 0) i %= tsizx; else i &= tsizx; }
+			if (ynice == 0) i *= tsizy; else i <<= tsizy;
+			bufplce[z] = waloff[globalpicnum]+i;
+
+			vince[z] = swal[dax]*globalyscale;
+			vplce[z] = globalzd + vince[z]*(y1ve[z]-globalhoriz+1);
+		}
+		if (bad == 15) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+		palookupoffse[3] = fpalookup+(getpalookup((long)mulscale16(swal[x+3],globvis),globalshade)<<8);
+
+		if ((palookupoffse[0] == palookupoffse[3]) && ((bad&0x9) == 0))
+		{
+			palookupoffse[1] = palookupoffse[0];
+			palookupoffse[2] = palookupoffse[0];
+		}
+		else
+		{
+			palookupoffse[1] = fpalookup+(getpalookup((long)mulscale16(swal[x+1],globvis),globalshade)<<8);
+			palookupoffse[2] = fpalookup+(getpalookup((long)mulscale16(swal[x+2],globvis),globalshade)<<8);
+		}
+
+		u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
+		d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
+
+		if ((bad > 0) || (u4 >= d4))
+		{
+			if (!(bad&1)) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+			if (!(bad&2)) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+			if (!(bad&4)) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+			if (!(bad&8)) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+			continue;
+		}
+
+		if (u4 > y1ve[0]) vplce[0] = mvlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+		if (u4 > y1ve[1]) vplce[1] = mvlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+		if (u4 > y1ve[2]) vplce[2] = mvlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+		if (u4 > y1ve[3]) vplce[3] = mvlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+
+		if (d4 >= u4) mvlineasm4(d4-u4+1,ylookup[u4]+p);
+
+		i = p+ylookup[d4+1];
+		if (y2ve[0] > d4) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
+		if (y2ve[1] > d4) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
+		if (y2ve[2] > d4) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
+		if (y2ve[3] > d4) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
+	}
+	for(;x<=x2;x++,p++)
+	{
+		y1ve[0] = max(uwal[x],startumost[x+windowx1]-windowy1);
+		y2ve[0] = min(dwal[x],startdmost[x+windowx1]-windowy1);
+		if (y2ve[0] <= y1ve[0]) continue;
+
+		palookupoffse[0] = fpalookup+(getpalookup((long)mulscale16(swal[x],globvis),globalshade)<<8);
+
+		bufplce[0] = lwal[x] + globalxpanning;
+		if (bufplce[0] >= tsizx) { if (xnice == 0) bufplce[0] %= tsizx; else bufplce[0] &= tsizx; }
+		if (ynice == 0) bufplce[0] *= tsizy; else bufplce[0] <<= tsizy;
+
+		vince[0] = swal[x]*globalyscale;
+		vplce[0] = globalzd + vince[0]*(y1ve[0]-globalhoriz+1);
+
+		mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0]-1,vplce[0],bufplce[0]+waloff[globalpicnum],p+ylookup[y1ve[0]]);
+	}
+	faketimerhandler();
+}
+
+/* renders parallaxed skies/floors  --ryan. */
+static void parascan(long dax1, long dax2, long sectnum,
+                     char dastat, long bunch)
+{
+	sectortype *sec;
+	long j, k, l, m, n, x, z, wallnum, nextsectnum, globalhorizbak;
+	short *topptr, *botptr;
+
+	sectnum = thesector[bunchfirst[bunch]]; sec = &sector[sectnum];
+
+	globalhorizbak = globalhoriz;
+	if (parallaxyscale != 65536)
+		globalhoriz = mulscale16(globalhoriz-(ydimen>>1),parallaxyscale) + (ydimen>>1);
+	globvis = globalpisibility;
+	/* globalorientation = 0L; */
+	if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+
+	if (dastat == 0)
+	{
+		globalpal = sec->ceilingpal;
+		globalpicnum = sec->ceilingpicnum;
+		globalshade = (long)sec->ceilingshade;
+		globalxpanning = (long)sec->ceilingxpanning;
+		globalypanning = (long)sec->ceilingypanning;
+		topptr = umost;
+		botptr = uplc;
+	}
+	else
+	{
+		globalpal = sec->floorpal;
+		globalpicnum = sec->floorpicnum;
+		globalshade = (long)sec->floorshade;
+		globalxpanning = (long)sec->floorxpanning;
+		globalypanning = (long)sec->floorypanning;
+		topptr = dplc;
+		botptr = dmost;
+	}
+
+	if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+	if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)sectnum);
+	globalshiftval = (picsiz[globalpicnum]>>4);
+	if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+	globalshiftval = 32-globalshiftval;
+	globalzd = (((tilesizy[globalpicnum]>>1)+parallaxyoffs)<<globalshiftval)+(globalypanning<<24);
+	globalyscale = (8<<(globalshiftval-19));
+	/*if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;*/
+
+	k = 11 - (picsiz[globalpicnum]&15) - pskybits;
+	x = -1;
+
+	for(z=bunchfirst[bunch];z>=0;z=p2[z])
+	{
+		wallnum = thewall[z]; nextsectnum = wall[wallnum].nextsector;
+
+		if (dastat == 0) j = sector[nextsectnum].ceilingstat;
+						else j = sector[nextsectnum].floorstat;
+
+		if ((nextsectnum < 0) || (wall[wallnum].cstat&32) || ((j&1) == 0))
+		{
+			if (x == -1) x = xb1[z];
+
+			if (parallaxtype == 0)
+			{
+				n = mulscale16(xdimenrecip,viewingrange);
+				for(j=xb1[z];j<=xb2[z];j++)
+					lplc[j] = (((mulscale23(j-halfxdimen,n)+globalang)&2047)>>k);
+			}
+			else
+			{
+				for(j=xb1[z];j<=xb2[z];j++)
+					lplc[j] = ((((long)radarang2[j]+globalang)&2047)>>k);
+			}
+			if (parallaxtype == 2)
+			{
+				n = mulscale16(xdimscale,viewingrange);
+				for(j=xb1[z];j<=xb2[z];j++)
+					swplc[j] = mulscale14(sintable[((long)radarang2[j]+512)&2047],n);
+			}
+			else
+				clearbuf(&swplc[xb1[z]],xb2[z]-xb1[z]+1,mulscale16(xdimscale,viewingrange));
+		}
+		else if (x >= 0)
+		{
+			l = globalpicnum; m = (picsiz[globalpicnum]&15);
+			globalpicnum = l+pskyoff[lplc[x]>>m];
+
+			if (((lplc[x]^lplc[xb1[z]-1])>>m) == 0)
+				wallscan(x,xb1[z]-1,topptr,botptr,swplc,lplc);
+			else
+			{
+				j = x;
+				while (x < xb1[z])
+				{
+					n = l+pskyoff[lplc[x]>>m];
+					if (n != globalpicnum)
+					{
+						wallscan(j,x-1,topptr,botptr,swplc,lplc);
+						j = x;
+						globalpicnum = n;
+					}
+					x++;
+				}
+				if (j < x)
+					wallscan(j,x-1,topptr,botptr,swplc,lplc);
+			}
+
+			globalpicnum = l;
+			x = -1;
+		}
+	}
+
+	if (x >= 0)
+	{
+		l = globalpicnum; m = (picsiz[globalpicnum]&15);
+		globalpicnum = l+pskyoff[lplc[x]>>m];
+
+		if (((lplc[x]^lplc[xb2[bunchlast[bunch]]])>>m) == 0)
+			wallscan(x,xb2[bunchlast[bunch]],topptr,botptr,swplc,lplc);
+		else
+		{
+			j = x;
+			while (x <= xb2[bunchlast[bunch]])
+			{
+				n = l+pskyoff[lplc[x]>>m];
+				if (n != globalpicnum)
+				{
+					wallscan(j,x-1,topptr,botptr,swplc,lplc);
+					j = x;
+					globalpicnum = n;
+				}
+				x++;
+			}
+			if (j <= x)
+				wallscan(j,x,topptr,botptr,swplc,lplc);
+		}
+		globalpicnum = l;
+	}
+	globalhoriz = globalhorizbak;
+}
+
+
+#define BITSOFPRECISION 3  /* Don't forget to change this in A.ASM also! */
+static void grouscan (long dax1, long dax2, long sectnum, char dastat)
+{
+	long i, j, l, x, y, dx, dy, wx, wy, y1, y2, daz;
+	long daslope, dasqr;
+	long shoffs, shinc, m1, m2, *mptr1, *mptr2, *nptr1, *nptr2;
+	walltype *wal;
+	sectortype *sec;
+
+	sec = &sector[sectnum];
+
+	if (dastat == 0)
+	{
+		if (globalposz <= getceilzofslope((short) sectnum,globalposx,globalposy))
+			return;  /* Back-face culling */
+		globalorientation = sec->ceilingstat;
+		globalpicnum = sec->ceilingpicnum;
+		globalshade = sec->ceilingshade;
+		globalpal = sec->ceilingpal;
+		daslope = sec->ceilingheinum;
+		daz = sec->ceilingz;
+	}
+	else
+	{
+		if (globalposz >= getflorzofslope((short) sectnum,globalposx,globalposy))
+			return;  /* Back-face culling */
+		globalorientation = sec->floorstat;
+		globalpicnum = sec->floorpicnum;
+		globalshade = sec->floorshade;
+		globalpal = sec->floorpal;
+		daslope = sec->floorheinum;
+		daz = sec->floorz;
+	}
+
+	if ((picanm[globalpicnum]&192) != 0) globalpicnum += animateoffs(globalpicnum,(short) sectnum);
+	setgotpic(globalpicnum);
+	if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) return;
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+
+	wal = &wall[sec->wallptr];
+	wx = wall[wal->point2].x - wal->x;
+	wy = wall[wal->point2].y - wal->y;
+	dasqr = krecipasm(nsqrtasm(wx*wx+wy*wy));
+	i = mulscale21(daslope,dasqr);
+	wx *= i; wy *= i;
+
+	globalx = -mulscale19(singlobalang,xdimenrecip);
+	globaly = mulscale19(cosglobalang,xdimenrecip);
+	globalx1 = (globalposx<<8);
+	globaly1 = -(globalposy<<8);
+	i = (dax1-halfxdimen)*xdimenrecip;
+	globalx2 = mulscale16(cosglobalang<<4,viewingrangerecip) - mulscale27(singlobalang,i);
+	globaly2 = mulscale16(singlobalang<<4,viewingrangerecip) + mulscale27(cosglobalang,i);
+	globalzd = (xdimscale<<9);
+	globalzx = -dmulscale17(wx,globaly2,-wy,globalx2) + mulscale10(1-globalhoriz,globalzd);
+	globalz = -dmulscale25(wx,globaly,-wy,globalx);
+
+	if (globalorientation&64)  /* Relative alignment */
+	{
+		dx = mulscale14(wall[wal->point2].x-wal->x,dasqr);
+		dy = mulscale14(wall[wal->point2].y-wal->y,dasqr);
+
+		i = nsqrtasm(daslope*daslope+16777216);
+
+		x = globalx; y = globaly;
+		globalx = dmulscale16(x,dx,y,dy);
+		globaly = mulscale12(dmulscale16(-y,dx,x,dy),i);
+
+		x = ((wal->x-globalposx)<<8); y = ((wal->y-globalposy)<<8);
+		globalx1 = dmulscale16(-x,dx,-y,dy);
+		globaly1 = mulscale12(dmulscale16(-y,dx,x,dy),i);
+
+		x = globalx2; y = globaly2;
+		globalx2 = dmulscale16(x,dx,y,dy);
+		globaly2 = mulscale12(dmulscale16(-y,dx,x,dy),i);
+	}
+	if (globalorientation&0x4)
+	{
+		i = globalx; globalx = -globaly; globaly = -i;
+		i = globalx1; globalx1 = globaly1; globaly1 = i;
+		i = globalx2; globalx2 = -globaly2; globaly2 = -i;
+	}
+	if (globalorientation&0x10) { globalx1 = -globalx1, globalx2 = -globalx2, globalx = -globalx; }
+	if (globalorientation&0x20) { globaly1 = -globaly1, globaly2 = -globaly2, globaly = -globaly; }
+
+	daz = dmulscale9(wx,globalposy-wal->y,-wy,globalposx-wal->x) + ((daz-globalposz)<<8);
+	globalx2 = mulscale20(globalx2,daz); globalx = mulscale28(globalx,daz);
+	globaly2 = mulscale20(globaly2,-daz); globaly = mulscale28(globaly,-daz);
+
+	i = 8-(picsiz[globalpicnum]&15); j = 8-(picsiz[globalpicnum]>>4);
+	if (globalorientation&8) { i++; j++; }
+	globalx1 <<= (i+12); globalx2 <<= i; globalx <<= i;
+	globaly1 <<= (j+12); globaly2 <<= j; globaly <<= j;
+
+	if (dastat == 0)
+	{
+		globalx1 += (((long)sec->ceilingxpanning)<<24);
+		globaly1 += (((long)sec->ceilingypanning)<<24);
+	}
+	else
+	{
+		globalx1 += (((long)sec->floorxpanning)<<24);
+		globaly1 += (((long)sec->floorypanning)<<24);
+	}
+
+	asm1 = -(globalzd>>(16-BITSOFPRECISION));
+
+	globvis = globalvisibility;
+	if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+	globvis = mulscale13(globvis,daz);
+	globvis = mulscale16(globvis,xdimscale);
+	j =(long) FP_OFF(palookup[globalpal]);
+
+	setupslopevlin(((long)(picsiz[globalpicnum]&15))+(((long)(picsiz[globalpicnum]>>4))<<8),waloff[globalpicnum],-ylookup[1]);
+
+	l = (globalzd>>16);
+
+	shinc = mulscale16(globalz,xdimenscale);
+	if (shinc > 0) shoffs = (4<<15); else shoffs = ((16380-ydimen)<<15);	// JBF: was 2044     16380
+	if (dastat == 0) y1 = umost[dax1]; else y1 = max(umost[dax1],dplc[dax1]);
+	m1 = mulscale16(y1,globalzd) + (globalzx>>6);
+		/* Avoid visibility overflow by crossing horizon */
+	if (globalzd > 0) m1 += (globalzd>>16); else m1 -= (globalzd>>16);
+	m2 = m1+l;
+	mptr1 = (long *)&slopalookup[y1+(shoffs>>15)]; mptr2 = mptr1+1;
+
+	for(x=dax1;x<=dax2;x++)
+	{
+		if (dastat == 0) { y1 = umost[x]; y2 = min(dmost[x],uplc[x])-1; }
+		else { y1 = max(umost[x],dplc[x]); y2 = dmost[x]-1; }
+		if (y1 <= y2)
+		{
+			nptr1 = (long *)&slopalookup[y1+(shoffs>>15)];
+			nptr2 = (long *)&slopalookup[y2+(shoffs>>15)];
+			while (nptr1 <= mptr1)
+			{
+				*mptr1-- = j + (getpalookup((long)mulscale24(krecipasm(m1),globvis),globalshade)<<8);
+				m1 -= l;
+			}
+			while (nptr2 >= mptr2)
+			{
+				*mptr2++ = j + (getpalookup((long)mulscale24(krecipasm(m2),globvis),globalshade)<<8);
+				m2 += l;
+			}
+
+			globalx3 = (globalx2>>10);
+			globaly3 = (globaly2>>10);
+			asm3 = mulscale16(y2,globalzd) + (globalzx>>6);		
+			slopevlin(ylookup[y2]+x+frameoffset,krecipasm(asm3>>3),(long)nptr2,y2-y1+1,globalx1,globaly1);
+
+			if ((x&15) == 0) faketimerhandler();
+		}
+		globalx2 += globalx;
+		globaly2 += globaly;
+		globalzx += globalz;
+		shoffs += shinc;
+	}
+}
+
+
+static int owallmost(short *mostbuf, long w, long z)
+{
+	long bad, inty, xcross, y, yinc;
+	long s1, s2, s3, s4, ix1, ix2, iy1, iy2, t;
+
+	z <<= 7;
+	s1 = mulscale20(globaluclip,yb1[w]); s2 = mulscale20(globaluclip,yb2[w]);
+	s3 = mulscale20(globaldclip,yb1[w]); s4 = mulscale20(globaldclip,yb2[w]);
+	bad = (z<s1)+((z<s2)<<1)+((z>s3)<<2)+((z>s4)<<3);
+
+	ix1 = xb1[w]; iy1 = yb1[w];
+	ix2 = xb2[w]; iy2 = yb2[w];
+
+	if ((bad&3) == 3)
+	{
+		clearbufbyte(&mostbuf[ix1],(ix2-ix1+1)*sizeof(mostbuf[0]),0L);
+		return(bad);
+	}
+
+	if ((bad&12) == 12)
+	{
+		clearbufbyte(&mostbuf[ix1],(ix2-ix1+1)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		return(bad);
+	}
+
+	if (bad&3)
+	{
+		t = divscale30(z-s1,s2-s1);
+		inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
+
+		if ((bad&3) == 2)
+		{
+			if (xb1[w] <= xcross) { iy2 = inty; ix2 = xcross; }
+			clearbufbyte(&mostbuf[xcross+1],(xb2[w]-xcross)*sizeof(mostbuf[0]),0L);
+		}
+		else
+		{
+			if (xcross <= xb2[w]) { iy1 = inty; ix1 = xcross; }
+			clearbufbyte(&mostbuf[xb1[w]],(xcross-xb1[w]+1)*sizeof(mostbuf[0]),0L);
+		}
+	}
+
+	if (bad&12)
+	{
+		t = divscale30(z-s3,s4-s3);
+		inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
+
+		if ((bad&12) == 8)
+		{
+			if (xb1[w] <= xcross) { iy2 = inty; ix2 = xcross; }
+			clearbufbyte(&mostbuf[xcross+1],(xb2[w]-xcross)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		}
+		else
+		{
+			if (xcross <= xb2[w]) { iy1 = inty; ix1 = xcross; }
+			clearbufbyte(&mostbuf[xb1[w]],(xcross-xb1[w]+1)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		}
+	}
+
+	y = (scale(z,xdimenscale,iy1)<<4);
+	yinc = ((scale(z,xdimenscale,iy2)<<4)-y) / (ix2-ix1+1);
+	qinterpolatedown16short((long *)&mostbuf[ix1],ix2-ix1+1,y+(globalhoriz<<16),yinc);
+
+	if (mostbuf[ix1] < 0) mostbuf[ix1] = 0;
+	if (mostbuf[ix1] > ydimen) mostbuf[ix1] = ydimen;
+	if (mostbuf[ix2] < 0) mostbuf[ix2] = 0;
+	if (mostbuf[ix2] > ydimen) mostbuf[ix2] = ydimen;
+
+	return(bad);
+}
+
+
+static int wallmost(short *mostbuf, long w, long sectnum, char dastat)
+{
+	long bad, i, j, t, y, z, inty, intz, xcross, yinc, fw;
+	long x1, y1, z1, x2, y2, z2, xv, yv, dx, dy, dasqr, oz1, oz2;
+	long s1, s2, s3, s4, ix1, ix2, iy1, iy2;
+
+	if (dastat == 0)
+	{
+		z = sector[sectnum].ceilingz-globalposz;
+		if ((sector[sectnum].ceilingstat&2) == 0) return(owallmost(mostbuf,w,z));
+	}
+	else
+	{
+		z = sector[sectnum].floorz-globalposz;
+		if ((sector[sectnum].floorstat&2) == 0) return(owallmost(mostbuf,w,z));
+	}
+
+	i = thewall[w];
+	if (i == sector[sectnum].wallptr) return(owallmost(mostbuf,w,z));
+
+	x1 = wall[i].x; x2 = wall[wall[i].point2].x-x1;
+	y1 = wall[i].y; y2 = wall[wall[i].point2].y-y1;
+
+	fw = sector[sectnum].wallptr; i = wall[fw].point2;
+	dx = wall[i].x-wall[fw].x; dy = wall[i].y-wall[fw].y;
+	dasqr = krecipasm(nsqrtasm(dx*dx+dy*dy));
+
+	if (xb1[w] == 0)
+		{ xv = cosglobalang+sinviewingrangeglobalang; yv = singlobalang-cosviewingrangeglobalang; }
+	else
+		{ xv = x1-globalposx; yv = y1-globalposy; }
+	i = xv*(y1-globalposy)-yv*(x1-globalposx); j = yv*x2-xv*y2;
+	if (klabs(j) > klabs(i>>3)) i = divscale28(i,j);
+	if (dastat == 0)
+	{
+		t = mulscale15(sector[sectnum].ceilingheinum,dasqr);
+		z1 = sector[sectnum].ceilingz;
+	}
+	else
+	{
+		t = mulscale15(sector[sectnum].floorheinum,dasqr);
+		z1 = sector[sectnum].floorz;
+	}
+	z1 = dmulscale24(dx*t,mulscale20(y2,i)+((y1-wall[fw].y)<<8),
+						 -dy*t,mulscale20(x2,i)+((x1-wall[fw].x)<<8))+((z1-globalposz)<<7);
+
+
+	if (xb2[w] == xdimen-1)
+		{ xv = cosglobalang-sinviewingrangeglobalang; yv = singlobalang+cosviewingrangeglobalang; }
+	else
+		{ xv = (x2+x1)-globalposx; yv = (y2+y1)-globalposy; }
+	i = xv*(y1-globalposy)-yv*(x1-globalposx); j = yv*x2-xv*y2;
+	if (klabs(j) > klabs(i>>3)) i = divscale28(i,j);
+	if (dastat == 0)
+	{
+		t = mulscale15(sector[sectnum].ceilingheinum,dasqr);
+		z2 = sector[sectnum].ceilingz;
+	}
+	else
+	{
+		t = mulscale15(sector[sectnum].floorheinum,dasqr);
+		z2 = sector[sectnum].floorz;
+	}
+	z2 = dmulscale24(dx*t,mulscale20(y2,i)+((y1-wall[fw].y)<<8),
+						 -dy*t,mulscale20(x2,i)+((x1-wall[fw].x)<<8))+((z2-globalposz)<<7);
+
+
+	s1 = mulscale20(globaluclip,yb1[w]); s2 = mulscale20(globaluclip,yb2[w]);
+	s3 = mulscale20(globaldclip,yb1[w]); s4 = mulscale20(globaldclip,yb2[w]);
+	bad = (z1<s1)+((z2<s2)<<1)+((z1>s3)<<2)+((z2>s4)<<3);
+
+	ix1 = xb1[w]; ix2 = xb2[w];
+	iy1 = yb1[w]; iy2 = yb2[w];
+	oz1 = z1; oz2 = z2;
+
+	if ((bad&3) == 3)
+	{
+		clearbufbyte(&mostbuf[ix1],(ix2-ix1+1)*sizeof(mostbuf[0]),0L);
+		return(bad);
+	}
+
+	if ((bad&12) == 12)
+	{
+		clearbufbyte(&mostbuf[ix1],(ix2-ix1+1)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		return(bad);
+	}
+
+	if (bad&3)
+	{
+			/* inty = intz / (globaluclip>>16) */
+		t = divscale30(oz1-s1,s2-s1+oz1-oz2);
+		inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		intz = oz1 + mulscale30(oz2-oz1,t);
+		xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
+
+		/*
+		 * t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
+		 * inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		 * intz = z1 + mulscale30(z2-z1,t);
+         */
+
+		if ((bad&3) == 2)
+		{
+			if (xb1[w] <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
+			clearbufbyte(&mostbuf[xcross+1],(xb2[w]-xcross)*sizeof(mostbuf[0]),0L);
+		}
+		else
+		{
+			if (xcross <= xb2[w]) { z1 = intz; iy1 = inty; ix1 = xcross; }
+			clearbufbyte(&mostbuf[xb1[w]],(xcross-xb1[w]+1)*sizeof(mostbuf[0]),0L);
+		}
+	}
+
+	if (bad&12)
+	{
+			/* inty = intz / (globaldclip>>16) */
+		t = divscale30(oz1-s3,s4-s3+oz1-oz2);
+		inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		intz = oz1 + mulscale30(oz2-oz1,t);
+		xcross = xb1[w] + scale(mulscale30(yb2[w],t),xb2[w]-xb1[w],inty);
+
+		/*
+		 * t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4));
+		 * inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t);
+		 * intz = z1 + mulscale30(z2-z1,t);
+         */
+
+		if ((bad&12) == 8)
+		{
+			if (xb1[w] <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; }
+			clearbufbyte(&mostbuf[xcross+1],(xb2[w]-xcross)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		}
+		else
+		{
+			if (xcross <= xb2[w]) { z1 = intz; iy1 = inty; ix1 = xcross; }
+			clearbufbyte(&mostbuf[xb1[w]],(xcross-xb1[w]+1)*sizeof(mostbuf[0]),ydimen+(ydimen<<16));
+		}
+	}
+
+	y = (scale(z1,xdimenscale,iy1)<<4);
+	yinc = ((scale(z2,xdimenscale,iy2)<<4)-y) / (ix2-ix1+1);
+	qinterpolatedown16short((long *)&mostbuf[ix1],ix2-ix1+1,y+(globalhoriz<<16),yinc);
+
+	if (mostbuf[ix1] < 0) mostbuf[ix1] = 0;
+	if (mostbuf[ix1] > ydimen) mostbuf[ix1] = ydimen;
+	if (mostbuf[ix2] < 0) mostbuf[ix2] = 0;
+	if (mostbuf[ix2] > ydimen) mostbuf[ix2] = ydimen;
+
+	return(bad);
+}
+
+
+static void drawalls(long bunch)
+{
+	sectortype *sec, *nextsec;
+	walltype *wal;
+	long i, x, x1, x2, cz[5], fz[5];
+	long z, wallnum, sectnum, nextsectnum;
+	long startsmostwallcnt, startsmostcnt, gotswall;
+	char andwstat1, andwstat2;
+
+	z = bunchfirst[bunch];
+	sectnum = thesector[z]; sec = &sector[sectnum];
+
+	andwstat1 = 0xff; andwstat2 = 0xff;
+	for(;z>=0;z=p2[z])  /* uplc/dplc calculation */
+	{
+		andwstat1 &= wallmost(uplc,z,sectnum,(char)0);
+		andwstat2 &= wallmost(dplc,z,sectnum,(char)1);
+	}
+
+	if ((andwstat1&3) != 3)     /* draw ceilings */
+	{
+		if ((sec->ceilingstat&3) == 2)
+			grouscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,0);
+		else if ((sec->ceilingstat&1) == 0)
+			ceilscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum);
+		else
+			parascan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,0,bunch);
+	}
+	if ((andwstat2&12) != 12)   /* draw floors */
+	{
+		if ((sec->floorstat&3) == 2)
+			grouscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,1);
+		else if ((sec->floorstat&1) == 0)
+			florscan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum);
+		else
+			parascan(xb1[bunchfirst[bunch]],xb2[bunchlast[bunch]],sectnum,1,bunch);
+	}
+
+		/* DRAW WALLS SECTION! */
+	for(z=bunchfirst[bunch];z>=0;z=p2[z])
+	{
+		x1 = xb1[z]; x2 = xb2[z];
+		if (umost[x2] >= dmost[x2])
+		{
+			for(x=x1;x<x2;x++)
+				if (umost[x] < dmost[x]) break;
+			if (x >= x2)
+			{
+				smostwall[smostwallcnt] = z;
+				smostwalltype[smostwallcnt] = 0;
+				smostwallcnt++;
+				continue;
+			}
+		}
+
+		wallnum = thewall[z]; wal = &wall[wallnum];
+		nextsectnum = wal->nextsector; nextsec = &sector[nextsectnum];
+
+		gotswall = 0;
+
+		startsmostwallcnt = smostwallcnt;
+		startsmostcnt = smostcnt;
+
+		if ((searchit == 2) && (searchx >= x1) && (searchx <= x2))
+		{
+			if (searchy <= uplc[searchx]) /* ceiling */
+			{
+				searchsector = sectnum; searchwall = wallnum;
+				searchstat = 1; searchit = 1;
+			}
+			else if (searchy >= dplc[searchx]) /* floor */
+			{
+				searchsector = sectnum; searchwall = wallnum;
+				searchstat = 2; searchit = 1;
+			}
+		}
+
+		if (nextsectnum >= 0)
+		{
+			getzsofslope((short)sectnum,wal->x,wal->y,&cz[0],&fz[0]);
+			getzsofslope((short)sectnum,wall[wal->point2].x,wall[wal->point2].y,&cz[1],&fz[1]);
+			getzsofslope((short)nextsectnum,wal->x,wal->y,&cz[2],&fz[2]);
+			getzsofslope((short)nextsectnum,wall[wal->point2].x,wall[wal->point2].y,&cz[3],&fz[3]);
+			getzsofslope((short)nextsectnum,globalposx,globalposy,&cz[4],&fz[4]);
+
+			if ((wal->cstat&48) == 16) maskwall[maskwallcnt++] = z;
+
+			if (((sec->ceilingstat&1) == 0) || ((nextsec->ceilingstat&1) == 0))
+			{
+				if ((cz[2] <= cz[0]) && (cz[3] <= cz[1]))
+				{
+					if (globparaceilclip)
+						for(x=x1;x<=x2;x++)
+							if (uplc[x] > umost[x])
+								if (umost[x] <= dmost[x])
+								{
+									umost[x] = uplc[x];
+									if (umost[x] > dmost[x]) numhits--;
+								}
+				}
+				else
+				{
+					wallmost(dwall,z,nextsectnum,(char)0);
+					if ((cz[2] > fz[0]) || (cz[3] > fz[1]))
+						for(i=x1;i<=x2;i++) if (dwall[i] > dplc[i]) dwall[i] = dplc[i];
+
+					if ((searchit == 2) && (searchx >= x1) && (searchx <= x2))
+						if (searchy <= dwall[searchx]) /* wall */
+						{
+							searchsector = sectnum; searchwall = wallnum;
+							searchstat = 0; searchit = 1;
+						}
+
+					globalorientation = (long)wal->cstat;
+					globalpicnum = wal->picnum;
+					if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+					globalxpanning = (long)wal->xpanning;
+					globalypanning = (long)wal->ypanning;
+					globalshiftval = (picsiz[globalpicnum]>>4);
+					if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+					globalshiftval = 32-globalshiftval;
+					if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)(wallnum+16384));
+					globalshade = (long)wal->shade;
+					globvis = globalvisibility;
+					if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+					globalpal = (long)wal->pal;
+					globalyscale = (wal->yrepeat<<(globalshiftval-19));
+					if ((globalorientation&4) == 0)
+						globalzd = (((globalposz-nextsec->ceilingz)*globalyscale)<<8);
+					else
+						globalzd = (((globalposz-sec->ceilingz)*globalyscale)<<8);
+					globalzd += (globalypanning<<24);
+					if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;
+
+					if (gotswall == 0) { gotswall = 1; prepwall(z,wal); }
+					wallscan(x1,x2,uplc,dwall,swall,lwall);
+
+					if ((cz[2] >= cz[0]) && (cz[3] >= cz[1]))
+					{
+						for(x=x1;x<=x2;x++)
+							if (dwall[x] > umost[x])
+								if (umost[x] <= dmost[x])
+								{
+									umost[x] = dwall[x];
+									if (umost[x] > dmost[x]) numhits--;
+								}
+					}
+					else
+					{
+						for(x=x1;x<=x2;x++)
+							if (umost[x] <= dmost[x])
+							{
+								i = max(uplc[x],dwall[x]);
+								if (i > umost[x])
+								{
+									umost[x] = i;
+									if (umost[x] > dmost[x]) numhits--;
+								}
+							}
+					}
+				}
+				if ((cz[2] < cz[0]) || (cz[3] < cz[1]) || (globalposz < cz[4]))
+				{
+					i = x2-x1+1;
+					if (smostcnt+i < MAXYSAVES)
+					{
+						smoststart[smostwallcnt] = smostcnt;
+						smostwall[smostwallcnt] = z;
+						smostwalltype[smostwallcnt] = 1;   /* 1 for umost */
+						smostwallcnt++;
+						copybufbyte((long *)&umost[x1],(long *)&smost[smostcnt],i*sizeof(smost[0]));
+						smostcnt += i;
+					}
+				}
+			}
+			if (((sec->floorstat&1) == 0) || ((nextsec->floorstat&1) == 0))
+			{
+				if ((fz[2] >= fz[0]) && (fz[3] >= fz[1]))
+				{
+					if (globparaflorclip)
+						for(x=x1;x<=x2;x++)
+							if (dplc[x] < dmost[x])
+								if (umost[x] <= dmost[x])
+								{
+									dmost[x] = dplc[x];
+									if (umost[x] > dmost[x]) numhits--;
+								}
+				}
+				else
+				{
+					wallmost(uwall,z,nextsectnum,(char)1);
+					if ((fz[2] < cz[0]) || (fz[3] < cz[1]))
+						for(i=x1;i<=x2;i++) if (uwall[i] < uplc[i]) uwall[i] = uplc[i];
+
+					if ((searchit == 2) && (searchx >= x1) && (searchx <= x2))
+						if (searchy >= uwall[searchx]) /* wall */
+						{
+							searchsector = sectnum; searchwall = wallnum;
+							if ((wal->cstat&2) > 0) searchwall = wal->nextwall;
+							searchstat = 0; searchit = 1;
+						}
+
+					if ((wal->cstat&2) > 0)
+					{
+						wallnum = wal->nextwall; wal = &wall[wallnum];
+						globalorientation = (long)wal->cstat;
+						globalpicnum = wal->picnum;
+						if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+						globalxpanning = (long)wal->xpanning;
+						globalypanning = (long)wal->ypanning;
+						if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)(wallnum+16384));
+						globalshade = (long)wal->shade;
+						globalpal = (long)wal->pal;
+						wallnum = thewall[z]; wal = &wall[wallnum];
+					}
+					else
+					{
+						globalorientation = (long)wal->cstat;
+						globalpicnum = wal->picnum;
+						if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+						globalxpanning = (long)wal->xpanning;
+						globalypanning = (long)wal->ypanning;
+						if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)(wallnum+16384));
+						globalshade = (long)wal->shade;
+						globalpal = (long)wal->pal;
+					}
+					globvis = globalvisibility;
+					if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+					globalshiftval = (picsiz[globalpicnum]>>4);
+					if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+					globalshiftval = 32-globalshiftval;
+					globalyscale = (wal->yrepeat<<(globalshiftval-19));
+					if ((globalorientation&4) == 0)
+						globalzd = (((globalposz-nextsec->floorz)*globalyscale)<<8);
+					else
+						globalzd = (((globalposz-sec->ceilingz)*globalyscale)<<8);
+					globalzd += (globalypanning<<24);
+					if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;
+
+					if (gotswall == 0) { gotswall = 1; prepwall(z,wal); }
+					wallscan(x1,x2,uwall,dplc,swall,lwall);
+
+					if ((fz[2] <= fz[0]) && (fz[3] <= fz[1]))
+					{
+						for(x=x1;x<=x2;x++)
+							if (uwall[x] < dmost[x])
+								if (umost[x] <= dmost[x])
+								{
+									dmost[x] = uwall[x];
+									if (umost[x] > dmost[x]) numhits--;
+								}
+					}
+					else
+					{
+						for(x=x1;x<=x2;x++)
+							if (umost[x] <= dmost[x])
+							{
+								i = min(dplc[x],uwall[x]);
+								if (i < dmost[x])
+								{
+									dmost[x] = i;
+									if (umost[x] > dmost[x]) numhits--;
+								}
+							}
+					}
+				}
+				if ((fz[2] > fz[0]) || (fz[3] > fz[1]) || (globalposz > fz[4]))
+				{
+					i = x2-x1+1;
+					if (smostcnt+i < MAXYSAVES)
+					{
+						smoststart[smostwallcnt] = smostcnt;
+						smostwall[smostwallcnt] = z;
+						smostwalltype[smostwallcnt] = 2;   /* 2 for dmost */
+						smostwallcnt++;
+						copybufbyte((long *)&dmost[x1],(long *)&smost[smostcnt],i*sizeof(smost[0]));
+						smostcnt += i;
+					}
+				}
+			}
+			if (numhits < 0) return;
+			if ((!(wal->cstat&32)) && ((gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0))
+			{
+				if (umost[x2] < dmost[x2])
+					scansector((short) nextsectnum);
+				else
+				{
+					for(x=x1;x<x2;x++)
+						if (umost[x] < dmost[x])
+							{ scansector((short) nextsectnum); break; }
+
+						/*
+						 * If can't see sector beyond, then cancel smost array and just
+						 *  store wall!
+					     */
+					if (x == x2)
+					{
+						smostwallcnt = startsmostwallcnt;
+						smostcnt = startsmostcnt;
+						smostwall[smostwallcnt] = z;
+						smostwalltype[smostwallcnt] = 0;
+						smostwallcnt++;
+					}
+				}
+			}
+		}
+		if ((nextsectnum < 0) || (wal->cstat&32))   /* White/1-way wall */
+		{
+			globalorientation = (long)wal->cstat;
+			if (nextsectnum < 0) globalpicnum = wal->picnum;
+								  else globalpicnum = wal->overpicnum;
+			if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+			globalxpanning = (long)wal->xpanning;
+			globalypanning = (long)wal->ypanning;
+			if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)(wallnum+16384));
+			globalshade = (long)wal->shade;
+			globvis = globalvisibility;
+			if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+			globalpal = (long)wal->pal;
+			globalshiftval = (picsiz[globalpicnum]>>4);
+			if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+			globalshiftval = 32-globalshiftval;
+			globalyscale = (wal->yrepeat<<(globalshiftval-19));
+			if (nextsectnum >= 0)
+			{
+				if ((globalorientation&4) == 0) globalzd = globalposz-nextsec->ceilingz;
+													else globalzd = globalposz-sec->ceilingz;
+			}
+			else
+			{
+				if ((globalorientation&4) == 0) globalzd = globalposz-sec->ceilingz;
+													else globalzd = globalposz-sec->floorz;
+			}
+			globalzd = ((globalzd*globalyscale)<<8) + (globalypanning<<24);
+			if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;
+
+			if (gotswall == 0) { gotswall = 1; prepwall(z,wal); }
+			wallscan(x1,x2,uplc,dplc,swall,lwall);
+
+			for(x=x1;x<=x2;x++)
+				if (umost[x] <= dmost[x])
+					{ umost[x] = 1; dmost[x] = 0; numhits--; }
+			smostwall[smostwallcnt] = z;
+			smostwalltype[smostwallcnt] = 0;
+			smostwallcnt++;
+
+			if ((searchit == 2) && (searchx >= x1) && (searchx <= x2))
+			{
+				searchit = 1; searchsector = sectnum; searchwall = wallnum;
+				if (nextsectnum < 0) searchstat = 0; else searchstat = 4;
+			}
+		}
+	}
+}
+
+
+static void dosetaspect(void)
+{
+	long i, j, k, x, xinc;
+
+	if (xyaspect != oxyaspect)
+	{
+		oxyaspect = xyaspect;
+		j = xyaspect*320;
+		horizlookup2[horizycent-1] = divscale26(131072,j);
+		for(i=ydim*4-1;i>=0;i--)
+			if (i != (horizycent-1))
+			{
+				horizlookup[i] = divscale28(1,i-(horizycent-1));
+				horizlookup2[i] = divscale14(klabs(horizlookup[i]),j);
+			}
+	}
+	if ((xdimen != oxdimen) || (viewingrange != oviewingrange))
+	{
+		oxdimen = xdimen;
+		oviewingrange = viewingrange;
+		xinc = mulscale32(viewingrange*320,xdimenrecip);
+		x = (640<<16)-mulscale1(xinc,xdimen);
+		for(i=0;i<xdimen;i++)
+		{
+			j = (x&65535); k = (x>>16); x += xinc;
+			if (j != 0) j = mulscale16((long)radarang[k+1]-(long)radarang[k],j);
+			radarang2[i] = (short)(((long)radarang[k]+j)>>6);
+		}
+#ifdef SUPERBUILD
+		for(i=1;i<16384;i++) distrecip[i] = divscale20(xdimen,i);
+		nytooclose = xdimen*2100;
+		nytoofar = 16384*16384-1048576;
+#endif
+	}
+}
+
+
+int wallfront(long l1, long l2)
+{
+	walltype *wal;
+	long x11, y11, x21, y21, x12, y12, x22, y22, dx, dy, t1, t2;
+
+	wal = &wall[thewall[l1]]; x11 = wal->x; y11 = wal->y;
+	wal = &wall[wal->point2]; x21 = wal->x; y21 = wal->y;
+	wal = &wall[thewall[l2]]; x12 = wal->x; y12 = wal->y;
+	wal = &wall[wal->point2]; x22 = wal->x; y22 = wal->y;
+
+	dx = x21-x11; dy = y21-y11;
+	t1 = dmulscale2(x12-x11,dy,-dx,y12-y11); /* p1(l2) vs. l1 */
+	t2 = dmulscale2(x22-x11,dy,-dx,y22-y11); /* p2(l2) vs. l1 */
+	if (t1 == 0) { t1 = t2; if (t1 == 0) return(-1); }
+	if (t2 == 0) t2 = t1;
+	if ((t1^t2) >= 0)
+	{
+		t2 = dmulscale2(globalposx-x11,dy,-dx,globalposy-y11); /* pos vs. l1 */
+		return((t2^t1) >= 0);
+	}
+
+	dx = x22-x12; dy = y22-y12;
+	t1 = dmulscale2(x11-x12,dy,-dx,y11-y12); /* p1(l1) vs. l2 */
+	t2 = dmulscale2(x21-x12,dy,-dx,y21-y12); /* p2(l1) vs. l2 */
+	if (t1 == 0) { t1 = t2; if (t1 == 0) return(-1); }
+	if (t2 == 0) t2 = t1;
+	if ((t1^t2) >= 0)
+	{
+		t2 = dmulscale2(globalposx-x12,dy,-dx,globalposy-y12); /* pos vs. l2 */
+		return((t2^t1) < 0);
+	}
+	return(-2);
+}
+
+
+static int bunchfront(long b1, long b2)
+{
+	long x1b1, x2b1, x1b2, x2b2, b1f, b2f, i;
+
+	b1f = bunchfirst[b1]; x1b1 = xb1[b1f]; x2b2 = xb2[bunchlast[b2]]+1;
+	if (x1b1 >= x2b2) return(-1);
+	b2f = bunchfirst[b2]; x1b2 = xb1[b2f]; x2b1 = xb2[bunchlast[b1]]+1;
+	if (x1b2 >= x2b1) return(-1);
+
+	if (x1b1 >= x1b2)
+	{
+		for(i=b2f;xb2[i]<x1b1;i=p2[i]);
+		return(wallfront(b1f,i));
+	}
+	for(i=b1f;xb2[i]<x1b2;i=p2[i]);
+	return(wallfront(i,b2f));
+}
+
+
+void drawrooms(long daposx, long daposy, long daposz,
+               short daang, long dahoriz, short dacursectnum)
+{
+	long i, j, z, cz, fz, closest;
+	short *shortptr1, *shortptr2;
+
+	beforedrawrooms = 0;
+	totalarea += (windowx2+1-windowx1)*(windowy2+1-windowy1);
+
+	globalposx = daposx; globalposy = daposy; globalposz = daposz;
+	globalang = (daang&2047);
+
+	globalhoriz = mulscale16(dahoriz-100,xdimenscale)+(ydimen>>1);
+	globaluclip = (0-globalhoriz)*xdimscale;
+	globaldclip = (ydimen-globalhoriz)*xdimscale;
+
+	i = mulscale16(xdimenscale,viewingrangerecip);
+	globalpisibility = mulscale16(parallaxvisibility,i);
+	globalvisibility = mulscale16(visibility,i);
+	globalhisibility = mulscale16(globalvisibility,xyaspect);
+	globalcisibility = mulscale8(globalhisibility,320);
+
+	globalcursectnum = dacursectnum;
+	totalclocklock = totalclock;
+
+	cosglobalang = sintable[(globalang+512)&2047];
+	singlobalang = sintable[globalang&2047];
+	cosviewingrangeglobalang = mulscale16(cosglobalang,viewingrange);
+	sinviewingrangeglobalang = mulscale16(singlobalang,viewingrange);
+
+	if ((stereomode != 0) || (vidoption == 6))
+	{
+		if (stereopixelwidth != ostereopixelwidth)
+		{
+			ostereopixelwidth = stereopixelwidth;
+			xdimen = (windowx2-windowx1+1)+(stereopixelwidth<<1); halfxdimen = (xdimen>>1);
+			xdimenrecip = divscale32(1L,xdimen);
+			setaspect((long)divscale16(xdimen,windowx2-windowx1+1),yxaspect);
+		}
+
+		if ((!(activepage&1)) ^ inpreparemirror)
+		{
+			for(i=windowx1;i<windowx1+(stereopixelwidth<<1);i++) { startumost[i] = 1, startdmost[i] = 0; }
+			for(;i<windowx2+1+(stereopixelwidth<<1);i++) { startumost[i] = windowy1, startdmost[i] = windowy2+1; }
+			viewoffset = windowy1*bytesperline+windowx1-(stereopixelwidth<<1);
+			i = stereowidth;
+		}
+		else
+		{
+			for(i=windowx1;i<windowx2+1;i++) { startumost[i] = windowy1, startdmost[i] = windowy2+1; }
+			for(;i<windowx2+1+(stereopixelwidth<<1);i++) { startumost[i] = 1, startdmost[i] = 0; }
+			viewoffset = windowy1*bytesperline+windowx1;
+			i = -stereowidth;
+		}
+		globalposx += mulscale24(singlobalang,i);
+		globalposy -= mulscale24(cosglobalang,i);
+		if (vidoption == 6) frameplace = (long)FP_OFF(screen)+(activepage&1)*65536;
+	}
+
+	if ((xyaspect != oxyaspect) || (xdimen != oxdimen) || (viewingrange != oviewingrange))
+		dosetaspect();
+
+	frameoffset = frameplace+viewoffset;
+
+	clearbufbyte(&gotsector[0],(long)((numsectors+7)>>3),0L);
+
+	shortptr1 = (short *)&startumost[windowx1];
+	shortptr2 = (short *)&startdmost[windowx1];
+	i = xdimen-1;
+	do
+	{
+		umost[i] = shortptr1[i]-windowy1;
+		dmost[i] = shortptr2[i]-windowy1;
+		i--;
+	} while (i != 0);
+	umost[0] = shortptr1[0]-windowy1;
+	dmost[0] = shortptr2[0]-windowy1;
+
+/*
+ * every cycle counts...and the CRC gets checked at the start, so this only
+ *  would trigger only if they've changed it in memory after loading (and what
+ *  would anyone do that for on purpose?), or if they've just defeated the
+ *  first CRC check...but if they've hacked the binary/source in that way,
+ *  then they'll just take this check out, too. In short, it ain't worth it.
+ *
+ *    --ryan.
+ */
+#if 0
+	if (smostwallcnt < 0)
+		if (getkensmessagecrc(FP_OFF(kensmessage)) != 0x56c764d4)
+			{ setvmode(0x3); printf("Nice try.\n"); Error(EXIT_SUCCESS, ""); }
+#endif
+
+	numhits = xdimen; numscans = 0; numbunches = 0;
+	maskwallcnt = 0; smostwallcnt = 0; smostcnt = 0; spritesortcnt = 0;
+
+	if (globalcursectnum >= MAXSECTORS)
+		globalcursectnum -= MAXSECTORS;
+	else
+	{
+		i = globalcursectnum;
+		updatesector(globalposx,globalposy,&globalcursectnum);
+		if (globalcursectnum < 0) globalcursectnum = i;
+	}
+
+	globparaceilclip = 1;
+	globparaflorclip = 1;
+	getzsofslope(globalcursectnum,globalposx,globalposy,&cz,&fz);
+	if (globalposz < cz) globparaceilclip = 0;
+	if (globalposz > fz) globparaflorclip = 0;
+
+	scansector(globalcursectnum);
+
+	if (inpreparemirror)
+	{
+		inpreparemirror = 0;
+		mirrorsx1 = xdimen-1; mirrorsx2 = 0;
+		for(i=numscans-1;i>=0;i--)
+		{
+			if (wall[thewall[i]].nextsector < 0) continue;
+			if (xb1[i] < mirrorsx1) mirrorsx1 = xb1[i];
+			if (xb2[i] > mirrorsx2) mirrorsx2 = xb2[i];
+		}
+
+		if (stereomode)
+		{
+			mirrorsx1 += (stereopixelwidth<<1);
+			mirrorsx2 += (stereopixelwidth<<1);
+		}
+
+		for(i=0;i<mirrorsx1;i++)
+			if (umost[i] <= dmost[i])
+				{ umost[i] = 1; dmost[i] = 0; numhits--; }
+		for(i=mirrorsx2+1;i<xdimen;i++)
+			if (umost[i] <= dmost[i])
+				{ umost[i] = 1; dmost[i] = 0; numhits--; }
+
+		drawalls(0L);
+		numbunches--;
+		bunchfirst[0] = bunchfirst[numbunches];
+		bunchlast[0] = bunchlast[numbunches];
+
+		mirrorsy1 = min(umost[mirrorsx1],umost[mirrorsx2]);
+		mirrorsy2 = max(dmost[mirrorsx1],dmost[mirrorsx2]);
+	}
+
+	while ((numbunches > 0) && (numhits > 0))
+	{
+		clearbuf(&tempbuf[0],(long)((numbunches+3)>>2),0L);
+		tempbuf[0] = 1;
+
+		closest = 0;              /* Almost works, but not quite :( */
+		for(i=1;i<numbunches;i++)
+		{
+			if ((j = bunchfront(i,closest)) < 0) continue;
+			tempbuf[i] = 1;
+			if (j == 0) tempbuf[closest] = 1, closest = i;
+		}
+		for(i=0;i<numbunches;i++) /* Double-check */
+		{
+			if (tempbuf[i]) continue;
+			if ((j = bunchfront(i,closest)) < 0) continue;
+			tempbuf[i] = 1;
+			if (j == 0) tempbuf[closest] = 1, closest = i, i = 0;
+		}
+
+		drawalls(closest);
+
+		if (automapping)
+		{
+			for(z=bunchfirst[closest];z>=0;z=p2[z])
+				show2dwall[thewall[z]>>3] |= pow2char[thewall[z]&7];
+		}
+
+		numbunches--;
+		bunchfirst[closest] = bunchfirst[numbunches];
+		bunchlast[closest] = bunchlast[numbunches];
+	}
+}
+
+
+static int spritewallfront (spritetype *s, long w)
+{
+	walltype *wal;
+	long x1, y1;
+
+	wal = &wall[w]; x1 = wal->x; y1 = wal->y;
+	wal = &wall[wal->point2];
+	return (dmulscale32(wal->x-x1,s->y-y1,-(s->x-x1),wal->y-y1) >= 0);
+}
+
+
+static void transmaskvline(long x)
+{
+	long vplc, vinc, p, i, palookupoffs, bufplc;
+	short y1v, y2v;
+
+	if ((x < 0) || (x >= xdimen)) return;
+
+	y1v = max(uwall[x],startumost[x+windowx1]-windowy1);
+	y2v = min(dwall[x],startdmost[x+windowx1]-windowy1);
+	y2v--;
+	if (y2v < y1v) return;
+
+	palookupoffs = (long)FP_OFF(palookup[globalpal]) + (getpalookup((long)mulscale16(swall[x],globvis),globalshade)<<8);
+
+	vinc = swall[x]*globalyscale;
+	vplc = globalzd + vinc*(y1v-globalhoriz+1);
+
+	i = lwall[x]+globalxpanning;
+	if (i >= tilesizx[globalpicnum]) i %= tilesizx[globalpicnum];
+	bufplc = waloff[globalpicnum]+i*tilesizy[globalpicnum];
+
+	p = ylookup[y1v]+x+frameoffset;
+
+	tvlineasm1(vinc,palookupoffs,y2v-y1v,vplc,bufplc,p);
+
+	transarea += y2v-y1v;
+}
+
+static void transmaskvline2 (long x)
+{
+	long i, y1, y2, x2;
+	short y1ve[2], y2ve[2];
+
+	if ((x < 0) || (x >= xdimen)) return;
+	if (x == xdimen-1) { transmaskvline(x); return; }
+
+	x2 = x+1;
+
+	y1ve[0] = max(uwall[x],startumost[x+windowx1]-windowy1);
+	y2ve[0] = min(dwall[x],startdmost[x+windowx1]-windowy1)-1;
+	if (y2ve[0] < y1ve[0]) { transmaskvline(x2); return; }
+	y1ve[1] = max(uwall[x2],startumost[x2+windowx1]-windowy1);
+	y2ve[1] = min(dwall[x2],startdmost[x2+windowx1]-windowy1)-1;
+	if (y2ve[1] < y1ve[1]) { transmaskvline(x); return; }
+
+	palookupoffse[0] = (long)FP_OFF(palookup[globalpal]) + (getpalookup((long)mulscale16(swall[x],globvis),globalshade)<<8);
+	palookupoffse[1] = (long)FP_OFF(palookup[globalpal]) + (getpalookup((long)mulscale16(swall[x2],globvis),globalshade)<<8);
+
+	setuptvlineasm2(globalshiftval,palookupoffse[0],palookupoffse[1]);
+
+	vince[0] = swall[x]*globalyscale;
+	vince[1] = swall[x2]*globalyscale;
+	vplce[0] = globalzd + vince[0]*(y1ve[0]-globalhoriz+1);
+	vplce[1] = globalzd + vince[1]*(y1ve[1]-globalhoriz+1);
+
+	i = lwall[x] + globalxpanning;
+	if (i >= tilesizx[globalpicnum]) i %= tilesizx[globalpicnum];
+	bufplce[0] = waloff[globalpicnum]+i*tilesizy[globalpicnum];
+
+	i = lwall[x2] + globalxpanning;
+	if (i >= tilesizx[globalpicnum]) i %= tilesizx[globalpicnum];
+	bufplce[1] = waloff[globalpicnum]+i*tilesizy[globalpicnum];
+
+	y1 = max(y1ve[0],y1ve[1]);
+	y2 = min(y2ve[0],y2ve[1]);
+
+	i = x+frameoffset;
+
+	if (y1ve[0] != y1ve[1])
+	{
+		if (y1ve[0] < y1)
+			vplce[0] = tvlineasm1(vince[0],palookupoffse[0],y1-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+i);
+		else
+			vplce[1] = tvlineasm1(vince[1],palookupoffse[1],y1-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+i+1);
+	}
+
+	if (y2 > y1)
+	{
+		asm1 = vince[1];
+		asm2 = ylookup[y2]+i+1;
+		tvlineasm2(vplce[1],vince[0],bufplce[0],bufplce[1],vplce[0],ylookup[y1]+i);
+		transarea += ((y2-y1)<<1);
+	}
+	else
+	{
+		asm1 = vplce[0];
+		asm2 = vplce[1];
+	}
+
+	if (y2ve[0] > y2ve[1])
+		tvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y2-1,asm1,bufplce[0],ylookup[y2+1]+i);
+	else if (y2ve[0] < y2ve[1])
+		tvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y2-1,asm2,bufplce[1],ylookup[y2+1]+i+1);
+
+	faketimerhandler();
+}
+
+static void transmaskwallscan(long x1, long x2)
+{
+	long x;
+
+	setgotpic(globalpicnum);
+	if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) return;
+
+	if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+
+	setuptvlineasm(globalshiftval);
+
+	x = x1;
+	while ((startumost[x+windowx1] > startdmost[x+windowx1]) && (x <= x2)) x++;
+	if ((x <= x2) && (x&1)) transmaskvline(x), x++;
+	while (x < x2) transmaskvline2(x), x += 2;
+	while (x <= x2) transmaskvline(x), x++;
+	faketimerhandler();
+}
+
+int loadboard(char *filename, long *daposx, long *daposy,
+              long *daposz, short *daang, short *dacursectnum)
+{
+    int x;
+	short fil, i, j, numsprites;
+    sectortype *sect;
+    spritetype *s;
+    walltype *w;
+
+    x = 0;
+
+	// FIX_00058: Save/load game crash in both single and multiplayer
+	// We have to reset those arrays since the same
+	// arrays are used as temporary space in the 
+	// compilecons() function like "label = (char *)&sprite[0];"
+	// to save memory space I guess. 
+	// Not reseting the array will leave dumps fooling 
+	// the function saveplayer(), eg at if(actorscrptr[PN] == 0)
+	// where PN is sprite[i].picnum was beyong actorscrptr[] size)
+	memset(sprite, 0, sizeof(sprite));
+	memset(sector, 0, sizeof(sector));
+	memset(wall, 0, sizeof(wall));
+
+	if ((fil = kopen4load(filename, 0)) == -1)
+		{ mapversion = 7L; return(-1); }
+
+	kread32(fil,&mapversion);
+	if (mapversion != 7L) return(-1);
+
+	initspritelists();
+
+	clearbuf(&show2dsector[0],(long)((MAXSECTORS+3)>>5),0L);
+	clearbuf(&show2dsprite[0],(long)((MAXSPRITES+3)>>5),0L);
+	clearbuf(&show2dwall[0],(long)((MAXWALLS+3)>>5),0L);
+
+	kread32(fil,daposx);
+	kread32(fil,daposy);
+	kread32(fil,daposz);
+	kread16(fil,daang);
+	kread16(fil,dacursectnum);
+	kread16(fil,&numsectors);
+
+	for (x = 0, sect = &sector[0]; x < numsectors; x++, sect++)
+	{
+		kread16(fil,&sect->wallptr);
+		kread16(fil,&sect->wallnum);
+		kread32(fil,&sect->ceilingz);
+		kread32(fil,&sect->floorz);
+		kread16(fil,&sect->ceilingstat);
+		kread16(fil,&sect->floorstat);
+		kread16(fil,&sect->ceilingpicnum);
+		kread16(fil,&sect->ceilingheinum);
+		 kread8(fil,&sect->ceilingshade);
+		 kread8(fil,&sect->ceilingpal);
+		 kread8(fil,&sect->ceilingxpanning);
+		 kread8(fil,&sect->ceilingypanning);
+		kread16(fil,&sect->floorpicnum);
+		kread16(fil,&sect->floorheinum);
+		 kread8(fil,&sect->floorshade);
+		 kread8(fil,&sect->floorpal);
+		 kread8(fil,&sect->floorxpanning);
+		 kread8(fil,&sect->floorypanning);
+		 kread8(fil,&sect->visibility);
+		 kread8(fil,&sect->filler);
+		kread16(fil,&sect->lotag);
+		kread16(fil,&sect->hitag);
+		kread16(fil,&sect->extra);
+	}
+
+	kread16(fil,&numwalls);
+	for (x = 0, w = &wall[0]; x < numwalls; x++, w++)
+	{
+		kread32(fil,&w->x);
+		kread32(fil,&w->y);
+		kread16(fil,&w->point2);
+		kread16(fil,&w->nextwall);
+		kread16(fil,&w->nextsector);
+		kread16(fil,&w->cstat);
+		kread16(fil,&w->picnum);
+		kread16(fil,&w->overpicnum);
+		 kread8(fil,&w->shade);
+		 kread8(fil,&w->pal);
+		 kread8(fil,&w->xrepeat);
+		 kread8(fil,&w->yrepeat);
+		 kread8(fil,&w->xpanning);
+		 kread8(fil,&w->ypanning);
+		kread16(fil,&w->lotag);
+		kread16(fil,&w->hitag);
+		kread16(fil,&w->extra);
+	}
+
+    kread16(fil,&numsprites);
+	for (x = 0, s = &sprite[0]; x < numsprites; x++, s++)
+	{
+		kread32(fil,&s->x);
+		kread32(fil,&s->y);
+		kread32(fil,&s->z);
+		kread16(fil,&s->cstat);
+		kread16(fil,&s->picnum);
+		 kread8(fil,&s->shade);
+		 kread8(fil,&s->pal);
+ 		 kread8(fil,&s->clipdist);
+		 kread8(fil,&s->filler);
+		 kread8(fil,&s->xrepeat);
+		 kread8(fil,&s->yrepeat);
+		 kread8(fil,&s->xoffset);
+		 kread8(fil,&s->yoffset);
+		kread16(fil,&s->sectnum);
+		kread16(fil,&s->statnum);
+		kread16(fil,&s->ang);
+		kread16(fil,&s->owner);
+		kread16(fil,&s->xvel);
+		kread16(fil,&s->yvel);
+		kread16(fil,&s->zvel);
+		kread16(fil,&s->lotag);
+		kread16(fil,&s->hitag);
+		kread16(fil,&s->extra);
+	}
+
+
+	for(i=0;i<numsprites;i++)
+		insertsprite(sprite[i].sectnum,sprite[i].statnum);
+
+		/* Must be after loading sectors, etc! */
+	updatesector(*daposx,*daposy,dacursectnum);
+
+	kclose(fil);
+
+	// FIX_00009: Show map CRC and GRP file version of each player in case of Out Of Synch
+
+	mapCRC = crc16((char*)sector, numsectors*sizeof(sectortype));
+	mapCRC += crc16((char*)wall, numwalls*sizeof(walltype));
+	mapCRC += crc16((char*)sprite, numsprites*sizeof(spritetype));
+
+	return(0);
+}
+
+
+static void write32(int f, long val)
+{
+    val = BUILDSWAP_INTEL32(val);
+    write(f, &val, 4);
+}
+
+static void write16(int f, short val)
+{
+    val = BUILDSWAP_INTEL16(val);
+    write(f, &val, 2);
+}
+
+static void write8(int f, char val)
+{
+    write(f, &val, 1);
+}
+
+
+int saveboard(char *filename, long *daposx, long *daposy,
+              long *daposz, short *daang, short *dacursectnum)
+{
+	int fil;
+    int x;
+    short i, j, numsprites;
+    int permissions = 0;
+    walltype *w;
+    sectortype *sect;
+
+    #if ((defined PLATFORM_DOS) || (defined PLATFORM_WIN32))
+        permissions = S_IWRITE;
+    #elif (defined PLATFORM_UNIX)
+        permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    #endif
+
+	if ((fil = open(filename,
+                    O_BINARY|O_TRUNC|O_CREAT|O_WRONLY,
+                    permissions)) == -1)
+    {
+		return(-1);
+    }
+
+	write32(fil,mapversion);
+
+	write32(fil,*daposx);
+	write32(fil,*daposy);
+	write32(fil,*daposz);
+	write16(fil,*daang);
+	write16(fil,*dacursectnum);
+
+	write16(fil,numsectors);
+    for (x = 0, sect = &sector[0]; x < numsectors; x++, sect++)
+    {
+		write16(fil,sect->wallptr);
+		write16(fil,sect->wallnum);
+		write32(fil,sect->ceilingz);
+		write32(fil,sect->floorz);
+		write16(fil,sect->ceilingstat);
+		write16(fil,sect->floorstat);
+		write16(fil,sect->ceilingpicnum);
+		write16(fil,sect->ceilingheinum);
+		 write8(fil,sect->ceilingshade);
+		 write8(fil,sect->ceilingpal);
+		 write8(fil,sect->ceilingxpanning);
+		 write8(fil,sect->ceilingypanning);
+		write16(fil,sect->floorpicnum);
+		write16(fil,sect->floorheinum);
+		 write8(fil,sect->floorshade);
+		 write8(fil,sect->floorpal);
+		 write8(fil,sect->floorxpanning);
+		 write8(fil,sect->floorypanning);
+		 write8(fil,sect->visibility);
+		 write8(fil,sect->filler);
+		write16(fil,sect->lotag);
+		write16(fil,sect->hitag);
+		write16(fil,sect->extra);
+    }
+
+	write16(fil,numwalls);
+    for (x = 0, w = &wall[0]; x < numwalls; x++, w++)
+    {
+		write32(fil,w->x);
+		write32(fil,w->y);
+		write16(fil,w->point2);
+		write16(fil,w->nextwall);
+		write16(fil,w->nextsector);
+		write16(fil,w->cstat);
+		write16(fil,w->picnum);
+		write16(fil,w->overpicnum);
+		 write8(fil,w->shade);
+		 write8(fil,w->pal);
+		 write8(fil,w->xrepeat);
+		 write8(fil,w->yrepeat);
+		 write8(fil,w->xpanning);
+		 write8(fil,w->ypanning);
+		write16(fil,w->lotag);
+		write16(fil,w->hitag);
+		write16(fil,w->extra);
+    }
+
+	numsprites = 0;
+	for(j=0;j<MAXSTATUS;j++)
+	{
+		i = headspritestat[j];
+		while (i != -1)
+		{
+			numsprites++;
+			i = nextspritestat[i];
+		}
+	}
+	write16(fil,numsprites);
+
+	for(j=0;j<MAXSTATUS;j++)
+	{
+		i = headspritestat[j];
+		while (i != -1)
+		{
+			spritetype *s = &sprite[i];
+			write32(fil,s->x);
+			write32(fil,s->y);
+			write32(fil,s->z);
+			write16(fil,s->cstat);
+			write16(fil,s->picnum);
+			 write8(fil,s->shade);
+			 write8(fil,s->pal);
+			 write8(fil,s->clipdist);
+			 write8(fil,s->filler);
+			 write8(fil,s->xrepeat);
+			 write8(fil,s->yrepeat);
+			 write8(fil,s->xoffset);
+			 write8(fil,s->yoffset);
+			write16(fil,s->sectnum);
+			write16(fil,s->statnum);
+			write16(fil,s->ang);
+			write16(fil,s->owner);
+			write16(fil,s->xvel);
+			write16(fil,s->yvel);
+			write16(fil,s->zvel);
+			write16(fil,s->lotag);
+			write16(fil,s->hitag);
+			write16(fil,s->extra);
+
+			i = nextspritestat[i];
+		}
+	}
+
+	close(fil);
+	return(0);
+}
+
+
+static void initksqrt(void)
+{
+	long i, j, k;
+
+	j = 1; k = 0;
+	for(i=0;i<4096;i++)
+	{
+		if (i >= j) { j <<= 2; k++; }
+		sqrtable[i] = (unsigned short)(msqrtasm((i<<18)+131072)<<1);
+		shlookup[i] = (k<<1)+((10-k)<<8);
+		if (i < 256) shlookup[i+4096] = ((k+6)<<1)+((10-(k+6))<<8);
+	}
+}
+
+
+static void loadtables(void)
+{
+	long i, fil;
+
+	if (tablesloaded == 0)
+	{
+		initksqrt();
+
+		for(i=0;i<2048;i++) reciptable[i] = divscale30(2048L,i+2048);
+
+		if ((fil = TCkopen4load("tables.dat",0)) != -1)
+		{
+            for (i = 0; i < 2048; i++)
+				kread16(fil,&sintable[i]);
+
+            for (i = 0; i < 640; i++)
+				kread16(fil,&radarang[i]);
+
+			for(i=0;i<640;i++) radarang[1279-i] = -radarang[i];
+			kread(fil,textfont,1024);
+			kread(fil,smalltextfont,1024);
+			kread(fil,britable,1024);
+			kclose(fil);
+		}
+		tablesloaded = 1;
+	}
+}
+
+
+static void initfastcolorlookup(long rscale, long gscale, long bscale)
+{
+	long i, j, x, y, z;
+	char *pal1;
+
+	j = 0;
+	for(i=64;i>=0;i--)
+	{
+		/*j = (i-64)*(i-64);*/
+		rdist[i] = rdist[128-i] = j*rscale;
+		gdist[i] = gdist[128-i] = j*gscale;
+		bdist[i] = bdist[128-i] = j*bscale;
+		j += 129-(i<<1);
+	}
+
+	clearbufbyte((void *)FP_OFF(colhere),sizeof(colhere),0L);
+	clearbufbyte((void *)FP_OFF(colhead),sizeof(colhead),0L);
+
+	pal1 = (char *)&palette[768-3];
+	for(i=255;i>=0;i--,pal1-=3)
+	{
+		j = (pal1[0]>>3)*FASTPALGRIDSIZ*FASTPALGRIDSIZ+(pal1[1]>>3)*FASTPALGRIDSIZ+(pal1[2]>>3)+FASTPALGRIDSIZ*FASTPALGRIDSIZ+FASTPALGRIDSIZ+1;
+		if (colhere[j>>3]&pow2char[j&7]) colnext[i] = colhead[j]; else colnext[i] = -1;
+		colhead[j] = i;
+		colhere[j>>3] |= pow2char[j&7];
+	}
+
+	i = 0;
+	for(x=-FASTPALGRIDSIZ*FASTPALGRIDSIZ;x<=FASTPALGRIDSIZ*FASTPALGRIDSIZ;x+=FASTPALGRIDSIZ*FASTPALGRIDSIZ)
+		for(y=-FASTPALGRIDSIZ;y<=FASTPALGRIDSIZ;y+=FASTPALGRIDSIZ)
+			for(z=-1;z<=1;z++)
+				colscan[i++] = x+y+z;
+	i = colscan[13]; colscan[13] = colscan[26]; colscan[26] = i;
+}
+
+
+static void loadpalette(void)
+{
+	long i, j, k, dist, fil;
+	char *ptr;
+
+	if (paletteloaded != 0) return;
+	if ((fil = TCkopen4load("palette.dat",0)) == -1) return;
+
+	kread(fil,palette,768);
+	kread16(fil,&numpalookups);
+
+	if ((palookup[0] = (char *)kkmalloc(numpalookups<<8)) == NULL)
+		allocache((long *)&palookup[0],numpalookups<<8,&permanentlock);
+	if ((transluc = (char *)kkmalloc(65536L)) == NULL)
+		allocache((long *)&transluc,65536,&permanentlock);
+
+	globalpalwritten = palookup[0]; globalpal = 0;
+	setpalookupaddress(globalpalwritten);
+
+	fixtransluscence((long)FP_OFF(transluc));
+
+	kread(fil,palookup[globalpal],numpalookups<<8);
+
+
+	/*kread(fil,transluc,65536);*/
+	for (k = 0; k < (65536 / 4); k++)
+	    kread32(fil, ((long *) transluc) + k);
+
+	kclose(fil);
+
+	initfastcolorlookup(30L,59L,11L);
+
+	paletteloaded = 1;
+
+	if (vidoption == 6)
+	{
+		for(k=0;k<MAXPALOOKUPS;k++)
+			if (palookup[k] != NULL)
+				for(i=0;i<256;i++)
+				{
+					dist = palette[i*3]*3+palette[i*3+1]*5+palette[i*3+2]*2;
+					ptr = (char *)(FP_OFF(palookup[k])+i);
+					for(j=0;j<32;j++)
+						ptr[j<<8] = (char)min(max(mulscale10(dist,32-j),0),15);
+				}
+
+		if (transluc != NULL)
+		{
+			for(i=0;i<16;i++)
+				for(j=0;j<16;j++)
+					transluc[(i<<8)+j] = ((i+j+1)>>1);
+		}
+	}
+}
+
+
+
+int setgamemode(char davidoption, long daxdim, long daydim)
+{
+	strcpy(kensmessage,"!!!! BUILD engine&tools programmed by Ken Silverman of E.G. RI.  (c) Copyright 1995 Ken Silverman.  Summary:  BUILD = Ken. !!!!");
+	if (getkensmessagecrc(FP_OFF(kensmessage)) != 0x56c764d4)
+		{ setvmode(0x3); printf("Nice try.\n"); Error(EXIT_SUCCESS, ""); }
+    return(_setgamemode(davidoption, daxdim, daydim));
+} /* setgamemode */
+
+
+static int dommxoverlay = 1;
+static int initengine_called = 0;
+
+void setmmxoverlay(int isenabled)
+{
+    if (!initengine_called)
+        dommxoverlay = (isenabled) ? 1 : 0;
+} /* setmmxoverlay */
+
+
+int getmmxoverlay(void)
+{
+    return(dommxoverlay);
+} /* getmmxoverlay */
+
+
+void initengine(void)
+{
+    long i;
+
+#ifdef SUPERBUILD
+	long j;
+#endif
+
+    initengine_called = 1;
+
+	if (dommxoverlay)
+        mmxoverlay();
+
+	loadtables();
+
+	xyaspect = -1;
+
+	pskyoff[0] = 0; pskybits = 0;
+
+	parallaxtype = 2; parallaxyoffs = 0L; parallaxyscale = 65536;
+	showinvisibility = 0;
+
+#ifdef SUPERBUILD
+	for(i=1;i<1024;i++) lowrecip[i] = ((1<<24)-1)/i;
+	for(i=0;i<MAXVOXELS;i++)
+		for(j=0;j<MAXVOXMIPS;j++)
+		{
+			voxoff[i][j] = 0L;
+			voxlock[i][j] = 200;
+		}
+#endif
+
+	paletteloaded = 0;
+
+	searchit = 0; searchstat = -1;
+
+	for(i=0;i<MAXPALOOKUPS;i++) palookup[i] = NULL;
+
+	clearbuf(&waloff[0],(long)MAXTILES,0L);
+
+	clearbuf(&show2dsector[0],(long)((MAXSECTORS+3)>>5),0L);
+	clearbuf(&show2dsprite[0],(long)((MAXSPRITES+3)>>5),0L);
+	clearbuf(&show2dwall[0],(long)((MAXWALLS+3)>>5),0L);
+	automapping = 0;
+
+	validmodecnt = 0;
+
+	pointhighlight = -1;
+	linehighlight = -1;
+	highlightcnt = 0;
+
+	totalclock = 0;
+	visibility = 512;
+	parallaxvisibility = 512;
+
+	loadpalette();
+}
+
+
+void uninitengine(void)
+{
+	if (transluc != NULL) { kkfree(transluc); transluc = NULL; }
+	if (pic != NULL) { kkfree(pic); pic = NULL; }
+	if (artfil != -1) kclose(artfil);
+    _uninitengine(); /* video driver specific. */
+}
+
+
+	/* Assume npoints=4 with polygon on &rx1,&ry1 */
+static int clippoly4(long cx1, long cy1, long cx2, long cy2)
+{
+	long n, nn, z, zz, x, x1, x2, y, y1, y2, t;
+
+	nn = 0; z = 0;
+	do
+	{
+		zz = ((z+1)&3);
+		x1 = rx1[z]; x2 = rx1[zz]-x1;
+
+		if ((cx1 <= x1) && (x1 <= cx2))
+			rx2[nn] = x1, ry2[nn] = ry1[z], nn++;
+
+		if (x2 <= 0) x = cx2; else x = cx1;
+		t = x-x1;
+		if (((t-x2)^t) < 0)
+			rx2[nn] = x, ry2[nn] = ry1[z]+scale(t,ry1[zz]-ry1[z],x2), nn++;
+
+		if (x2 <= 0) x = cx1; else x = cx2;
+		t = x-x1;
+		if (((t-x2)^t) < 0)
+			rx2[nn] = x, ry2[nn] = ry1[z]+scale(t,ry1[zz]-ry1[z],x2), nn++;
+
+		z = zz;
+	} while (z != 0);
+	if (nn < 3) return(0);
+
+	n = 0; z = 0;
+	do
+	{
+		zz = z+1; if (zz == nn) zz = 0;
+		y1 = ry2[z]; y2 = ry2[zz]-y1;
+
+		if ((cy1 <= y1) && (y1 <= cy2))
+			ry1[n] = y1, rx1[n] = rx2[z], n++;
+
+		if (y2 <= 0) y = cy2; else y = cy1;
+		t = y-y1;
+		if (((t-y2)^t) < 0)
+			ry1[n] = y, rx1[n] = rx2[z]+scale(t,rx2[zz]-rx2[z],y2), n++;
+
+		if (y2 <= 0) y = cy1; else y = cy2;
+		t = y-y1;
+		if (((t-y2)^t) < 0)
+			ry1[n] = y, rx1[n] = rx2[z]+scale(t,rx2[zz]-rx2[z],y2), n++;
+
+		z = zz;
+	} while (z != 0);
+	return(n);
+}
+
+
+
+static void dorotatesprite (long sx, long sy, long z, short a, short picnum,
+	signed char dashade, unsigned char dapalnum, char dastat, long cx1,
+	long cy1, long cx2, long cy2)
+{
+	long cosang, sinang, v, nextv, dax1, dax2, oy, bx, by, ny1, ny2;
+	long i, x, y, x1, y1, x2, y2, gx1, gy1, p, bufplc, palookupoffs;
+	long xsiz, ysiz, xoff, yoff, npoints, yplc, yinc, lx, rx, xx, xend;
+	long xv, yv, xv2, yv2, obuffermode=0, qlinemode=0, y1ve[4], y2ve[4], u4, d4;
+	char bad;
+
+	xsiz = tilesizx[picnum]; ysiz = tilesizy[picnum];
+	if (dastat&16) { xoff = 0; yoff = 0; }
+	else
+	{
+		xoff = (long)((signed char)((picanm[picnum]>>8)&255))+(xsiz>>1);
+		yoff = (long)((signed char)((picanm[picnum]>>16)&255))+(ysiz>>1);
+	}
+
+	if (dastat&4) yoff = ysiz-yoff;
+
+	cosang = sintable[(a+512)&2047]; sinang = sintable[a&2047];
+
+	if ((dastat&2) != 0)  /* Auto window size scaling */
+	{
+		if ((dastat&8) == 0)
+		{
+			x = xdimenscale;   /* = scale(xdimen,yxaspect,320); */
+			if (stereomode) x = scale(windowx2-windowx1+1,yxaspect,320);
+			sx = ((cx1+cx2+2)<<15)+scale(sx-(320<<15),xdimen,320);
+			sy = ((cy1+cy2+2)<<15)+mulscale16(sy-(200<<15),x);
+		}
+		else
+		{
+			  /*
+			   * If not clipping to startmosts, & auto-scaling on, as a
+			   *  hard-coded bonus, scale to full screen instead
+               */
+            x = scale(xdim,yxaspect,320);
+			sx = (xdim<<15)+32768+scale(sx-(320<<15),xdim,320);
+			sy = (ydim<<15)+32768+mulscale16(sy-(200<<15),x);
+		}
+		z = mulscale16(z,x);
+	}
+
+	xv = mulscale14(cosang,z);
+	yv = mulscale14(sinang,z);
+	if (((dastat&2) != 0) || ((dastat&8) == 0)) /* Don't aspect unscaled perms */
+	{
+		xv2 = mulscale16(xv,xyaspect);
+		yv2 = mulscale16(yv,xyaspect);
+	}
+	else
+	{
+		xv2 = xv;
+		yv2 = yv;
+	}
+
+	ry1[0] = sy - (yv*xoff + xv*yoff);
+	ry1[1] = ry1[0] + yv*xsiz;
+	ry1[3] = ry1[0] + xv*ysiz;
+	ry1[2] = ry1[1]+ry1[3]-ry1[0];
+	i = (cy1<<16); if ((ry1[0]<i) && (ry1[1]<i) && (ry1[2]<i) && (ry1[3]<i)) return;
+	i = (cy2<<16); if ((ry1[0]>i) && (ry1[1]>i) && (ry1[2]>i) && (ry1[3]>i)) return;
+
+	rx1[0] = sx - (xv2*xoff - yv2*yoff);
+	rx1[1] = rx1[0] + xv2*xsiz;
+	rx1[3] = rx1[0] - yv2*ysiz;
+	rx1[2] = rx1[1]+rx1[3]-rx1[0];
+	i = (cx1<<16); if ((rx1[0]<i) && (rx1[1]<i) && (rx1[2]<i) && (rx1[3]<i)) return;
+	i = (cx2<<16); if ((rx1[0]>i) && (rx1[1]>i) && (rx1[2]>i) && (rx1[3]>i)) return;
+
+	gx1 = rx1[0]; gy1 = ry1[0];   /* back up these before clipping */
+
+	if ((npoints = clippoly4(cx1<<16,cy1<<16,(cx2+1)<<16,(cy2+1)<<16)) < 3) return;
+
+	lx = rx1[0]; rx = rx1[0];
+
+	nextv = 0;
+	for(v=npoints-1;v>=0;v--)
+	{
+		x1 = rx1[v]; x2 = rx1[nextv];
+		dax1 = (x1>>16); if (x1 < lx) lx = x1;
+		dax2 = (x2>>16); if (x1 > rx) rx = x1;
+		if (dax1 != dax2)
+		{
+			y1 = ry1[v]; y2 = ry1[nextv];
+			yinc = divscale16(y2-y1,x2-x1);
+			if (dax2 > dax1)
+			{
+				yplc = y1 + mulscale16((dax1<<16)+65535-x1,yinc);
+				qinterpolatedown16short((long *)(&uplc[dax1]),dax2-dax1,yplc,yinc);
+			}
+			else
+			{
+				yplc = y2 + mulscale16((dax2<<16)+65535-x2,yinc);
+				qinterpolatedown16short((long *)(&dplc[dax2]),dax1-dax2,yplc,yinc);
+			}
+		}
+		nextv = v;
+	}
+
+	if (waloff[picnum] == 0) loadtile(picnum);
+	setgotpic(picnum);
+	bufplc = waloff[picnum];
+
+	palookupoffs = (long) FP_OFF(palookup[dapalnum]) + (getpalookup(0L,(long)dashade)<<8);
+
+	i = divscale32(1L,z);
+	xv = mulscale14(sinang,i);
+	yv = mulscale14(cosang,i);
+	if (((dastat&2) != 0) || ((dastat&8) == 0)) /* Don't aspect unscaled perms */
+	{
+		yv2 = mulscale16(-xv,yxaspect);
+		xv2 = mulscale16(yv,yxaspect);
+	}
+	else
+	{
+		yv2 = -xv;
+		xv2 = yv;
+	}
+
+	x1 = (lx>>16); x2 = (rx>>16);
+
+	oy = 0;
+	x = (x1<<16)-1-gx1; y = (oy<<16)+65535-gy1;
+	bx = dmulscale16(x,xv2,y,xv);
+	by = dmulscale16(x,yv2,y,yv);
+	if (dastat&4) { yv = -yv; yv2 = -yv2; by = (ysiz<<16)-1-by; }
+
+	if ((vidoption == 1) && (origbuffermode == 0))
+	{
+		if (dastat&128)
+		{
+			obuffermode = buffermode;
+			buffermode = 0;
+			setactivepage(activepage);
+		}
+	}
+	else if (dastat&8)
+		 permanentupdate = 1;
+
+	if ((dastat&1) == 0)
+	{
+		if (((a&1023) == 0) && (ysiz <= 256))  /* vlineasm4 has 256 high limit! */
+		{
+			if (dastat&64) setupvlineasm(24L); else setupmvlineasm(24L);
+			by <<= 8; yv <<= 8; yv2 <<= 8;
+
+			palookupoffse[0] = palookupoffse[1] = palookupoffse[2] = palookupoffse[3] = palookupoffs;
+			vince[0] = vince[1] = vince[2] = vince[3] = yv;
+
+			for(x=x1;x<x2;x+=4)
+			{
+				bad = 15; xend = min(x2-x,4);
+				for(xx=0;xx<xend;xx++)
+				{
+					bx += xv2;
+
+					y1 = uplc[x+xx]; y2 = dplc[x+xx];
+					if ((dastat&8) == 0)
+					{
+						if (startumost[x+xx] > y1) y1 = startumost[x+xx];
+						if (startdmost[x+xx] < y2) y2 = startdmost[x+xx];
+					}
+					if (y2 <= y1) continue;
+
+					by += yv*(y1-oy); oy = y1;
+
+					bufplce[xx] = (bx>>16)*ysiz+bufplc;
+					vplce[xx] = by;
+					y1ve[xx] = y1;
+					y2ve[xx] = y2-1;
+					bad &= ~pow2char[xx];
+				}
+
+				p = x+frameplace;
+
+				u4 = max(max(y1ve[0],y1ve[1]),max(y1ve[2],y1ve[3]));
+				d4 = min(min(y2ve[0],y2ve[1]),min(y2ve[2],y2ve[3]));
+
+				if (dastat&64)
+				{
+					if ((bad != 0) || (u4 >= d4))
+					{
+						if (!(bad&1)) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+						if (!(bad&2)) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+						if (!(bad&4)) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+						if (!(bad&8)) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+						continue;
+					}
+
+					if (u4 > y1ve[0]) vplce[0] = prevlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+					if (u4 > y1ve[1]) vplce[1] = prevlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+					if (u4 > y1ve[2]) vplce[2] = prevlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+					if (u4 > y1ve[3]) vplce[3] = prevlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+
+					if (d4 >= u4) vlineasm4(d4-u4+1,ylookup[u4]+p);
+
+					i = p+ylookup[d4+1];
+					if (y2ve[0] > d4) prevlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
+					if (y2ve[1] > d4) prevlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
+					if (y2ve[2] > d4) prevlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
+					if (y2ve[3] > d4) prevlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
+				}
+				else
+				{
+					if ((bad != 0) || (u4 >= d4))
+					{
+						if (!(bad&1)) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-y1ve[0],vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+						if (!(bad&2)) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-y1ve[1],vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+						if (!(bad&4)) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-y1ve[2],vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+						if (!(bad&8)) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-y1ve[3],vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+						continue;
+					}
+
+					if (u4 > y1ve[0]) vplce[0] = mvlineasm1(vince[0],palookupoffse[0],u4-y1ve[0]-1,vplce[0],bufplce[0],ylookup[y1ve[0]]+p+0);
+					if (u4 > y1ve[1]) vplce[1] = mvlineasm1(vince[1],palookupoffse[1],u4-y1ve[1]-1,vplce[1],bufplce[1],ylookup[y1ve[1]]+p+1);
+					if (u4 > y1ve[2]) vplce[2] = mvlineasm1(vince[2],palookupoffse[2],u4-y1ve[2]-1,vplce[2],bufplce[2],ylookup[y1ve[2]]+p+2);
+					if (u4 > y1ve[3]) vplce[3] = mvlineasm1(vince[3],palookupoffse[3],u4-y1ve[3]-1,vplce[3],bufplce[3],ylookup[y1ve[3]]+p+3);
+
+					if (d4 >= u4) mvlineasm4(d4-u4+1,ylookup[u4]+p);
+
+					i = p+ylookup[d4+1];
+					if (y2ve[0] > d4) mvlineasm1(vince[0],palookupoffse[0],y2ve[0]-d4-1,vplce[0],bufplce[0],i+0);
+					if (y2ve[1] > d4) mvlineasm1(vince[1],palookupoffse[1],y2ve[1]-d4-1,vplce[1],bufplce[1],i+1);
+					if (y2ve[2] > d4) mvlineasm1(vince[2],palookupoffse[2],y2ve[2]-d4-1,vplce[2],bufplce[2],i+2);
+					if (y2ve[3] > d4) mvlineasm1(vince[3],palookupoffse[3],y2ve[3]-d4-1,vplce[3],bufplce[3],i+3);
+				}
+
+				faketimerhandler();
+			}
+		}
+		else
+		{
+			if (dastat&64)
+			{
+				if ((xv2&0x0000ffff) == 0)
+				{
+					qlinemode = 1;
+					setupqrhlineasm4(0L,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,0L,0L);
+				}
+				else
+				{
+					qlinemode = 0;
+					setuprhlineasm4(xv2<<16,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,ysiz,0L);
+				}
+			}
+			else
+				setuprmhlineasm4(xv2<<16,yv2<<16,(xv2>>16)*ysiz+(yv2>>16),palookupoffs,ysiz,0L);
+
+			y1 = uplc[x1];
+			if (((dastat&8) == 0) && (startumost[x1] > y1)) y1 = startumost[x1];
+			y2 = y1;
+			for(x=x1;x<x2;x++)
+			{
+				ny1 = uplc[x]-1; ny2 = dplc[x];
+				if ((dastat&8) == 0)
+				{
+					if (startumost[x]-1 > ny1) ny1 = startumost[x]-1;
+					if (startdmost[x] < ny2) ny2 = startdmost[x];
+				}
+
+				if (ny1 < ny2-1)
+				{
+					if (ny1 >= y2)
+					{
+						while (y1 < y2-1)
+						{
+							y1++; if ((y1&31) == 0) faketimerhandler();
+
+								/* x,y1 */
+							bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
+							if (dastat&64) {  if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
+																  else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+															  } else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+						}
+						y1 = ny1;
+					}
+					else
+					{
+						while (y1 < ny1)
+						{
+							y1++; if ((y1&31) == 0) faketimerhandler();
+
+								/* x,y1 */
+							bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
+							if (dastat&64) {  if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
+																  else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+															  } else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+						}
+						while (y1 > ny1) lastx[y1--] = x;
+					}
+					while (y2 > ny2)
+					{
+						y2--; if ((y2&31) == 0) faketimerhandler();
+
+							/* x,y2 */
+						bx += xv*(y2-oy); by += yv*(y2-oy); oy = y2;
+						if (dastat&64) {  if (qlinemode) qrhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y2]+x+frameplace);
+															  else rhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y2]+x+frameplace);
+														  } else rmhlineasm4(x-lastx[y2],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y2]+x+frameplace);
+					}
+					while (y2 < ny2) lastx[y2++] = x;
+				}
+				else
+				{
+					while (y1 < y2-1)
+					{
+						y1++; if ((y1&31) == 0) faketimerhandler();
+
+							/* x,y1 */
+						bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
+						if (dastat&64) {  if (qlinemode) qrhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x+frameplace);
+															  else rhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+														  } else rmhlineasm4(x-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x+frameplace);
+					}
+					if (x == x2-1) { bx += xv2; by += yv2; break; }
+					y1 = uplc[x+1];
+					if (((dastat&8) == 0) && (startumost[x+1] > y1)) y1 = startumost[x+1];
+					y2 = y1;
+				}
+				bx += xv2; by += yv2;
+			}
+			while (y1 < y2-1)
+			{
+				y1++; if ((y1&31) == 0) faketimerhandler();
+
+					/* x2,y1 */
+				bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1;
+				if (dastat&64) {  if (qlinemode) qrhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,0L    ,by<<16,ylookup[y1]+x2+frameplace);
+													  else rhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x2+frameplace);
+												  } else rmhlineasm4(x2-lastx[y1],(bx>>16)*ysiz+(by>>16)+bufplc,0L,bx<<16,by<<16,ylookup[y1]+x2+frameplace);
+			}
+		}
+	}
+	else
+	{
+	if ((dastat&1) == 0)
+	{
+		if (dastat&64)
+				setupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
+		else
+				msetupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
+	}
+	else
+	{
+			tsetupspritevline(palookupoffs,(xv>>16)*ysiz,xv<<16,ysiz,yv,0L);
+		if (dastat&32) settransreverse(); else settransnormal();
+	}
+	for(x=x1;x<x2;x++)
+	{
+		bx += xv2; by += yv2;
+
+		y1 = uplc[x]; y2 = dplc[x];
+		if ((dastat&8) == 0)
+		{
+			if (startumost[x] > y1) y1 = startumost[x];
+			if (startdmost[x] < y2) y2 = startdmost[x];
+		}
+		if (y2 <= y1) continue;
+
+		switch(y1-oy)
+		{
+		case -1: bx -= xv; by -= yv; oy = y1; break;
+		case 0: break;
+		case 1: bx += xv; by += yv; oy = y1; break;
+		default: bx += xv*(y1-oy); by += yv*(y1-oy); oy = y1; break;
+		}
+
+		p = ylookup[y1]+x+frameplace;
+
+		if ((dastat&1) == 0)
+		{
+			if (dastat&64)
+					spritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
+			else
+					mspritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
+		}
+		else
+		{
+				tspritevline(0L,by<<16,y2-y1+1,bx<<16,(bx>>16)*ysiz+(by>>16)+bufplc,p);
+				transarea += (y2-y1);
+		}
+		faketimerhandler();
+	}
+	}
+
+	if ((vidoption == 1) && (dastat&128) && (origbuffermode == 0))
+	{
+		buffermode = obuffermode;
+		setactivepage(activepage);
+	}
+}
+
+
+void nextpage(void)
+{
+	long i;
+	permfifotype *per;
+	/* long j,k; */
+
+#if 0
+	char snotbuf[32];
+	j = 0; k = 0;
+	for(i=0;i<4096;i++)
+	   if (waloff[i] != 0)
+	   {
+	      sprintf(snotbuf,"%ld-%ld",i,tilesizx[i]*tilesizy[i]);
+	      printext256((j>>5)*40+32,(j&31)*6,walock[i]>>3,-1,snotbuf,1);
+	      k += tilesizx[i]*tilesizy[i];
+	      j++;
+	   }
+	sprintf(snotbuf,"Total: %ld",k);
+	printext256((j>>5)*40+32,(j&31)*6,31,-1,snotbuf,1);
+#endif
+
+    if (qsetmode == 200)
+    {
+  		for(i=permtail;i!=permhead;i=((i+1)&(MAXPERMS-1)))
+   		{
+   			per = &permfifo[i];
+   			if ((per->pagesleft > 0) && (per->pagesleft <= numpages))
+   				dorotatesprite(per->sx,per->sy,per->z,per->a,per->picnum,
+   									per->dashade,per->dapalnum,per->dastat,
+   									per->cx1,per->cy1,per->cx2,per->cy2);
+   		}
+    } /* if */
+
+    _nextpage();  /* video driver specific. */
+
+
+    if (qsetmode == 200)
+    {
+   		for(i=permtail;i!=permhead;i=((i+1)&(MAXPERMS-1)))
+   		{
+   			per = &permfifo[i];
+   			if (per->pagesleft >= 130)
+   				dorotatesprite(per->sx,per->sy,per->z,per->a,per->picnum,
+   									per->dashade,per->dapalnum,per->dastat,
+   									per->cx1,per->cy1,per->cx2,per->cy2);
+				if (per->pagesleft&127) per->pagesleft--;
+   			if (((per->pagesleft&127) == 0) && (i == permtail))
+   				permtail = ((permtail+1)&(MAXPERMS-1));
+   		}
+    } /* if */
+
+	faketimerhandler();
+
+	if ((totalclock >= lastageclock+8) || (totalclock < lastageclock))
+		{ lastageclock = totalclock; agecache(); }
+
+	beforedrawrooms = 1;
+	numframes++;
+}
+
+
+void loadtile(short tilenume)
+{
+	char *ptr;
+	long i, dasiz;
+
+	if ((unsigned)tilenume >= (unsigned)MAXTILES) return;
+	dasiz = tilesizx[tilenume]*tilesizy[tilenume];
+	if (dasiz <= 0) return;
+
+	i = tilefilenum[tilenume];
+	if (i != artfilnum)
+	{
+		if (artfil != -1) kclose(artfil);
+		artfilnum = i;
+		artfilplc = 0L;
+
+		artfilename[7] = (i%10)+48;
+		artfilename[6] = ((i/10)%10)+48;
+		artfilename[5] = ((i/100)%10)+48;
+		artfil = TCkopen4load(artfilename,0);
+		faketimerhandler();
+	}
+
+	#if BUILD_CACHEDEBUG
+        fprintf(stderr, "BUILDCACHE: Tile:%d\n", tilenume);
+    #endif
+
+	if (waloff[tilenume] == 0)
+	{
+		walock[tilenume] = 199;
+		allocache(&waloff[tilenume],dasiz,(unsigned char *) &walock[tilenume]);
+	}
+
+	if (artfilplc != tilefileoffs[tilenume])
+	{
+		klseek(artfil,tilefileoffs[tilenume]-artfilplc,SEEK_CUR);
+		faketimerhandler();
+	}
+	ptr = (char *)waloff[tilenume];
+	kread(artfil,ptr,dasiz);
+	faketimerhandler();
+	artfilplc = tilefileoffs[tilenume]+dasiz;
+}
+
+
+int allocatepermanenttile(short tilenume, long xsiz, long ysiz)
+{
+	long j, dasiz;
+
+	if ((xsiz <= 0) || (ysiz <= 0) || ((unsigned)tilenume >= (unsigned)MAXTILES))
+		return(0);
+
+	dasiz = xsiz*ysiz;
+
+	walock[tilenume] = 255;
+	allocache(&waloff[tilenume],dasiz,(unsigned char *) &walock[tilenume]);
+
+	tilesizx[tilenume] = xsiz;
+	tilesizy[tilenume] = ysiz;
+	picanm[tilenume] = 0;
+
+	j = 15; while ((j > 1) && (pow2long[j] > xsiz)) j--;
+	picsiz[tilenume] = ((char)j);
+	j = 15; while ((j > 1) && (pow2long[j] > ysiz)) j--;
+	picsiz[tilenume] += ((char)(j<<4));
+
+	return(waloff[tilenume]);
+}
+
+
+int loadpics(char *filename, char* gamedir)
+{
+	long offscount, localtilestart, localtileend, dasiz;
+	short fil, i, j, k;
+	//char fullpathartfilename[512];
+
+	strcpy(artfilename,filename);
+
+	for(i=0;i<MAXTILES;i++)
+	{
+		tilesizx[i] = 0;
+		tilesizy[i] = 0;
+		picanm[i] = 0L;
+	}
+
+	artsize = 0L;
+
+	numtilefiles = 0;
+	do
+	{
+		k = numtilefiles;
+
+		artfilename[7] = (k%10)+48;
+		artfilename[6] = ((k/10)%10)+48;
+		artfilename[5] = ((k/100)%10)+48;
+
+		/*
+		// Are we loading a TC?
+		if(gamedir[0] != 0)
+		{
+			// Yes
+			sprintf(fullpathartfilename, "%s\\%s", gamedir, artfilename);
+			if(!SafeFileExists(fullpathartfilename))
+			{
+				// If this isn't in the TC's game root, then just load it as normal
+				sprintf(fullpathartfilename, "%s", artfilename);
+			}
+		}
+		else
+		{
+			// No
+			sprintf(fullpathartfilename, "%s", artfilename);
+		}
+		*/
+
+		if ((fil = TCkopen4load(artfilename,0)) != -1)
+		{
+			kread32(fil,&artversion);
+			if (artversion != 1) return(-1);
+
+			kread32(fil,&numtiles);
+			kread32(fil,&localtilestart);
+			kread32(fil,&localtileend);
+
+			/*kread(fil,&tilesizx[localtilestart],(localtileend-localtilestart+1)<<1);*/
+			for (i = localtilestart; i <= localtileend; i++)
+				kread16(fil,&tilesizx[i]);
+
+			/*kread(fil,&tilesizy[localtilestart],(localtileend-localtilestart+1)<<1);*/
+			for (i = localtilestart; i <= localtileend; i++)
+			    kread16(fil,&tilesizy[i]);
+
+			/*kread(fil,&picanm[localtilestart],(localtileend-localtilestart+1)<<2);*/
+			for (i = localtilestart; i <= localtileend; i++)
+			    kread32(fil,&picanm[i]);
+
+			offscount = 4+4+4+4+((localtileend-localtilestart+1)<<3);
+			for(i=localtilestart;i<=localtileend;i++)
+			{
+				tilefilenum[i] = k;
+				tilefileoffs[i] = offscount;
+				dasiz = (long)(tilesizx[i]*tilesizy[i]);
+				offscount += dasiz;
+				artsize += ((dasiz+15)&0xfffffff0);
+			}
+			kclose(fil);
+
+			numtilefiles++;
+
+		}
+	}
+	while (k != numtilefiles);
+	printf("Art files loaded\n");
+	clearbuf(&gotpic[0],(long)((MAXTILES+31)>>5),0L);
+
+	/* try dpmi_DETERMINEMAXREALALLOC! */
+
+	cachesize = max(artsize,1048576);
+	while ((pic = (char *)kkmalloc(cachesize)) == NULL)
+	{
+		cachesize -= 65536L;
+		if (cachesize < 65536) return(-1);
+	}
+	initcache(((long)FP_OFF(pic)+15)&0xfffffff0,(cachesize-((-(long)FP_OFF(pic))&15))&0xfffffff0);
+
+	for(i=0;i<MAXTILES;i++)
+	{
+		j = 15;
+		while ((j > 1) && (pow2long[j] > tilesizx[i])) j--;
+		picsiz[i] = ((char)j);
+		j = 15;
+		while ((j > 1) && (pow2long[j] > tilesizy[i])) j--;
+		picsiz[i] += ((char)(j<<4));
+	}
+
+	artfil = -1;
+	artfilnum = -1;
+	artfilplc = 0L;
+
+	return(0);
+}
+
+#ifdef SUPERBUILD
+void qloadkvx(long voxindex, char *filename)
+{
+	long i, fil, dasiz, lengcnt, lengtot;
+	char *ptr;
+
+	if ((fil = kopen4load(filename,0)) == -1) return;
+
+	lengcnt = 0;
+	lengtot = kfilelength(fil);
+
+	for(i=0;i<MAXVOXMIPS;i++)
+	{
+		kread32(fil,&dasiz);
+
+			/* Must store filenames to use cacheing system :( */
+		voxlock[voxindex][i] = 200;
+		allocache(&voxoff[voxindex][i],dasiz,(unsigned char *)&voxlock[voxindex][i]);
+		ptr = (char *)voxoff[voxindex][i];
+		kread(fil,ptr,dasiz);
+
+		lengcnt += dasiz+4;
+		if (lengcnt >= lengtot-768) break;
+	}
+	kclose(fil);
+}
+#endif
+
+
+int clipinsidebox(long x, long y, short wallnum, long walldist)
+{
+	walltype *wal;
+	long x1, y1, x2, y2, r;
+
+	r = (walldist<<1);
+	wal = &wall[wallnum];     x1 = wal->x+walldist-x; y1 = wal->y+walldist-y;
+	wal = &wall[wal->point2]; x2 = wal->x+walldist-x; y2 = wal->y+walldist-y;
+
+	if ((x1 < 0) && (x2 < 0)) return(0);
+	if ((y1 < 0) && (y2 < 0)) return(0);
+	if ((x1 >= r) && (x2 >= r)) return(0);
+	if ((y1 >= r) && (y2 >= r)) return(0);
+
+	x2 -= x1; y2 -= y1;
+	if (x2*(walldist-y1) >= y2*(walldist-x1))  /* Front */
+	{
+		if (x2 > 0) x2 *= (0-y1); else x2 *= (r-y1);
+		if (y2 > 0) y2 *= (r-x1); else y2 *= (0-x1);
+		return(x2 < y2);
+	}
+	if (x2 > 0) x2 *= (r-y1); else x2 *= (0-y1);
+	if (y2 > 0) y2 *= (0-x1); else y2 *= (r-x1);
+	return((x2 >= y2)<<1);
+}
+
+static int clipinsideboxline(long x, long y, long x1, long y1, long x2, long y2, long walldist)
+{
+	long r;
+
+	r = (walldist<<1);
+
+	x1 += walldist-x; x2 += walldist-x;
+	if ((x1 < 0) && (x2 < 0)) return(0);
+	if ((x1 >= r) && (x2 >= r)) return(0);
+
+	y1 += walldist-y; y2 += walldist-y;
+	if ((y1 < 0) && (y2 < 0)) return(0);
+	if ((y1 >= r) && (y2 >= r)) return(0);
+
+	x2 -= x1; y2 -= y1;
+	if (x2*(walldist-y1) >= y2*(walldist-x1))  /* Front */
+	{
+		if (x2 > 0) x2 *= (0-y1); else x2 *= (r-y1);
+		if (y2 > 0) y2 *= (r-x1); else y2 *= (0-x1);
+		return(x2 < y2);
+	}
+	if (x2 > 0) x2 *= (r-y1); else x2 *= (0-y1);
+	if (y2 > 0) y2 *= (0-x1); else y2 *= (r-x1);
+	return((x2 >= y2)<<1);
+}
+
+
+void drawline256 (long x1, long y1, long x2, long y2, unsigned char col)
+{
+	long dx, dy, i, j, p, inc, plc, daend;
+
+	col = palookup[0][col];
+
+	dx = x2-x1; dy = y2-y1;
+	if (dx >= 0)
+	{
+		if ((x1 >= wx2) || (x2 < wx1)) return;
+		if (x1 < wx1) y1 += scale(wx1-x1,dy,dx), x1 = wx1;
+		if (x2 > wx2) y2 += scale(wx2-x2,dy,dx), x2 = wx2;
+	}
+	else
+	{
+		if ((x2 >= wx2) || (x1 < wx1)) return;
+		if (x2 < wx1) y2 += scale(wx1-x2,dy,dx), x2 = wx1;
+		if (x1 > wx2) y1 += scale(wx2-x1,dy,dx), x1 = wx2;
+	}
+	if (dy >= 0)
+	{
+		if ((y1 >= wy2) || (y2 < wy1)) return;
+		if (y1 < wy1) x1 += scale(wy1-y1,dx,dy), y1 = wy1;
+		if (y2 > wy2) x2 += scale(wy2-y2,dx,dy), y2 = wy2;
+	}
+	else
+	{
+		if ((y2 >= wy2) || (y1 < wy1)) return;
+		if (y2 < wy1) x2 += scale(wy1-y2,dx,dy), y2 = wy1;
+		if (y1 > wy2) x1 += scale(wy2-y1,dx,dy), y1 = wy2;
+	}
+
+	if (klabs(dx) >= klabs(dy))
+	{
+		if (dx == 0) return;
+		if (dx < 0)
+		{
+			i = x1; x1 = x2; x2 = i;
+			i = y1; y1 = y2; y2 = i;
+		}
+
+		inc = divscale12(dy,dx);
+		plc = y1+mulscale12((2047-x1)&4095,inc);
+		i = ((x1+2048)>>12); daend = ((x2+2048)>>12);
+		for(;i<daend;i++)
+		{
+			j = (plc>>12);
+			if ((j >= startumost[i]) && (j < startdmost[i]))
+				drawpixel(ylookup[j]+i+frameplace,col);
+			plc += inc;
+		}
+	}
+	else
+	{
+		if (dy < 0)
+		{
+			i = x1; x1 = x2; x2 = i;
+			i = y1; y1 = y2; y2 = i;
+		}
+
+		inc = divscale12(dx,dy);
+		plc = x1+mulscale12((2047-y1)&4095,inc);
+		i = ((y1+2048)>>12); daend = ((y2+2048)>>12);
+		p = ylookup[i]+frameplace;
+		for(;i<daend;i++)
+		{
+			j = (plc>>12);
+			if ((i >= startumost[j]) && (i < startdmost[j]))
+				drawpixel(j+p,col);
+			plc += inc; p += ylookup[1];
+		}
+	}
+}
+
+
+int inside(long x, long y, short sectnum)
+{
+	walltype *wal;
+	long i, x1, y1, x2, y2;
+	unsigned long cnt;
+
+	if ((sectnum < 0) || (sectnum >= numsectors)) return(-1);
+
+	cnt = 0;
+	wal = &wall[sector[sectnum].wallptr];
+	i = sector[sectnum].wallnum;
+	do
+	{
+		y1 = wal->y-y; y2 = wall[wal->point2].y-y;
+		if ((y1^y2) < 0)
+		{
+			x1 = wal->x-x; x2 = wall[wal->point2].x-x;
+			if ((x1^x2) >= 0) cnt ^= x1; else cnt ^= (x1*y2-x2*y1)^y2;
+		}
+		wal++; i--;
+	} while (i);
+	return(cnt>>31);
+}
+
+
+int getangle(long xvect, long yvect)
+{
+	if ((xvect|yvect) == 0) return(0);
+	if (xvect == 0) return(512+((yvect<0)<<10));
+	if (yvect == 0) return(((xvect<0)<<10));
+	if (xvect == yvect) return(256+((xvect<0)<<10));
+	if (xvect == -yvect) return(768+((xvect>0)<<10));
+	if (klabs(xvect) > klabs(yvect))
+		return(((radarang[640+scale(160,yvect,xvect)]>>6)+((xvect<0)<<10))&2047);
+	return(((radarang[640-scale(160,xvect,yvect)]>>6)+512+((yvect<0)<<10))&2047);
+}
+
+
+int ksqrt(long num)
+{
+	return(nsqrtasm(num));
+}
+
+
+void copytilepiece(long tilenume1, long sx1, long sy1, long xsiz, long ysiz,
+                   long tilenume2, long sx2, long sy2)
+{
+	unsigned char *ptr1, *ptr2, dat;
+	long xsiz1, ysiz1, xsiz2, ysiz2, i, j, x1, y1, x2, y2;
+
+	xsiz1 = tilesizx[tilenume1]; ysiz1 = tilesizy[tilenume1];
+	xsiz2 = tilesizx[tilenume2]; ysiz2 = tilesizy[tilenume2];
+	if ((xsiz1 > 0) && (ysiz1 > 0) && (xsiz2 > 0) && (ysiz2 > 0))
+	{
+		if (waloff[tilenume1] == 0) loadtile((short) tilenume1);
+		if (waloff[tilenume2] == 0) loadtile((short) tilenume2);
+
+		x1 = sx1;
+		for(i=0;i<xsiz;i++)
+		{
+			y1 = sy1;
+			for(j=0;j<ysiz;j++)
+			{
+				x2 = sx2+i;
+				y2 = sy2+j;
+				if ((x2 >= 0) && (y2 >= 0) && (x2 < xsiz2) && (y2 < ysiz2))
+				{
+					ptr1 = (unsigned char *) (waloff[tilenume1] + x1*ysiz1 + y1);
+					ptr2 = (unsigned char *) (waloff[tilenume2] + x2*ysiz2 + y2);
+					dat = *ptr1;
+					if (dat != 255)
+						*ptr2 = *ptr1;
+				}
+
+				y1++; if (y1 >= ysiz1) y1 = 0;
+			}
+			x1++; if (x1 >= xsiz1) x1 = 0;
+		}
+	}
+}
+
+
+static void drawmaskwall(short damaskwallcnt)
+{
+	long i, j, k, x, z, sectnum, z1, z2, lx, rx;
+	sectortype *sec, *nsec;
+	walltype *wal;
+
+	z = maskwall[damaskwallcnt];
+	wal = &wall[thewall[z]];
+	sectnum = thesector[z]; sec = &sector[sectnum];
+	nsec = &sector[wal->nextsector];
+	z1 = max(nsec->ceilingz,sec->ceilingz);
+	z2 = min(nsec->floorz,sec->floorz);
+
+	wallmost(uwall,z,sectnum,(char)0);
+	wallmost(uplc,z,(long)wal->nextsector,(char)0);
+	for(x=xb1[z];x<=xb2[z];x++) if (uplc[x] > uwall[x]) uwall[x] = uplc[x];
+	wallmost(dwall,z,sectnum,(char)1);
+	wallmost(dplc,z,(long)wal->nextsector,(char)1);
+	for(x=xb1[z];x<=xb2[z];x++) if (dplc[x] < dwall[x]) dwall[x] = dplc[x];
+	prepwall(z,wal);
+
+	globalorientation = (long)wal->cstat;
+	globalpicnum = wal->overpicnum;
+	if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+	globalxpanning = (long)wal->xpanning;
+	globalypanning = (long)wal->ypanning;
+	if (picanm[globalpicnum]&192) globalpicnum += animateoffs(globalpicnum,(short)(thewall[z]+16384));
+	globalshade = (long)wal->shade;
+	globvis = globalvisibility;
+	if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+	globalpal = (long)wal->pal;
+	globalshiftval = (picsiz[globalpicnum]>>4);
+	if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+	globalshiftval = 32-globalshiftval;
+	globalyscale = (wal->yrepeat<<(globalshiftval-19));
+	if ((globalorientation&4) == 0)
+		globalzd = (((globalposz-z1)*globalyscale)<<8);
+	else
+		globalzd = (((globalposz-z2)*globalyscale)<<8);
+	globalzd += (globalypanning<<24);
+	if (globalorientation&256) globalyscale = -globalyscale, globalzd = -globalzd;
+
+	for(i=smostwallcnt-1;i>=0;i--)
+	{
+		j = smostwall[i];
+		if ((xb1[j] > xb2[z]) || (xb2[j] < xb1[z])) continue;
+		if (wallfront(j,z)) continue;
+
+		lx = max(xb1[j],xb1[z]); rx = min(xb2[j],xb2[z]);
+
+		switch(smostwalltype[i])
+		{
+			case 0:
+				if (lx <= rx)
+				{
+					if ((lx == xb1[z]) && (rx == xb2[z])) return;
+					clearbufbyte(&dwall[lx],(rx-lx+1)*sizeof(dwall[0]),0L);
+				}
+				break;
+			case 1:
+				k = smoststart[i] - xb1[j];
+				for(x=lx;x<=rx;x++)
+					if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
+				break;
+			case 2:
+				k = smoststart[i] - xb1[j];
+				for(x=lx;x<=rx;x++)
+					if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
+				break;
+		}
+	}
+
+		/* maskwall */
+	if ((searchit >= 1) && (searchx >= xb1[z]) && (searchx <= xb2[z]))
+		if ((searchy >= uwall[searchx]) && (searchy <= dwall[searchx]))
+		{
+			searchsector = sectnum; searchwall = thewall[z];
+			searchstat = 4; searchit = 1;
+		}
+
+	if ((globalorientation&128) == 0)
+		maskwallscan(xb1[z],xb2[z],uwall,dwall,swall,lwall);
+	else
+	{
+		if (globalorientation&128)
+		{
+			if (globalorientation&512) settransreverse(); else settransnormal();
+		}
+		transmaskwallscan(xb1[z],xb2[z]);
+	}
+}
+
+
+static void ceilspritehline (long x2, long y)
+{
+	long x1, v, bx, by;
+
+	/*
+	 * x = x1 + (x2-x1)t + (y1-y2)u  �  x = 160v
+	 * y = y1 + (y2-y1)t + (x2-x1)u  �  y = (scrx-160)v
+	 * z = z1 = z2                   �  z = posz + (scry-horiz)v
+     */
+
+	x1 = lastx[y]; if (x2 < x1) return;
+
+	v = mulscale20(globalzd,horizlookup[y-globalhoriz+horizycent]);
+	bx = mulscale14(globalx2*x1+globalx1,v) + globalxpanning;
+	by = mulscale14(globaly2*x1+globaly1,v) + globalypanning;
+	asm1 = mulscale14(globalx2,v);
+	asm2 = mulscale14(globaly2,v);
+
+	asm3 = (long)FP_OFF(palookup[globalpal]) + (getpalookup((long)mulscale28(klabs(v),globvis),globalshade)<<8);
+
+	if ((globalorientation&2) == 0)
+		mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
+	else
+	{
+		thline(globalbufplc,bx,(x2-x1)<<16,0L,by,ylookup[y]+x1+frameoffset);
+		transarea += (x2-x1);
+	}
+}
+
+
+static void ceilspritescan (long x1, long x2)
+{
+	long x, y1, y2, twall, bwall;
+
+	y1 = uwall[x1]; y2 = y1;
+	for(x=x1;x<=x2;x++)
+	{
+		twall = uwall[x]-1; bwall = dwall[x];
+		if (twall < bwall-1)
+		{
+			if (twall >= y2)
+			{
+				while (y1 < y2-1) ceilspritehline(x-1,++y1);
+				y1 = twall;
+			}
+			else
+			{
+				while (y1 < twall) ceilspritehline(x-1,++y1);
+				while (y1 > twall) lastx[y1--] = x;
+			}
+			while (y2 > bwall) ceilspritehline(x-1,--y2);
+			while (y2 < bwall) lastx[y2++] = x;
+		}
+		else
+		{
+			while (y1 < y2-1) ceilspritehline(x-1,++y1);
+			if (x == x2) break;
+			y1 = uwall[x+1]; y2 = y1;
+		}
+	}
+	while (y1 < y2-1) ceilspritehline(x2,++y1);
+	faketimerhandler();
+}
+
+
+#ifdef SUPERBUILD
+static void drawvox(long dasprx, long daspry, long dasprz, long dasprang,
+		  long daxscale, long dayscale, unsigned char daindex,
+		  signed char dashade, unsigned char dapal, long *daumost, long *dadmost)
+{
+	long i, j, k, x, y, syoff, ggxstart, ggystart, nxoff;
+	long cosang, sinang, sprcosang, sprsinang, backx, backy, gxinc, gyinc;
+	long daxsiz, daysiz, dazsiz, daxpivot, daypivot, dazpivot;
+	long daxscalerecip, dayscalerecip, cnt, gxstart, gystart, odayscale;
+	long l1, l2, slabxoffs, xyvoxoffs, *longptr;
+	long lx, rx, nx, ny, x1=0, y1=0, z1, x2=0, y2=0, z2, yplc, yinc=0;
+	long yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc;
+	short *shortptr;
+	char *voxptr, *voxend, *davoxptr, oand, oand16, oand32;
+
+	cosang = sintable[(globalang+512)&2047];
+	sinang = sintable[globalang&2047];
+	sprcosang = sintable[(dasprang+512)&2047];
+	sprsinang = sintable[dasprang&2047];
+
+	i = klabs(dmulscale6(dasprx-globalposx,cosang,daspry-globalposy,sinang));
+	j = (long)(getpalookup((long)mulscale21(globvis,i),(long)dashade)<<8);
+	setupdrawslab(ylookup[1],(long)FP_OFF(palookup[dapal])+j);
+	j = 1310720;
+	j *= min(daxscale,dayscale); j >>= 6;  /* New hacks (for sized-down voxels) */
+	for(k=0;k<MAXVOXMIPS;k++)
+	{
+		if (i < j) { i = k; break; }
+		j <<= 1;
+	}
+	if (k >= MAXVOXMIPS) i = MAXVOXMIPS-1;
+
+	davoxptr = (char *)voxoff[daindex][i]; if (!davoxptr) return;
+
+	daxscale <<= (i+8); dayscale <<= (i+8);
+	odayscale = dayscale;
+	daxscale = mulscale16(daxscale,xyaspect);
+	daxscale = scale(daxscale,xdimenscale,xdimen<<8);
+	dayscale = scale(dayscale,mulscale16(xdimenscale,viewingrangerecip),xdimen<<8);
+
+	daxscalerecip = (1<<30)/daxscale;
+	dayscalerecip = (1<<30)/dayscale;
+
+	longptr = (long *)davoxptr;
+	daxsiz = longptr[0]; daysiz = longptr[1]; dazsiz = longptr[2];
+	daxpivot = longptr[3]; daypivot = longptr[4]; dazpivot = longptr[5];
+	davoxptr += (6<<2);
+
+	x = mulscale16(globalposx-dasprx,daxscalerecip);
+	y = mulscale16(globalposy-daspry,daxscalerecip);
+	backx = ((dmulscale10(x,sprcosang,y,sprsinang)+daxpivot)>>8);
+	backy = ((dmulscale10(y,sprcosang,x,-sprsinang)+daypivot)>>8);
+	cbackx = min(max(backx,0),daxsiz-1);
+	cbacky = min(max(backy,0),daysiz-1);
+
+	sprcosang = mulscale14(daxscale,sprcosang);
+	sprsinang = mulscale14(daxscale,sprsinang);
+
+	x = (dasprx-globalposx) - dmulscale18(daxpivot,sprcosang,daypivot,-sprsinang);
+	y = (daspry-globalposy) - dmulscale18(daypivot,sprcosang,daxpivot,sprsinang);
+
+	cosang = mulscale16(cosang,dayscalerecip);
+	sinang = mulscale16(sinang,dayscalerecip);
+
+	gxstart = y*cosang - x*sinang;
+	gystart = x*cosang + y*sinang;
+	gxinc = dmulscale10(sprsinang,cosang,sprcosang,-sinang);
+	gyinc = dmulscale10(sprcosang,cosang,sprsinang,sinang);
+
+	x = 0; y = 0; j = max(daxsiz,daysiz);
+	for(i=0;i<=j;i++)
+	{
+		ggxinc[i] = x; x += gxinc;
+		ggyinc[i] = y; y += gyinc;
+	}
+
+	if ((klabs(globalposz-dasprz)>>10) >= klabs(odayscale)) return;
+	syoff = divscale21(globalposz-dasprz,odayscale) + (dazpivot<<7);
+	yoff = ((klabs(gxinc)+klabs(gyinc))>>1);
+	longptr = (long *)davoxptr;
+	xyvoxoffs = ((daxsiz+1)<<2);
+
+	for(cnt=0;cnt<8;cnt++)
+	{
+		switch(cnt)
+		{
+			case 0: xs = 0;        ys = 0;        xi = 1;  yi = 1;  break;
+			case 1: xs = daxsiz-1; ys = 0;        xi = -1; yi = 1;  break;
+			case 2: xs = 0;        ys = daysiz-1; xi = 1;  yi = -1; break;
+			case 3: xs = daxsiz-1; ys = daysiz-1; xi = -1; yi = -1; break;
+			case 4: xs = 0;        ys = cbacky;   xi = 1;  yi = 2;  break;
+			case 5: xs = daxsiz-1; ys = cbacky;   xi = -1; yi = 2;  break;
+			case 6: xs = cbackx;   ys = 0;        xi = 2;  yi = 1;  break;
+			case 7: xs = cbackx;   ys = daysiz-1; xi = 2;  yi = -1; break;
+		}
+		xe = cbackx; ye = cbacky;
+		if (cnt < 4)
+		{
+			if ((xi < 0) && (xe >= xs)) continue;
+			if ((xi > 0) && (xe <= xs)) continue;
+			if ((yi < 0) && (ye >= ys)) continue;
+			if ((yi > 0) && (ye <= ys)) continue;
+		}
+		else
+		{
+			if ((xi < 0) && (xe > xs)) continue;
+			if ((xi > 0) && (xe < xs)) continue;
+			if ((yi < 0) && (ye > ys)) continue;
+			if ((yi > 0) && (ye < ys)) continue;
+			xe += xi; ye += yi;
+		}
+
+		i = ksgn(ys-backy)+ksgn(xs-backx)*3+4;
+		switch(i)
+		{
+			case 6: case 7: x1 = 0; y1 = 0; break;
+			case 8: case 5: x1 = gxinc; y1 = gyinc; break;
+			case 0: case 3: x1 = gyinc; y1 = -gxinc; break;
+			case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break;
+		}
+		switch(i)
+		{
+			case 2: case 5: x2 = 0; y2 = 0; break;
+			case 0: case 1: x2 = gxinc; y2 = gyinc; break;
+			case 8: case 7: x2 = gyinc; y2 = -gxinc; break;
+			case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break;
+		}
+		oand = pow2char[(xs<backx)+0]+pow2char[(ys<backy)+2];
+		oand16 = oand+16;
+		oand32 = oand+32;
+
+		if (yi > 0) { dagxinc = gxinc; dagyinc = mulscale16(gyinc,viewingrangerecip); }
+				 else { dagxinc = -gxinc; dagyinc = -mulscale16(gyinc,viewingrangerecip); }
+
+			/* Fix for non 90 degree viewing ranges */
+		nxoff = mulscale16(x2-x1,viewingrangerecip);
+		x1 = mulscale16(x1,viewingrangerecip);
+
+		ggxstart = gxstart+ggyinc[ys];
+		ggystart = gystart-ggxinc[ys];
+
+		for(x=xs;x!=xe;x+=xi)
+		{
+			slabxoffs = (long)&davoxptr[longptr[x]];
+			shortptr = (short *)&davoxptr[((x*(daysiz+1))<<1)+xyvoxoffs];
+
+			nx = mulscale16(ggxstart+ggxinc[x],viewingrangerecip)+x1;
+			ny = ggystart+ggyinc[x];
+			for(y=ys;y!=ye;y+=yi,nx+=dagyinc,ny-=dagxinc)
+			{
+				if ((ny <= nytooclose) || (ny >= nytoofar)) continue;
+				voxptr = (char *)(shortptr[y]+slabxoffs);
+				voxend = (char *)(shortptr[y+1]+slabxoffs);
+				if (voxptr == voxend) continue;
+
+				lx = mulscale32(nx>>3,distrecip[(ny+y1)>>14])+halfxdimen;
+				if (lx < 0) lx = 0;
+				rx = mulscale32((nx+nxoff)>>3,distrecip[(ny+y2)>>14])+halfxdimen;
+				if (rx > xdimen) rx = xdimen;
+				if (rx <= lx) continue;
+				rx -= lx;
+
+				l1 = distrecip[(ny-yoff)>>14];
+				l2 = distrecip[(ny+yoff)>>14];
+				for(;voxptr<voxend;voxptr+=voxptr[1]+3)
+				{
+					j = (voxptr[0]<<15)-syoff;
+					if (j < 0)
+					{
+						k = j+(voxptr[1]<<15);
+						if (k < 0)
+						{
+							if ((voxptr[2]&oand32) == 0) continue;
+							z2 = mulscale32(l2,k) + globalhoriz;     /* Below slab */
+						}
+						else
+						{
+							if ((voxptr[2]&oand) == 0) continue;    /* Middle of slab */
+							z2 = mulscale32(l1,k) + globalhoriz;
+						}
+						z1 = mulscale32(l1,j) + globalhoriz;
+					}
+					else
+					{
+						if ((voxptr[2]&oand16) == 0) continue;
+						z1 = mulscale32(l2,j) + globalhoriz;        /* Above slab */
+						z2 = mulscale32(l1,j+(voxptr[1]<<15)) + globalhoriz;
+					}
+
+					if (voxptr[1] == 1)
+					{
+						yplc = 0; yinc = 0;
+						if (z1 < daumost[lx]) z1 = daumost[lx];
+					}
+					else
+					{
+						if (z2-z1 >= 1024) yinc = divscale16(voxptr[1],z2-z1);
+						else if (z2 > z1) yinc = (lowrecip[z2-z1]*voxptr[1]>>8);
+						if (z1 < daumost[lx]) { yplc = yinc*(daumost[lx]-z1); z1 = daumost[lx]; } else yplc = 0;
+					}
+					if (z2 > dadmost[lx]) z2 = dadmost[lx];
+					z2 -= z1; if (z2 <= 0) continue;
+
+					drawslab(rx,yplc,z2,yinc,(long)&voxptr[3],ylookup[z1]+lx+frameoffset);
+				}
+			}
+		}
+	}
+}
+#endif
+
+
+static void drawsprite (long snum)
+{
+	spritetype *tspr;
+	sectortype *sec;
+	long startum, startdm, sectnum, xb, yp, cstat;
+	long siz, xsiz, ysiz, xoff, yoff, xspan, yspan;
+	long x1, y1, x2, y2, lx, rx, dalx2, darx2, i, j, k, x, linum, linuminc;
+	long yinc, z, z1, z2, xp1, yp1, xp2, yp2;
+	long xv, yv, top, topinc, bot, botinc, hplc, hinc;
+	long cosang, sinang, dax, day, lpoint, lmax, rpoint, rmax, dax1, dax2, y;
+	long npoints, npoints2, zz, t, zsgn, zzsgn;
+	short tilenum, spritenum;
+	char swapped, daclip;
+
+    #ifdef SUPERBUILD
+        long *longptr;
+    #endif
+
+	tspr = tspriteptr[snum];
+
+	xb = spritesx[snum];
+	yp = spritesy[snum];
+	tilenum = tspr->picnum;
+	spritenum = tspr->owner;
+	cstat = tspr->cstat;
+
+	if ((cstat&48) != 48)
+	{
+		if (picanm[tilenum]&192) tilenum += animateoffs(tilenum,(short) (spritenum+32768));
+		if ((tilesizx[tilenum] <= 0) || (tilesizy[tilenum] <= 0) || (spritenum < 0))
+			return;
+	}
+	if ((tspr->xrepeat <= 0) || (tspr->yrepeat <= 0)) return;
+
+	sectnum = tspr->sectnum; sec = &sector[sectnum];
+	globalpal = tspr->pal;
+	// FIX_00088: crash on maps using a bad palette index (like the end of roch3.map)
+	if (!palookup[globalpal]) 
+		globalpal = 0; // seem to crash when globalpal > 25
+	globalshade = tspr->shade;
+	if (cstat&2)
+	{
+		if (cstat&512) settransreverse(); else settransnormal();
+	}
+
+	xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)tspr->xoffset);
+	yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)tspr->yoffset);
+
+	if ((cstat&48) == 0)
+	{
+		if (yp <= (4<<8)) return;
+
+		siz = divscale19(xdimenscale,yp);
+
+		xv = mulscale16(((long)tspr->xrepeat)<<16,xyaspect);
+
+		xspan = tilesizx[tilenum];
+		yspan = tilesizy[tilenum];
+		xsiz = mulscale30(siz,xv*xspan);
+		ysiz = mulscale14(siz,tspr->yrepeat*yspan);
+
+		if (((tilesizx[tilenum]>>11) >= xsiz) || (yspan >= (ysiz>>1)))
+			return;  /* Watch out for divscale overflow */
+
+		x1 = xb-(xsiz>>1);
+		if (xspan&1) x1 += mulscale31(siz,xv);  /* Odd xspans */
+		i = mulscale30(siz,xv*xoff);
+		if ((cstat&4) == 0) x1 -= i; else x1 += i;
+
+		y1 = mulscale16(tspr->z-globalposz,siz);
+		y1 -= mulscale14(siz,tspr->yrepeat*yoff);
+		y1 += (globalhoriz<<8)-ysiz;
+		if (cstat&128)
+		{
+			y1 += (ysiz>>1);
+			if (yspan&1) y1 += mulscale15(siz,tspr->yrepeat);  /* Odd yspans */
+		}
+
+		x2 = x1+xsiz-1;
+		y2 = y1+ysiz-1;
+		if ((y1|255) >= (y2|255)) return;
+
+		lx = (x1>>8)+1; if (lx < 0) lx = 0;
+		rx = (x2>>8); if (rx >= xdimen) rx = xdimen-1;
+		if (lx > rx) return;
+
+		yinc = divscale32(yspan,ysiz);
+
+		if ((sec->ceilingstat&3) == 0)
+			startum = globalhoriz+mulscale24(siz,sec->ceilingz-globalposz)-1;
+		else
+			startum = 0;
+		if ((sec->floorstat&3) == 0)
+			startdm = globalhoriz+mulscale24(siz,sec->floorz-globalposz)+1;
+		else
+			startdm = 0x7fffffff;
+		if ((y1>>8) > startum) startum = (y1>>8);
+		if ((y2>>8) < startdm) startdm = (y2>>8);
+
+		if (startum < -32768) startum = -32768;
+		if (startdm > 32767) startdm = 32767;
+		if (startum >= startdm) return;
+
+		if ((cstat&4) == 0)
+		{
+			linuminc = divscale24(xspan,xsiz);
+			linum = mulscale8((lx<<8)-x1,linuminc);
+		}
+		else
+		{
+			linuminc = -divscale24(xspan,xsiz);
+			linum = mulscale8((lx<<8)-x2,linuminc);
+		}
+		if ((cstat&8) > 0)
+		{
+			yinc = -yinc;
+			i = y1; y1 = y2; y2 = i;
+		}
+
+		for(x=lx;x<=rx;x++)
+		{
+			uwall[x] = max(startumost[x+windowx1]-windowy1,(short)startum);
+			dwall[x] = min(startdmost[x+windowx1]-windowy1,(short)startdm);
+		}
+		daclip = 0;
+		for(i=smostwallcnt-1;i>=0;i--)
+		{
+			if (smostwalltype[i]&daclip) continue;
+			j = smostwall[i];
+			if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
+			if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
+			if (spritewallfront(tspr,(long)thewall[j]) && ((yp <= yb1[j]) || (yp <= yb2[j]))) continue;
+
+			dalx2 = max(xb1[j],lx); darx2 = min(xb2[j],rx);
+
+			switch(smostwalltype[i])
+			{
+				case 0:
+					if (dalx2 <= darx2)
+					{
+						if ((dalx2 == lx) && (darx2 == rx)) return;
+						clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
+					}
+					break;
+				case 1:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
+					if ((dalx2 == lx) && (darx2 == rx)) daclip |= 1;
+					break;
+				case 2:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
+					if ((dalx2 == lx) && (darx2 == rx)) daclip |= 2;
+					break;
+			}
+		}
+
+		if (uwall[rx] >= dwall[rx])
+		{
+			for(x=lx;x<rx;x++)
+				if (uwall[x] < dwall[x]) break;
+			if (x == rx) return;
+		}
+
+			/* sprite */
+		if ((searchit >= 1) && (searchx >= lx) && (searchx <= rx))
+			if ((searchy >= uwall[searchx]) && (searchy < dwall[searchx]))
+			{
+				searchsector = sectnum; searchwall = spritenum;
+				searchstat = 3; searchit = 1;
+			}
+
+		z2 = tspr->z - ((yoff*tspr->yrepeat)<<2);
+		if (cstat&128)
+		{
+			z2 += ((yspan*tspr->yrepeat)<<1);
+			if (yspan&1) z2 += (tspr->yrepeat<<1);        /* Odd yspans */
+		}
+		z1 = z2 - ((yspan*tspr->yrepeat)<<2);
+
+		globalorientation = 0;
+		globalpicnum = tilenum;
+		if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+		globalxpanning = 0L;
+		globalypanning = 0L;
+		globvis = globalvisibility;
+		if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+		globalshiftval = (picsiz[globalpicnum]>>4);
+		if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+		globalshiftval = 32-globalshiftval;
+		globalyscale = divscale(512,tspr->yrepeat,globalshiftval-19);
+		globalzd = (((globalposz-z1)*globalyscale)<<8);
+		if ((cstat&8) > 0)
+		{
+			globalyscale = -globalyscale;
+			globalzd = (((globalposz-z2)*globalyscale)<<8);
+		}
+
+		qinterpolatedown16((long *)&lwall[lx],rx-lx+1,linum,linuminc);
+		clearbuf(&swall[lx],rx-lx+1,mulscale19(yp,xdimscale));
+
+		if ((cstat&2) == 0)
+			maskwallscan(lx,rx,uwall,dwall,swall,lwall);
+		else
+			transmaskwallscan(lx,rx);
+	}
+	else if ((cstat&48) == 16)
+	{
+		if ((cstat&4) > 0) xoff = -xoff;
+		if ((cstat&8) > 0) yoff = -yoff;
+
+		xspan = tilesizx[tilenum]; yspan = tilesizy[tilenum];
+		xv = tspr->xrepeat*sintable[(tspr->ang+2560+1536)&2047];
+		yv = tspr->xrepeat*sintable[(tspr->ang+2048+1536)&2047];
+		i = (xspan>>1)+xoff;
+		x1 = tspr->x-globalposx-mulscale16(xv,i); x2 = x1+mulscale16(xv,xspan);
+		y1 = tspr->y-globalposy-mulscale16(yv,i); y2 = y1+mulscale16(yv,xspan);
+
+		yp1 = dmulscale6(x1,cosviewingrangeglobalang,y1,sinviewingrangeglobalang);
+		yp2 = dmulscale6(x2,cosviewingrangeglobalang,y2,sinviewingrangeglobalang);
+		if ((yp1 <= 0) && (yp2 <= 0)) return;
+		xp1 = dmulscale6(y1,cosglobalang,-x1,singlobalang);
+		xp2 = dmulscale6(y2,cosglobalang,-x2,singlobalang);
+
+		x1 += globalposx; y1 += globalposy;
+		x2 += globalposx; y2 += globalposy;
+
+		swapped = 0;
+		if (dmulscale32(xp1,yp2,-xp2,yp1) >= 0)  /* If wall's NOT facing you */
+		{
+			if ((cstat&64) != 0) return;
+			i = xp1, xp1 = xp2, xp2 = i;
+			i = yp1, yp1 = yp2, yp2 = i;
+			i = x1, x1 = x2, x2 = i;
+			i = y1, y1 = y2, y2 = i;
+			swapped = 1;
+		}
+
+		if (xp1 >= -yp1)
+		{
+			if (xp1 > yp1) return;
+
+			if (yp1 == 0) return;
+			xb1[MAXWALLSB-1] = halfxdimen + scale(xp1,halfxdimen,yp1);
+			if (xp1 >= 0) xb1[MAXWALLSB-1]++;   /* Fix for SIGNED divide */
+			if (xb1[MAXWALLSB-1] >= xdimen) xb1[MAXWALLSB-1] = xdimen-1;
+			yb1[MAXWALLSB-1] = yp1;
+		}
+		else
+		{
+			if (xp2 < -yp2) return;
+			xb1[MAXWALLSB-1] = 0;
+			i = yp1-yp2+xp1-xp2;
+			if (i == 0) return;
+			yb1[MAXWALLSB-1] = yp1 + scale(yp2-yp1,xp1+yp1,i);
+		}
+		if (xp2 <= yp2)
+		{
+			if (xp2 < -yp2) return;
+
+			if (yp2 == 0) return;
+			xb2[MAXWALLSB-1] = halfxdimen + scale(xp2,halfxdimen,yp2) - 1;
+			if (xp2 >= 0) xb2[MAXWALLSB-1]++;   /* Fix for SIGNED divide */
+			if (xb2[MAXWALLSB-1] >= xdimen) xb2[MAXWALLSB-1] = xdimen-1;
+			yb2[MAXWALLSB-1] = yp2;
+		}
+		else
+		{
+			if (xp1 > yp1) return;
+
+			xb2[MAXWALLSB-1] = xdimen-1;
+			i = xp2-xp1+yp1-yp2;
+			if (i == 0) return;
+			yb2[MAXWALLSB-1] = yp1 + scale(yp2-yp1,yp1-xp1,i);
+		}
+
+		if ((yb1[MAXWALLSB-1] < 256) || (yb2[MAXWALLSB-1] < 256) || (xb1[MAXWALLSB-1] > xb2[MAXWALLSB-1]))
+			return;
+
+		topinc = -mulscale10(yp1,xspan);
+		top = (((mulscale10(xp1,xdimen) - mulscale9(xb1[MAXWALLSB-1]-halfxdimen,yp1))*xspan)>>3);
+		botinc = ((yp2-yp1)>>8);
+		bot = mulscale11(xp1-xp2,xdimen) + mulscale2(xb1[MAXWALLSB-1]-halfxdimen,botinc);
+
+		j = xb2[MAXWALLSB-1]+3;
+		z = mulscale20(top,krecipasm(bot));
+		lwall[xb1[MAXWALLSB-1]] = (z>>8);
+		for(x=xb1[MAXWALLSB-1]+4;x<=j;x+=4)
+		{
+			top += topinc; bot += botinc;
+			zz = z; z = mulscale20(top,krecipasm(bot));
+			lwall[x] = (z>>8);
+			i = ((z+zz)>>1);
+			lwall[x-2] = (i>>8);
+			lwall[x-3] = ((i+zz)>>9);
+			lwall[x-1] = ((i+z)>>9);
+		}
+
+		if (lwall[xb1[MAXWALLSB-1]] < 0) lwall[xb1[MAXWALLSB-1]] = 0;
+		if (lwall[xb2[MAXWALLSB-1]] >= xspan) lwall[xb2[MAXWALLSB-1]] = xspan-1;
+
+		if ((swapped^((cstat&4)>0)) > 0)
+		{
+			j = xspan-1;
+			for(x=xb1[MAXWALLSB-1];x<=xb2[MAXWALLSB-1];x++)
+				lwall[x] = j-lwall[x];
+		}
+
+		rx1[MAXWALLSB-1] = xp1; ry1[MAXWALLSB-1] = yp1;
+		rx2[MAXWALLSB-1] = xp2; ry2[MAXWALLSB-1] = yp2;
+
+		hplc = divscale19(xdimenscale,yb1[MAXWALLSB-1]);
+		hinc = divscale19(xdimenscale,yb2[MAXWALLSB-1]);
+		hinc = (hinc-hplc)/(xb2[MAXWALLSB-1]-xb1[MAXWALLSB-1]+1);
+
+		z2 = tspr->z - ((yoff*tspr->yrepeat)<<2);
+		if (cstat&128)
+		{
+			z2 += ((yspan*tspr->yrepeat)<<1);
+			if (yspan&1) z2 += (tspr->yrepeat<<1);        /* Odd yspans */
+		}
+		z1 = z2 - ((yspan*tspr->yrepeat)<<2);
+
+		globalorientation = 0;
+		globalpicnum = tilenum;
+		if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+		globalxpanning = 0L;
+		globalypanning = 0L;
+		globvis = globalvisibility;
+		if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+		globalshiftval = (picsiz[globalpicnum]>>4);
+		if (pow2long[globalshiftval] != tilesizy[globalpicnum]) globalshiftval++;
+		globalshiftval = 32-globalshiftval;
+		globalyscale = divscale(512,tspr->yrepeat,globalshiftval-19);
+		globalzd = (((globalposz-z1)*globalyscale)<<8);
+		if ((cstat&8) > 0)
+		{
+			globalyscale = -globalyscale;
+			globalzd = (((globalposz-z2)*globalyscale)<<8);
+		}
+
+		if (((sec->ceilingstat&1) == 0) && (z1 < sec->ceilingz))
+			z1 = sec->ceilingz;
+		if (((sec->floorstat&1) == 0) && (z2 > sec->floorz))
+			z2 = sec->floorz;
+
+		owallmost(uwall,(long)(MAXWALLSB-1),z1-globalposz);
+		owallmost(dwall,(long)(MAXWALLSB-1),z2-globalposz);
+		for(i=xb1[MAXWALLSB-1];i<=xb2[MAXWALLSB-1];i++)
+			{ swall[i] = (krecipasm(hplc)<<2); hplc += hinc; }
+
+		for(i=smostwallcnt-1;i>=0;i--)
+		{
+			j = smostwall[i];
+
+			if ((xb1[j] > xb2[MAXWALLSB-1]) || (xb2[j] < xb1[MAXWALLSB-1])) continue;
+
+			dalx2 = xb1[j]; darx2 = xb2[j];
+			if (max(yb1[MAXWALLSB-1],yb2[MAXWALLSB-1]) > min(yb1[j],yb2[j]))
+			{
+				if (min(yb1[MAXWALLSB-1],yb2[MAXWALLSB-1]) > max(yb1[j],yb2[j]))
+				{
+					x = 0x80000000;
+				}
+				else
+				{
+					x = thewall[j]; xp1 = wall[x].x; yp1 = wall[x].y;
+					x = wall[x].point2; xp2 = wall[x].x; yp2 = wall[x].y;
+
+					z1 = (xp2-xp1)*(y1-yp1) - (yp2-yp1)*(x1-xp1);
+					z2 = (xp2-xp1)*(y2-yp1) - (yp2-yp1)*(x2-xp1);
+					if ((z1^z2) >= 0)
+						x = (z1+z2);
+					else
+					{
+						z1 = (x2-x1)*(yp1-y1) - (y2-y1)*(xp1-x1);
+						z2 = (x2-x1)*(yp2-y1) - (y2-y1)*(xp2-x1);
+
+						if ((z1^z2) >= 0)
+							x = -(z1+z2);
+						else
+						{
+							if ((xp2-xp1)*(tspr->y-yp1) == (tspr->x-xp1)*(yp2-yp1))
+							{
+								if (wall[thewall[j]].nextsector == tspr->sectnum)
+									x = 0x80000000;
+								else
+									x = 0x7fffffff;
+							}
+							else
+							{     /* INTERSECTION! */
+								x = (xp1-globalposx) + scale(xp2-xp1,z1,z1-z2);
+								y = (yp1-globalposy) + scale(yp2-yp1,z1,z1-z2);
+
+								yp1 = dmulscale14(x,cosglobalang,y,singlobalang);
+								if (yp1 > 0)
+								{
+									xp1 = dmulscale14(y,cosglobalang,-x,singlobalang);
+
+									x = halfxdimen + scale(xp1,halfxdimen,yp1);
+									if (xp1 >= 0) x++;   /* Fix for SIGNED divide */
+
+									if (z1 < 0)
+										{ if (dalx2 < x) dalx2 = x; }
+									else
+										{ if (darx2 > x) darx2 = x; }
+									x = 0x80000001;
+								}
+								else
+									x = 0x7fffffff;
+							}
+						}
+					}
+				}
+				if (x < 0)
+				{
+					if (dalx2 < xb1[MAXWALLSB-1]) dalx2 = xb1[MAXWALLSB-1];
+					if (darx2 > xb2[MAXWALLSB-1]) darx2 = xb2[MAXWALLSB-1];
+					switch(smostwalltype[i])
+					{
+						case 0:
+							if (dalx2 <= darx2)
+							{
+								if ((dalx2 == xb1[MAXWALLSB-1]) && (darx2 == xb2[MAXWALLSB-1])) return;
+								clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
+							}
+							break;
+						case 1:
+							k = smoststart[i] - xb1[j];
+							for(x=dalx2;x<=darx2;x++)
+								if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
+							break;
+						case 2:
+							k = smoststart[i] - xb1[j];
+							for(x=dalx2;x<=darx2;x++)
+								if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
+							break;
+					}
+				}
+			}
+		}
+
+			/* sprite */
+		if ((searchit >= 1) && (searchx >= xb1[MAXWALLSB-1]) && (searchx <= xb2[MAXWALLSB-1]))
+			if ((searchy >= uwall[searchx]) && (searchy <= dwall[searchx]))
+			{
+				searchsector = sectnum; searchwall = spritenum;
+				searchstat = 3; searchit = 1;
+			}
+
+		if ((cstat&2) == 0)
+			maskwallscan(xb1[MAXWALLSB-1],xb2[MAXWALLSB-1],uwall,dwall,swall,lwall);
+		else
+			transmaskwallscan(xb1[MAXWALLSB-1],xb2[MAXWALLSB-1]);
+	}
+	else if ((cstat&48) == 32)
+	{
+		if ((cstat&64) != 0)
+			if ((globalposz > tspr->z) == ((cstat&8)==0))
+				return;
+
+		if ((cstat&4) > 0) xoff = -xoff;
+		if ((cstat&8) > 0) yoff = -yoff;
+		xspan = tilesizx[tilenum];
+		yspan = tilesizy[tilenum];
+
+			/* Rotate center point */
+		dax = tspr->x-globalposx;
+		day = tspr->y-globalposy;
+		rzi[0] = dmulscale10(cosglobalang,dax,singlobalang,day);
+		rxi[0] = dmulscale10(cosglobalang,day,-singlobalang,dax);
+
+			/* Get top-left corner */
+		i = ((tspr->ang+2048-globalang)&2047);
+		cosang = sintable[(i+512)&2047]; sinang = sintable[i];
+		dax = ((xspan>>1)+xoff)*tspr->xrepeat;
+		day = ((yspan>>1)+yoff)*tspr->yrepeat;
+		rzi[0] += dmulscale12(sinang,dax,cosang,day);
+		rxi[0] += dmulscale12(sinang,day,-cosang,dax);
+
+			/* Get other 3 corners */
+		dax = xspan*tspr->xrepeat;
+		day = yspan*tspr->yrepeat;
+		rzi[1] = rzi[0]-mulscale12(sinang,dax);
+		rxi[1] = rxi[0]+mulscale12(cosang,dax);
+		dax = -mulscale12(cosang,day);
+		day = -mulscale12(sinang,day);
+		rzi[2] = rzi[1]+dax; rxi[2] = rxi[1]+day;
+		rzi[3] = rzi[0]+dax; rxi[3] = rxi[0]+day;
+
+			/* Put all points on same z */
+		ryi[0] = scale((tspr->z-globalposz),yxaspect,320<<8);
+		if (ryi[0] == 0) return;
+		ryi[1] = ryi[2] = ryi[3] = ryi[0];
+
+		if ((cstat&4) == 0)
+			{ z = 0; z1 = 1; z2 = 3; }
+		else
+			{ z = 1; z1 = 0; z2 = 2; }
+
+		dax = rzi[z1]-rzi[z]; day = rxi[z1]-rxi[z];
+		bot = dmulscale8(dax,dax,day,day);
+		if (((klabs(dax)>>13) >= bot) || ((klabs(day)>>13) >= bot)) return;
+		globalx1 = divscale18(dax,bot);
+		globalx2 = divscale18(day,bot);
+
+		dax = rzi[z2]-rzi[z]; day = rxi[z2]-rxi[z];
+		bot = dmulscale8(dax,dax,day,day);
+		if (((klabs(dax)>>13) >= bot) || ((klabs(day)>>13) >= bot)) return;
+		globaly1 = divscale18(dax,bot);
+		globaly2 = divscale18(day,bot);
+
+			/* Calculate globals for hline texture mapping function */
+		globalxpanning = (rxi[z]<<12);
+		globalypanning = (rzi[z]<<12);
+		globalzd = (ryi[z]<<12);
+
+		rzi[0] = mulscale16(rzi[0],viewingrange);
+		rzi[1] = mulscale16(rzi[1],viewingrange);
+		rzi[2] = mulscale16(rzi[2],viewingrange);
+		rzi[3] = mulscale16(rzi[3],viewingrange);
+
+		if (ryi[0] < 0)   /* If ceilsprite is above you, reverse order of points */
+		{
+			i = rxi[1]; rxi[1] = rxi[3]; rxi[3] = i;
+			i = rzi[1]; rzi[1] = rzi[3]; rzi[3] = i;
+		}
+
+
+			/* Clip polygon in 3-space */
+		npoints = 4;
+
+			/* Clip edge 1 */
+		npoints2 = 0;
+		zzsgn = rxi[0]+rzi[0];
+		for(z=0;z<npoints;z++)
+		{
+			zz = z+1; if (zz == npoints) zz = 0;
+			zsgn = zzsgn; zzsgn = rxi[zz]+rzi[zz];
+			if (zsgn >= 0)
+			{
+				rxi2[npoints2] = rxi[z]; ryi2[npoints2] = ryi[z]; rzi2[npoints2] = rzi[z];
+				npoints2++;
+			}
+			if ((zsgn^zzsgn) < 0)
+			{
+				t = divscale30(zsgn,zsgn-zzsgn);
+				rxi2[npoints2] = rxi[z] + mulscale30(t,rxi[zz]-rxi[z]);
+				ryi2[npoints2] = ryi[z] + mulscale30(t,ryi[zz]-ryi[z]);
+				rzi2[npoints2] = rzi[z] + mulscale30(t,rzi[zz]-rzi[z]);
+				npoints2++;
+			}
+		}
+		if (npoints2 <= 2) return;
+
+			/* Clip edge 2 */
+		npoints = 0;
+		zzsgn = rxi2[0]-rzi2[0];
+		for(z=0;z<npoints2;z++)
+		{
+			zz = z+1; if (zz == npoints2) zz = 0;
+			zsgn = zzsgn; zzsgn = rxi2[zz]-rzi2[zz];
+			if (zsgn <= 0)
+			{
+				rxi[npoints] = rxi2[z]; ryi[npoints] = ryi2[z]; rzi[npoints] = rzi2[z];
+				npoints++;
+			}
+			if ((zsgn^zzsgn) < 0)
+			{
+				t = divscale30(zsgn,zsgn-zzsgn);
+				rxi[npoints] = rxi2[z] + mulscale30(t,rxi2[zz]-rxi2[z]);
+				ryi[npoints] = ryi2[z] + mulscale30(t,ryi2[zz]-ryi2[z]);
+				rzi[npoints] = rzi2[z] + mulscale30(t,rzi2[zz]-rzi2[z]);
+				npoints++;
+			}
+		}
+		if (npoints <= 2) return;
+
+			/* Clip edge 3 */
+		npoints2 = 0;
+		zzsgn = ryi[0]*halfxdimen + (rzi[0]*(globalhoriz-0));
+		for(z=0;z<npoints;z++)
+		{
+			zz = z+1; if (zz == npoints) zz = 0;
+			zsgn = zzsgn; zzsgn = ryi[zz]*halfxdimen + (rzi[zz]*(globalhoriz-0));
+			if (zsgn >= 0)
+			{
+				rxi2[npoints2] = rxi[z];
+				ryi2[npoints2] = ryi[z];
+				rzi2[npoints2] = rzi[z];
+				npoints2++;
+			}
+			if ((zsgn^zzsgn) < 0)
+			{
+				t = divscale30(zsgn,zsgn-zzsgn);
+				rxi2[npoints2] = rxi[z] + mulscale30(t,rxi[zz]-rxi[z]);
+				ryi2[npoints2] = ryi[z] + mulscale30(t,ryi[zz]-ryi[z]);
+				rzi2[npoints2] = rzi[z] + mulscale30(t,rzi[zz]-rzi[z]);
+				npoints2++;
+			}
+		}
+		if (npoints2 <= 2) return;
+
+			/* Clip edge 4 */
+		npoints = 0;
+		zzsgn = ryi2[0]*halfxdimen + (rzi2[0]*(globalhoriz-ydimen));
+		for(z=0;z<npoints2;z++)
+		{
+			zz = z+1; if (zz == npoints2) zz = 0;
+			zsgn = zzsgn; zzsgn = ryi2[zz]*halfxdimen + (rzi2[zz]*(globalhoriz-ydimen));
+			if (zsgn <= 0)
+			{
+				rxi[npoints] = rxi2[z];
+				ryi[npoints] = ryi2[z];
+				rzi[npoints] = rzi2[z];
+				npoints++;
+			}
+			if ((zsgn^zzsgn) < 0)
+			{
+				t = divscale30(zsgn,zsgn-zzsgn);
+				rxi[npoints] = rxi2[z] + mulscale30(t,rxi2[zz]-rxi2[z]);
+				ryi[npoints] = ryi2[z] + mulscale30(t,ryi2[zz]-ryi2[z]);
+				rzi[npoints] = rzi2[z] + mulscale30(t,rzi2[zz]-rzi2[z]);
+				npoints++;
+			}
+		}
+		if (npoints <= 2) return;
+
+			/* Project onto screen */
+		lpoint = -1; lmax = 0x7fffffff;
+		rpoint = -1; rmax = 0x80000000;
+		for(z=0;z<npoints;z++)
+		{
+			xsi[z] = scale(rxi[z],xdimen<<15,rzi[z]) + (xdimen<<15);
+			ysi[z] = scale(ryi[z],xdimen<<15,rzi[z]) + (globalhoriz<<16);
+			if (xsi[z] < 0) xsi[z] = 0;
+			if (xsi[z] > (xdimen<<16)) xsi[z] = (xdimen<<16);
+			if (ysi[z] < ((long)0<<16)) ysi[z] = ((long)0<<16);
+			if (ysi[z] > ((long)ydimen<<16)) ysi[z] = ((long)ydimen<<16);
+			if (xsi[z] < lmax) lmax = xsi[z], lpoint = z;
+			if (xsi[z] > rmax) rmax = xsi[z], rpoint = z;
+		}
+
+			/* Get uwall arrays */
+		for(z=lpoint;z!=rpoint;z=zz)
+		{
+			zz = z+1; if (zz == npoints) zz = 0;
+
+			dax1 = ((xsi[z]+65535)>>16);
+			dax2 = ((xsi[zz]+65535)>>16);
+			if (dax2 > dax1)
+			{
+				yinc = divscale16(ysi[zz]-ysi[z],xsi[zz]-xsi[z]);
+				y = ysi[z] + mulscale16((dax1<<16)-xsi[z],yinc);
+				qinterpolatedown16short((long *)(&uwall[dax1]),dax2-dax1,y,yinc);
+			}
+		}
+
+			/* Get dwall arrays */
+		for(;z!=lpoint;z=zz)
+		{
+			zz = z+1; if (zz == npoints) zz = 0;
+
+			dax1 = ((xsi[zz]+65535)>>16);
+			dax2 = ((xsi[z]+65535)>>16);
+			if (dax2 > dax1)
+			{
+				yinc = divscale16(ysi[zz]-ysi[z],xsi[zz]-xsi[z]);
+				y = ysi[zz] + mulscale16((dax1<<16)-xsi[zz],yinc);
+				qinterpolatedown16short((long *)(&dwall[dax1]),dax2-dax1,y,yinc);
+			}
+		}
+
+
+		lx = ((lmax+65535)>>16);
+		rx = ((rmax+65535)>>16);
+		for(x=lx;x<=rx;x++)
+		{
+			uwall[x] = max(uwall[x],startumost[x+windowx1]-windowy1);
+			dwall[x] = min(dwall[x],startdmost[x+windowx1]-windowy1);
+		}
+
+			/* Additional uwall/dwall clipping goes here */
+		for(i=smostwallcnt-1;i>=0;i--)
+		{
+			j = smostwall[i];
+			if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
+			if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
+
+				/* if (spritewallfront(tspr,thewall[j]) == 0) */
+			x = thewall[j]; xp1 = wall[x].x; yp1 = wall[x].y;
+			x = wall[x].point2; xp2 = wall[x].x; yp2 = wall[x].y;
+			x = (xp2-xp1)*(tspr->y-yp1)-(tspr->x-xp1)*(yp2-yp1);
+			if ((yp > yb1[j]) && (yp > yb2[j])) x = -1;
+			if ((x >= 0) && ((x != 0) || (wall[thewall[j]].nextsector != tspr->sectnum))) continue;
+
+			dalx2 = max(xb1[j],lx); darx2 = min(xb2[j],rx);
+
+			switch(smostwalltype[i])
+			{
+				case 0:
+					if (dalx2 <= darx2)
+					{
+						if ((dalx2 == lx) && (darx2 == rx)) return;
+						clearbufbyte(&dwall[dalx2],(darx2-dalx2+1)*sizeof(dwall[0]),0L);
+					}
+					break;
+				case 1:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] > uwall[x]) uwall[x] = smost[k+x];
+					break;
+				case 2:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] < dwall[x]) dwall[x] = smost[k+x];
+					break;
+			}
+		}
+
+			/* sprite */
+		if ((searchit >= 1) && (searchx >= lx) && (searchx <= rx))
+			if ((searchy >= uwall[searchx]) && (searchy <= dwall[searchx]))
+			{
+				searchsector = sectnum; searchwall = spritenum;
+				searchstat = 3; searchit = 1;
+			}
+
+		globalorientation = cstat;
+		globalpicnum = tilenum;
+		if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+		/* if (picanm[globalpicnum]&192) globalpicnum += animateoffs((short)globalpicnum,spritenum+32768); */
+
+		if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+		setgotpic(globalpicnum);
+		globalbufplc = waloff[globalpicnum];
+
+		globvis = mulscale16(globalhisibility,viewingrange);
+		if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+
+		x = picsiz[globalpicnum]; y = ((x>>4)&15); x &= 15;
+		if (pow2long[x] != xspan)
+		{
+			x++;
+			globalx1 = mulscale(globalx1,xspan,x);
+			globalx2 = mulscale(globalx2,xspan,x);
+		}
+
+		dax = globalxpanning; day = globalypanning;
+		globalxpanning = -dmulscale6(globalx1,day,globalx2,dax);
+		globalypanning = -dmulscale6(globaly1,day,globaly2,dax);
+
+		globalx2 = mulscale16(globalx2,viewingrange);
+		globaly2 = mulscale16(globaly2,viewingrange);
+		globalzd = mulscale16(globalzd,viewingrangerecip);
+
+		globalx1 = (globalx1-globalx2)*halfxdimen;
+		globaly1 = (globaly1-globaly2)*halfxdimen;
+
+		if ((cstat&2) == 0)
+			msethlineshift(x,y);
+		else
+			tsethlineshift(x,y);
+
+			/* Draw it! */
+		ceilspritescan(lx,rx-1);
+	}
+#ifdef SUPERBUILD
+	else if ((cstat&48) == 48)
+	{
+		lx = 0; rx = xdim-1;
+		for(x=lx;x<=rx;x++)
+		{
+			lwall[x] = (long)startumost[x+windowx1]-windowy1;
+			swall[x] = (long)startdmost[x+windowx1]-windowy1;
+		}
+		for(i=smostwallcnt-1;i>=0;i--)
+		{
+			j = smostwall[i];
+			if ((xb1[j] > rx) || (xb2[j] < lx)) continue;
+			if ((yp <= yb1[j]) && (yp <= yb2[j])) continue;
+			if (spritewallfront(tspr,(long)thewall[j]) && ((yp <= yb1[j]) || (yp <= yb2[j]))) continue;
+
+			dalx2 = max(xb1[j],lx); darx2 = min(xb2[j],rx);
+
+			switch(smostwalltype[i])
+			{
+				case 0:
+					if (dalx2 <= darx2)
+					{
+						if ((dalx2 == lx) && (darx2 == rx)) return;
+							clearbufbyte(&swall[dalx2],(darx2-dalx2+1)*sizeof(swall[0]),0L);
+					}
+					break;
+				case 1:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] > lwall[x]) lwall[x] = smost[k+x];
+					break;
+				case 2:
+					k = smoststart[i] - xb1[j];
+					for(x=dalx2;x<=darx2;x++)
+						if (smost[k+x] < swall[x]) swall[x] = smost[k+x];
+					break;
+			}
+		}
+
+		if (lwall[rx] >= swall[rx])
+		{
+			for(x=lx;x<rx;x++)
+				if (lwall[x] < swall[x]) break;
+			if (x == rx) return;
+		}
+
+		for(i=0;i<MAXVOXMIPS;i++)
+			if (!voxoff[tilenum][i])
+			{
+				kloadvoxel(tilenum);
+				break;
+			}
+
+		longptr = (long *)voxoff[tilenum][0];
+		//if (!(cstat&128)) tspr->z -= mulscale6(longptr[5],(long)tspr->yrepeat);
+		if (!(cstat&128))
+        {
+#pragma message ("[Crashbug: commenting out for now]")
+			// crashes 2nd demo of atomic, when pig jumps in hole.
+            tspr->z -= mulscale6(longptr[5],(long)tspr->yrepeat);
+        }
+		yoff = (long)((signed char)((picanm[sprite[tspr->owner].picnum]>>16)&255))+((long)tspr->yoffset);
+		tspr->z -= ((yoff*tspr->yrepeat)<<2);
+
+		globvis = globalvisibility;
+		if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+
+		if ((searchit >= 1) && (yp > (4<<8)) && (searchy >= lwall[searchx]) && (searchy < swall[searchx]))
+		{
+			siz = divscale19(xdimenscale,yp);
+
+			xv = mulscale16(((long)tspr->xrepeat)<<16,xyaspect);
+
+			xspan = ((longptr[0]+longptr[1])>>1);
+			yspan = longptr[2];
+			xsiz = mulscale30(siz,xv*xspan);
+			ysiz = mulscale14(siz,tspr->yrepeat*yspan);
+
+				/* Watch out for divscale overflow */
+			if (((xspan>>11) < xsiz) && (yspan < (ysiz>>1)))
+			{
+				x1 = xb-(xsiz>>1);
+				if (xspan&1) x1 += mulscale31(siz,xv);  /* Odd xspans */
+				i = mulscale30(siz,xv*xoff);
+				if ((cstat&4) == 0) x1 -= i; else x1 += i;
+
+				y1 = mulscale16(tspr->z-globalposz,siz);
+				/*y1 -= mulscale14(siz,tspr->yrepeat*yoff);*/
+				y1 += (globalhoriz<<8)-ysiz;
+				/*if (cstat&128)  //Already fixed up above */
+				y1 += (ysiz>>1);
+
+				x2 = x1+xsiz-1;
+				y2 = y1+ysiz-1;
+				if (((y1|255) < (y2|255)) && (searchx >= (x1>>8)+1) && (searchx <= (x2>>8)))
+				{
+					if ((sec->ceilingstat&3) == 0)
+						startum = globalhoriz+mulscale24(siz,sec->ceilingz-globalposz)-1;
+					else
+						startum = 0;
+					if ((sec->floorstat&3) == 0)
+						startdm = globalhoriz+mulscale24(siz,sec->floorz-globalposz)+1;
+					else
+						startdm = 0x7fffffff;
+
+						/* sprite */
+					if ((searchy >= max(startum,(y1>>8))) && (searchy < min(startdm,(y2>>8))))
+					{
+						searchsector = sectnum; searchwall = spritenum;
+						searchstat = 3; searchit = 1;
+					}
+				}
+			}
+		}
+
+		drawvox(tspr->x,tspr->y,tspr->z,(long)tspr->ang+1536,(long)tspr->xrepeat,(long)tspr->yrepeat,tilenum,tspr->shade,tspr->pal,lwall,swall);
+	}
+#endif
+	if (automapping == 1) show2dsprite[spritenum>>3] |= pow2char[spritenum&7];
+}
+
+
+void drawmasks(void)
+{
+	long i, j, k, l, gap, xs, ys, xp, yp, yoff, yspan;
+	/* long zs, zp; */
+
+	for(i=spritesortcnt-1;i>=0;i--) tspriteptr[i] = &tsprite[i];
+	for(i=spritesortcnt-1;i>=0;i--)
+	{
+		xs = tspriteptr[i]->x-globalposx; ys = tspriteptr[i]->y-globalposy;
+		yp = dmulscale6(xs,cosviewingrangeglobalang,ys,sinviewingrangeglobalang);
+		if (yp > (4<<8))
+		{
+			xp = dmulscale6(ys,cosglobalang,-xs,singlobalang);
+			spritesx[i] = scale(xp+yp,xdimen<<7,yp);
+		}
+		else if ((tspriteptr[i]->cstat&48) == 0)
+		{
+			spritesortcnt--;  /* Delete face sprite if on wrong side! */
+			if (i != spritesortcnt)
+			{
+				tspriteptr[i] = tspriteptr[spritesortcnt];
+				spritesx[i] = spritesx[spritesortcnt];
+				spritesy[i] = spritesy[spritesortcnt];
+			}
+			continue;
+		}
+		spritesy[i] = yp;
+	}
+
+	gap = 1; while (gap < spritesortcnt) gap = (gap<<1)+1;
+	for(gap>>=1;gap>0;gap>>=1)      /* Sort sprite list */
+		for(i=0;i<spritesortcnt-gap;i++)
+			for(l=i;l>=0;l-=gap)
+			{
+				if (spritesy[l] <= spritesy[l+gap]) break;
+				swaplong((long *)&tspriteptr[l],(long *)&tspriteptr[l+gap]);
+				swaplong(&spritesx[l],&spritesx[l+gap]);
+				swaplong(&spritesy[l],&spritesy[l+gap]);
+			}
+
+	if (spritesortcnt > 0)
+		spritesy[spritesortcnt] = (spritesy[spritesortcnt-1]^1);
+
+	ys = spritesy[0]; i = 0;
+	for(j=1;j<=spritesortcnt;j++)
+	{
+		if (spritesy[j] == ys) continue;
+		ys = spritesy[j];
+		if (j > i+1)
+		{
+			for(k=i;k<j;k++)
+			{
+				spritesz[k] = tspriteptr[k]->z;
+				if ((tspriteptr[k]->cstat&48) != 32)
+				{
+					yoff = (long)((signed char)((picanm[tspriteptr[k]->picnum]>>16)&255))+((long)tspriteptr[k]->yoffset);
+					spritesz[k] -= ((yoff*tspriteptr[k]->yrepeat)<<2);
+					yspan = (tilesizy[tspriteptr[k]->picnum]*tspriteptr[k]->yrepeat<<2);
+					if (!(tspriteptr[k]->cstat&128)) spritesz[k] -= (yspan>>1);
+					if (klabs(spritesz[k]-globalposz) < (yspan>>1)) spritesz[k] = globalposz;
+				}
+			}
+			for(k=i+1;k<j;k++)
+				for(l=i;l<k;l++)
+					if (klabs(spritesz[k]-globalposz) < klabs(spritesz[l]-globalposz))
+					{
+						swaplong((long *)&tspriteptr[k],(long *)&tspriteptr[l]);
+						swaplong(&spritesx[k],&spritesx[l]);
+						swaplong(&spritesy[k],&spritesy[l]);
+						swaplong(&spritesz[k],&spritesz[l]);
+					}
+			for(k=i+1;k<j;k++)
+				for(l=i;l<k;l++)
+					if (tspriteptr[k]->statnum < tspriteptr[l]->statnum)
+					{
+						swaplong((long *)&tspriteptr[k],(long *)&tspriteptr[l]);
+						swaplong(&spritesx[k],&spritesx[l]);
+						swaplong(&spritesy[k],&spritesy[l]);
+					}
+		}
+		i = j;
+	}
+
+	/*for(i=spritesortcnt-1;i>=0;i--)
+	{
+		xs = tspriteptr[i].x-globalposx;
+		ys = tspriteptr[i].y-globalposy;
+		zs = tspriteptr[i].z-globalposz;
+
+		xp = ys*cosglobalang-xs*singlobalang;
+		yp = (zs<<1);
+		zp = xs*cosglobalang+ys*singlobalang;
+
+		xs = scale(xp,halfxdimen<<12,zp)+((halfxdimen+windowx1)<<12);
+		ys = scale(yp,xdimenscale<<12,zp)+((globalhoriz+windowy1)<<12);
+
+		drawline256(xs-65536,ys-65536,xs+65536,ys+65536,31);
+		drawline256(xs+65536,ys-65536,xs-65536,ys+65536,31);
+	}*/
+
+	while ((spritesortcnt > 0) && (maskwallcnt > 0))  /* While BOTH > 0 */
+	{
+		j = maskwall[maskwallcnt-1];
+		if (spritewallfront(tspriteptr[spritesortcnt-1],(long)thewall[j]) == 0)
+			drawsprite(--spritesortcnt);
+		else
+		{
+				/* Check to see if any sprites behind the masked wall... */
+			k = -1;
+			gap = 0;
+			for(i=spritesortcnt-2;i>=0;i--)
+				if ((xb1[j] <= (spritesx[i]>>8)) && ((spritesx[i]>>8) <= xb2[j]))
+					if (spritewallfront(tspriteptr[i],(long)thewall[j]) == 0)
+					{
+						drawsprite(i);
+						tspriteptr[i]->owner = -1;
+						k = i;
+						gap++;
+					}
+			if (k >= 0)       /* remove holes in sprite list */
+			{
+				for(i=k;i<spritesortcnt;i++)
+					if (tspriteptr[i]->owner >= 0)
+					{
+						if (i > k)
+						{
+							tspriteptr[k] = tspriteptr[i];
+							spritesx[k] = spritesx[i];
+							spritesy[k] = spritesy[i];
+						}
+						k++;
+					}
+				spritesortcnt -= gap;
+			}
+
+				/* finally safe to draw the masked wall */
+			drawmaskwall(--maskwallcnt);
+		}
+	}
+	while (spritesortcnt > 0) drawsprite(--spritesortcnt);
+	while (maskwallcnt > 0) drawmaskwall(--maskwallcnt);
+}
+
+
+int setsprite(short spritenum, long newx, long newy, long newz)
+{
+	short tempsectnum;
+
+	sprite[spritenum].x = newx;
+	sprite[spritenum].y = newy;
+	sprite[spritenum].z = newz;
+
+	tempsectnum = sprite[spritenum].sectnum;
+	updatesector(newx,newy,&tempsectnum);
+	if (tempsectnum < 0)
+		return(-1);
+	if (tempsectnum != sprite[spritenum].sectnum)
+		changespritesect(spritenum,tempsectnum);
+
+	return(0);
+}
+
+
+void initspritelists(void)
+{
+	long i;
+
+	for (i=0;i<MAXSECTORS;i++)     /* Init doubly-linked sprite sector lists */
+		headspritesect[i] = -1;
+	headspritesect[MAXSECTORS] = 0;
+	for(i=0;i<MAXSPRITES;i++)
+	{
+		prevspritesect[i] = i-1;
+		nextspritesect[i] = i+1;
+		sprite[i].sectnum = MAXSECTORS;
+	}
+	prevspritesect[0] = -1;
+	nextspritesect[MAXSPRITES-1] = -1;
+
+
+	for(i=0;i<MAXSTATUS;i++)      /* Init doubly-linked sprite status lists */
+		headspritestat[i] = -1;
+	headspritestat[MAXSTATUS] = 0;
+	for(i=0;i<MAXSPRITES;i++)
+	{
+		prevspritestat[i] = i-1;
+		nextspritestat[i] = i+1;
+		sprite[i].statnum = MAXSTATUS;
+	}
+	prevspritestat[0] = -1;
+	nextspritestat[MAXSPRITES-1] = -1;
+}
+
+
+int insertsprite(short sectnum, short statnum)
+{
+	insertspritestat(statnum);
+	return(insertspritesect(sectnum));
+}
+
+
+int insertspritesect(short sectnum)
+{
+	short blanktouse;
+
+	if ((sectnum >= MAXSECTORS) || (headspritesect[MAXSECTORS] == -1))
+		return(-1);  /* list full */
+
+	blanktouse = headspritesect[MAXSECTORS];
+
+	headspritesect[MAXSECTORS] = nextspritesect[blanktouse];
+	if (headspritesect[MAXSECTORS] >= 0)
+		prevspritesect[headspritesect[MAXSECTORS]] = -1;
+
+	prevspritesect[blanktouse] = -1;
+	nextspritesect[blanktouse] = headspritesect[sectnum];
+	if (headspritesect[sectnum] >= 0)
+		prevspritesect[headspritesect[sectnum]] = blanktouse;
+	headspritesect[sectnum] = blanktouse;
+
+	sprite[blanktouse].sectnum = sectnum;
+
+	return(blanktouse);
+}
+
+
+int insertspritestat(short statnum)
+{
+	short blanktouse;
+
+	if ((statnum >= MAXSTATUS) || (headspritestat[MAXSTATUS] == -1))
+		return(-1);  /* list full */
+
+	blanktouse = headspritestat[MAXSTATUS];
+
+	headspritestat[MAXSTATUS] = nextspritestat[blanktouse];
+	if (headspritestat[MAXSTATUS] >= 0)
+		prevspritestat[headspritestat[MAXSTATUS]] = -1;
+
+	prevspritestat[blanktouse] = -1;
+	nextspritestat[blanktouse] = headspritestat[statnum];
+	if (headspritestat[statnum] >= 0)
+		prevspritestat[headspritestat[statnum]] = blanktouse;
+	headspritestat[statnum] = blanktouse;
+
+	sprite[blanktouse].statnum = statnum;
+
+	return(blanktouse);
+}
+
+
+int deletesprite(short spritenum)
+{
+	deletespritestat(spritenum);
+	return(deletespritesect(spritenum));
+}
+
+
+int deletespritesect(short deleteme)
+{
+	if (sprite[deleteme].sectnum == MAXSECTORS)
+		return(-1);
+
+	if (headspritesect[sprite[deleteme].sectnum] == deleteme)
+		headspritesect[sprite[deleteme].sectnum] = nextspritesect[deleteme];
+
+	if (prevspritesect[deleteme] >= 0) nextspritesect[prevspritesect[deleteme]] = nextspritesect[deleteme];
+	if (nextspritesect[deleteme] >= 0) prevspritesect[nextspritesect[deleteme]] = prevspritesect[deleteme];
+
+	if (headspritesect[MAXSECTORS] >= 0) prevspritesect[headspritesect[MAXSECTORS]] = deleteme;
+	prevspritesect[deleteme] = -1;
+	nextspritesect[deleteme] = headspritesect[MAXSECTORS];
+	headspritesect[MAXSECTORS] = deleteme;
+
+	sprite[deleteme].sectnum = MAXSECTORS;
+	return(0);
+}
+
+
+int deletespritestat(short deleteme)
+{
+	if (sprite[deleteme].statnum == MAXSTATUS)
+		return(-1);
+
+	if (headspritestat[sprite[deleteme].statnum] == deleteme)
+		headspritestat[sprite[deleteme].statnum] = nextspritestat[deleteme];
+
+	if (prevspritestat[deleteme] >= 0) nextspritestat[prevspritestat[deleteme]] = nextspritestat[deleteme];
+	if (nextspritestat[deleteme] >= 0) prevspritestat[nextspritestat[deleteme]] = prevspritestat[deleteme];
+
+	if (headspritestat[MAXSTATUS] >= 0) prevspritestat[headspritestat[MAXSTATUS]] = deleteme;
+	prevspritestat[deleteme] = -1;
+	nextspritestat[deleteme] = headspritestat[MAXSTATUS];
+	headspritestat[MAXSTATUS] = deleteme;
+
+	sprite[deleteme].statnum = MAXSTATUS;
+	return(0);
+}
+
+
+int changespritesect(short spritenum, short newsectnum)
+{
+	if ((newsectnum < 0) || (newsectnum > MAXSECTORS)) return(-1);
+	if (sprite[spritenum].sectnum == newsectnum) return(0);
+	if (sprite[spritenum].sectnum == MAXSECTORS) return(-1);
+	if (deletespritesect(spritenum) < 0) return(-1);
+	insertspritesect(newsectnum);
+	return(0);
+}
+
+
+int changespritestat(short spritenum, short newstatnum)
+{
+	if ((newstatnum < 0) || (newstatnum > MAXSTATUS)) return(-1);
+	if (sprite[spritenum].statnum == newstatnum) return(0);
+	if (sprite[spritenum].statnum == MAXSTATUS) return(-1);
+	if (deletespritestat(spritenum) < 0) return(-1);
+	insertspritestat(newstatnum);
+	return(0);
+}
+
+
+int nextsectorneighborz(short sectnum, long thez,
+                        short topbottom, short direction)
+{
+	walltype *wal;
+	long i, testz, nextz;
+	short sectortouse;
+
+	if (direction == 1) nextz = 0x7fffffff; else nextz = 0x80000000;
+
+	sectortouse = -1;
+
+	wal = &wall[sector[sectnum].wallptr];
+	i = sector[sectnum].wallnum;
+	do
+	{
+		if (wal->nextsector >= 0)
+		{
+			if (topbottom == 1)
+			{
+				testz = sector[wal->nextsector].floorz;
+				if (direction == 1)
+				{
+					if ((testz > thez) && (testz < nextz))
+					{
+						nextz = testz;
+						sectortouse = wal->nextsector;
+					}
+				}
+				else
+				{
+					if ((testz < thez) && (testz > nextz))
+					{
+						nextz = testz;
+						sectortouse = wal->nextsector;
+					}
+				}
+			}
+			else
+			{
+				testz = sector[wal->nextsector].ceilingz;
+				if (direction == 1)
+				{
+					if ((testz > thez) && (testz < nextz))
+					{
+						nextz = testz;
+						sectortouse = wal->nextsector;
+					}
+				}
+				else
+				{
+					if ((testz < thez) && (testz > nextz))
+					{
+						nextz = testz;
+						sectortouse = wal->nextsector;
+					}
+				}
+			}
+		}
+		wal++;
+		i--;
+	} while (i != 0);
+
+	return(sectortouse);
+}
+
+
+int cansee(long x1, long y1, long z1, short sect1,
+            long x2, long y2, long z2, short sect2)
+{
+	sectortype *sec;
+	walltype *wal, *wal2;
+	long i, cnt, nexts, x, y, z, cz, fz, dasectnum, dacnt, danum;
+	long x21, y21, z21, x31, y31, x34, y34, bot, t;
+
+	if ((x1 == x2) && (y1 == y2)) return(sect1 == sect2);
+
+	x21 = x2-x1; y21 = y2-y1; z21 = z2-z1;
+
+	clipsectorlist[0] = sect1; danum = 1;
+	for(dacnt=0;dacnt<danum;dacnt++)
+	{
+		dasectnum = clipsectorlist[dacnt]; sec = &sector[dasectnum];
+		for(cnt=sec->wallnum,wal=&wall[sec->wallptr];cnt>0;cnt--,wal++)
+		{
+			wal2 = &wall[wal->point2];
+			x31 = wal->x-x1; x34 = wal->x-wal2->x;
+			y31 = wal->y-y1; y34 = wal->y-wal2->y;
+
+			bot = y21*x34-x21*y34; if (bot <= 0) continue;
+			t = y21*x31-x21*y31; if ((unsigned)t >= (unsigned)bot) continue;
+			t = y31*x34-x31*y34; if ((unsigned)t >= (unsigned)bot) continue;
+
+			nexts = wal->nextsector;
+			if ((nexts < 0) || (wal->cstat&32)) return(0);
+
+			t = divscale24(t,bot);
+			x = x1 + mulscale24(x21,t);
+			y = y1 + mulscale24(y21,t);
+			z = z1 + mulscale24(z21,t);
+
+			getzsofslope((short)dasectnum,x,y,&cz,&fz);
+			if ((z <= cz) || (z >= fz)) return(0);
+			getzsofslope((short)nexts,x,y,&cz,&fz);
+			if ((z <= cz) || (z >= fz)) return(0);
+
+			for(i=danum-1;i>=0;i--) if (clipsectorlist[i] == nexts) break;
+			if (i < 0) clipsectorlist[danum++] = nexts;
+		}
+	}
+	for(i=danum-1;i>=0;i--) if (clipsectorlist[i] == sect2) return(1);
+	return(0);
+}
+
+
+int lintersect(long x1, long y1, long z1, long x2, long y2, long z2,
+               long x3, long y3, long x4, long y4, long *intx,
+               long *inty, long *intz)
+{     /* p1 to p2 is a line segment */
+	long x21, y21, x34, y34, x31, y31, bot, topt, topu, t;
+
+	x21 = x2-x1; x34 = x3-x4;
+	y21 = y2-y1; y34 = y3-y4;
+	bot = x21*y34 - y21*x34;
+	if (bot >= 0)
+	{
+		if (bot == 0) return(0);
+		x31 = x3-x1; y31 = y3-y1;
+		topt = x31*y34 - y31*x34; if ((topt < 0) || (topt >= bot)) return(0);
+		topu = x21*y31 - y21*x31; if ((topu < 0) || (topu >= bot)) return(0);
+	}
+	else
+	{
+		x31 = x3-x1; y31 = y3-y1;
+		topt = x31*y34 - y31*x34; if ((topt > 0) || (topt <= bot)) return(0);
+		topu = x21*y31 - y21*x31; if ((topu > 0) || (topu <= bot)) return(0);
+	}
+	t = divscale24(topt,bot);
+	*intx = x1 + mulscale24(x21,t);
+	*inty = y1 + mulscale24(y21,t);
+	*intz = z1 + mulscale24(z2-z1,t);
+	return(1);
+}
+
+
+int rintersect(long x1, long y1, long z1, long vx, long vy, long vz,
+               long x3, long y3, long x4, long y4, long *intx,
+               long *inty, long *intz)
+{     /* p1 towards p2 is a ray */
+	long x34, y34, x31, y31, bot, topt, topu, t;
+
+	x34 = x3-x4; y34 = y3-y4;
+	bot = vx*y34 - vy*x34;
+	if (bot >= 0)
+	{
+		if (bot == 0) return(0);
+		x31 = x3-x1; y31 = y3-y1;
+		topt = x31*y34 - y31*x34; if (topt < 0) return(0);
+		topu = vx*y31 - vy*x31; if ((topu < 0) || (topu >= bot)) return(0);
+	}
+	else
+	{
+		x31 = x3-x1; y31 = y3-y1;
+		topt = x31*y34 - y31*x34; if (topt > 0) return(0);
+		topu = vx*y31 - vy*x31; if ((topu > 0) || (topu <= bot)) return(0);
+	}
+	t = divscale16(topt,bot);
+	*intx = x1 + mulscale16(vx,t);
+	*inty = y1 + mulscale16(vy,t);
+	*intz = z1 + mulscale16(vz,t);
+	return(1);
+}
+
+
+int hitscan(long xs, long ys, long zs, short sectnum,
+            long vx, long vy, long vz,
+	        short *hitsect, short *hitwall, short *hitsprite,
+	        long *hitx, long *hity, long *hitz, unsigned long cliptype)
+{
+	sectortype *sec;
+	walltype *wal, *wal2;
+	spritetype *spr;
+	long z, zz, x1, y1=0, z1=0, x2, y2, x3, y3, x4, y4, intx, inty, intz;
+	long topt, topu, bot, dist, offx, offy, cstat;
+	long i, j, k, l, tilenum, xoff, yoff, dax, day, daz, daz2;
+	long ang, cosang, sinang, xspan, yspan, xrepeat, yrepeat;
+	long dawalclipmask, dasprclipmask;
+	short tempshortcnt, tempshortnum, dasector, startwall, endwall;
+	short nextsector;
+	char clipyou;
+
+	*hitsect = -1; *hitwall = -1; *hitsprite = -1;
+	if (sectnum < 0) return(-1);
+
+	*hitx = hitscangoalx; *hity = hitscangoaly;
+
+	dawalclipmask = (cliptype&65535);
+	dasprclipmask = (cliptype>>16);
+
+	clipsectorlist[0] = sectnum;
+	tempshortcnt = 0; tempshortnum = 1;
+	do
+	{
+		dasector = clipsectorlist[tempshortcnt]; sec = &sector[dasector];
+
+		x1 = 0x7fffffff;
+		if (sec->ceilingstat&2)
+		{
+			wal = &wall[sec->wallptr]; wal2 = &wall[wal->point2];
+			dax = wal2->x-wal->x; day = wal2->y-wal->y;
+			i = nsqrtasm(dax*dax+day*day); if (i == 0) continue;
+			i = divscale15(sec->ceilingheinum,i);
+			dax *= i; day *= i;
+
+			j = (vz<<8)-dmulscale15(dax,vy,-day,vx);
+			if (j != 0)
+			{
+				i = ((sec->ceilingz-zs)<<8)+dmulscale15(dax,ys-wal->y,-day,xs-wal->x);
+				if (((i^j) >= 0) && ((klabs(i)>>1) < klabs(j)))
+				{
+					i = divscale30(i,j);
+					x1 = xs + mulscale30(vx,i);
+					y1 = ys + mulscale30(vy,i);
+					z1 = zs + mulscale30(vz,i);
+				}
+			}
+		}
+		else if ((vz < 0) && (zs >= sec->ceilingz))
+		{
+			z1 = sec->ceilingz; i = z1-zs;
+			if ((klabs(i)>>1) < -vz)
+			{
+				i = divscale30(i,vz);
+				x1 = xs + mulscale30(vx,i);
+				y1 = ys + mulscale30(vy,i);
+			}
+		}
+		if ((x1 != 0x7fffffff) && (klabs(x1-xs)+klabs(y1-ys) < klabs((*hitx)-xs)+klabs((*hity)-ys)))
+			if (inside(x1,y1,dasector) != 0)
+			{
+				*hitsect = dasector; *hitwall = -1; *hitsprite = -1;
+				*hitx = x1; *hity = y1; *hitz = z1;
+			}
+
+		x1 = 0x7fffffff;
+		if (sec->floorstat&2)
+		{
+			wal = &wall[sec->wallptr]; wal2 = &wall[wal->point2];
+			dax = wal2->x-wal->x; day = wal2->y-wal->y;
+			i = nsqrtasm(dax*dax+day*day); if (i == 0) continue;
+			i = divscale15(sec->floorheinum,i);
+			dax *= i; day *= i;
+
+			j = (vz<<8)-dmulscale15(dax,vy,-day,vx);
+			if (j != 0)
+			{
+				i = ((sec->floorz-zs)<<8)+dmulscale15(dax,ys-wal->y,-day,xs-wal->x);
+				if (((i^j) >= 0) && ((klabs(i)>>1) < klabs(j)))
+				{
+					i = divscale30(i,j);
+					x1 = xs + mulscale30(vx,i);
+					y1 = ys + mulscale30(vy,i);
+					z1 = zs + mulscale30(vz,i);
+				}
+			}
+		}
+		else if ((vz > 0) && (zs <= sec->floorz))
+		{
+			z1 = sec->floorz; i = z1-zs;
+			if ((klabs(i)>>1) < vz)
+			{
+				i = divscale30(i,vz);
+				x1 = xs + mulscale30(vx,i);
+				y1 = ys + mulscale30(vy,i);
+			}
+		}
+		if ((x1 != 0x7fffffff) && (klabs(x1-xs)+klabs(y1-ys) < klabs((*hitx)-xs)+klabs((*hity)-ys)))
+			if (inside(x1,y1,dasector) != 0)
+			{
+				*hitsect = dasector; *hitwall = -1; *hitsprite = -1;
+				*hitx = x1; *hity = y1; *hitz = z1;
+			}
+
+		startwall = sec->wallptr; endwall = startwall + sec->wallnum;
+		for(z=startwall,wal=&wall[startwall];z<endwall;z++,wal++)
+		{
+			wal2 = &wall[wal->point2];
+			x1 = wal->x; y1 = wal->y; x2 = wal2->x; y2 = wal2->y;
+
+			if ((x1-xs)*(y2-ys) < (x2-xs)*(y1-ys)) continue;
+			if (rintersect(xs,ys,zs,vx,vy,vz,x1,y1,x2,y2,&intx,&inty,&intz) == 0) continue;
+
+			if (klabs(intx-xs)+klabs(inty-ys) >= klabs((*hitx)-xs)+klabs((*hity)-ys)) continue;
+
+			nextsector = wal->nextsector;
+			if ((nextsector < 0) || (wal->cstat&dawalclipmask))
+			{
+				*hitsect = dasector; *hitwall = z; *hitsprite = -1;
+				*hitx = intx; *hity = inty; *hitz = intz;
+				continue;
+			}
+			getzsofslope(nextsector,intx,inty,&daz,&daz2);
+			if ((intz <= daz) || (intz >= daz2))
+			{
+				*hitsect = dasector; *hitwall = z; *hitsprite = -1;
+				*hitx = intx; *hity = inty; *hitz = intz;
+				continue;
+			}
+
+			for(zz=tempshortnum-1;zz>=0;zz--)
+				if (clipsectorlist[zz] == nextsector) break;
+			if (zz < 0) clipsectorlist[tempshortnum++] = nextsector;
+		}
+
+		for(z=headspritesect[dasector];z>=0;z=nextspritesect[z])
+		{
+			spr = &sprite[z];
+			cstat = spr->cstat;
+			if ((cstat&dasprclipmask) == 0) continue;
+
+			x1 = spr->x; y1 = spr->y; z1 = spr->z;
+			switch(cstat&48)
+			{
+				case 0:
+					topt = vx*(x1-xs) + vy*(y1-ys); if (topt <= 0) continue;
+					bot = vx*vx + vy*vy; if (bot == 0) continue;
+
+					intz = zs+scale(vz,topt,bot);
+
+					i = (tilesizy[spr->picnum]*spr->yrepeat<<2);
+					if (cstat&128) z1 += (i>>1);
+					if (picanm[spr->picnum]&0x00ff0000) z1 -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+					if ((intz > z1) || (intz < z1-i)) continue;
+					topu = vx*(y1-ys) - vy*(x1-xs);
+
+					offx = scale(vx,topu,bot);
+					offy = scale(vy,topu,bot);
+					dist = offx*offx + offy*offy;
+					i = tilesizx[spr->picnum]*spr->xrepeat; i *= i;
+					if (dist > (i>>7)) continue;
+					intx = xs + scale(vx,topt,bot);
+					inty = ys + scale(vy,topt,bot);
+
+					if (klabs(intx-xs)+klabs(inty-ys) > klabs((*hitx)-xs)+klabs((*hity)-ys)) continue;
+
+					*hitsect = dasector; *hitwall = -1; *hitsprite = z;
+					*hitx = intx; *hity = inty; *hitz = intz;
+					break;
+				case 16:
+						/*
+						 * These lines get the 2 points of the rotated sprite
+						 * Given: (x1, y1) starts out as the center point
+						 */
+					tilenum = spr->picnum;
+					xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+					if ((cstat&4) > 0) xoff = -xoff;
+					k = spr->ang; l = spr->xrepeat;
+					dax = sintable[k&2047]*l; day = sintable[(k+1536)&2047]*l;
+					l = tilesizx[tilenum]; k = (l>>1)+xoff;
+					x1 -= mulscale16(dax,k); x2 = x1+mulscale16(dax,l);
+					y1 -= mulscale16(day,k); y2 = y1+mulscale16(day,l);
+
+					if ((cstat&64) != 0)   /* back side of 1-way sprite */
+						if ((x1-xs)*(y2-ys) < (x2-xs)*(y1-ys)) continue;
+
+					if (rintersect(xs,ys,zs,vx,vy,vz,x1,y1,x2,y2,&intx,&inty,&intz) == 0) continue;
+
+					if (klabs(intx-xs)+klabs(inty-ys) > klabs((*hitx)-xs)+klabs((*hity)-ys)) continue;
+
+					k = ((tilesizy[spr->picnum]*spr->yrepeat)<<2);
+					if (cstat&128) daz = spr->z+(k>>1); else daz = spr->z;
+					if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+					if ((intz < daz) && (intz > daz-k))
+					{
+						*hitsect = dasector; *hitwall = -1; *hitsprite = z;
+						*hitx = intx; *hity = inty; *hitz = intz;
+					}
+					break;
+				case 32:
+					if (vz == 0) continue;
+					intz = z1;
+					if (((intz-zs)^vz) < 0) continue;
+					if ((cstat&64) != 0)
+						if ((zs > intz) == ((cstat&8)==0)) continue;
+
+					intx = xs+scale(intz-zs,vx,vz);
+					inty = ys+scale(intz-zs,vy,vz);
+
+					if (klabs(intx-xs)+klabs(inty-ys) > klabs((*hitx)-xs)+klabs((*hity)-ys)) continue;
+
+					tilenum = spr->picnum;
+					xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+					yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
+					if ((cstat&4) > 0) xoff = -xoff;
+					if ((cstat&8) > 0) yoff = -yoff;
+
+					ang = spr->ang;
+					cosang = sintable[(ang+512)&2047]; sinang = sintable[ang];
+					xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
+					yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;
+
+					dax = ((xspan>>1)+xoff)*xrepeat; day = ((yspan>>1)+yoff)*yrepeat;
+					x1 += dmulscale16(sinang,dax,cosang,day)-intx;
+					y1 += dmulscale16(sinang,day,-cosang,dax)-inty;
+					l = xspan*xrepeat;
+					x2 = x1 - mulscale16(sinang,l);
+					y2 = y1 + mulscale16(cosang,l);
+					l = yspan*yrepeat;
+					k = -mulscale16(cosang,l); x3 = x2+k; x4 = x1+k;
+					k = -mulscale16(sinang,l); y3 = y2+k; y4 = y1+k;
+
+					clipyou = 0;
+					if ((y1^y2) < 0)
+					{
+						if ((x1^x2) < 0) clipyou ^= (x1*y2<x2*y1)^(y1<y2);
+						else if (x1 >= 0) clipyou ^= 1;
+					}
+					if ((y2^y3) < 0)
+					{
+						if ((x2^x3) < 0) clipyou ^= (x2*y3<x3*y2)^(y2<y3);
+						else if (x2 >= 0) clipyou ^= 1;
+					}
+					if ((y3^y4) < 0)
+					{
+						if ((x3^x4) < 0) clipyou ^= (x3*y4<x4*y3)^(y3<y4);
+						else if (x3 >= 0) clipyou ^= 1;
+					}
+					if ((y4^y1) < 0)
+					{
+						if ((x4^x1) < 0) clipyou ^= (x4*y1<x1*y4)^(y4<y1);
+						else if (x4 >= 0) clipyou ^= 1;
+					}
+
+					if (clipyou != 0)
+					{
+						*hitsect = dasector; *hitwall = -1; *hitsprite = z;
+						*hitx = intx; *hity = inty; *hitz = intz;
+					}
+					break;
+			}
+		}
+		tempshortcnt++;
+	} while (tempshortcnt < tempshortnum);
+	return(0);
+}
+
+
+int neartag(long xs, long ys, long zs, short sectnum, short ange,
+            short *neartagsector, short *neartagwall, short *neartagsprite,
+            long *neartaghitdist, long neartagrange, char tagsearch)
+{
+	walltype *wal, *wal2;
+	spritetype *spr;
+	long i, z, zz, xe, ye, ze, x1, y1, z1, x2, y2, intx, inty, intz;
+	long topt, topu, bot, dist, offx, offy, vx, vy, vz;
+	short tempshortcnt, tempshortnum, dasector, startwall, endwall;
+	short nextsector, good;
+
+	*neartagsector = -1; *neartagwall = -1; *neartagsprite = -1;
+	*neartaghitdist = 0;
+
+	if (sectnum < 0) return(0);
+	if ((tagsearch < 1) || (tagsearch > 3)) return(0);
+
+	vx = mulscale14(sintable[(ange+2560)&2047],neartagrange); xe = xs+vx;
+	vy = mulscale14(sintable[(ange+2048)&2047],neartagrange); ye = ys+vy;
+	vz = 0; ze = 0;
+
+	clipsectorlist[0] = sectnum;
+	tempshortcnt = 0; tempshortnum = 1;
+
+	do
+	{
+		dasector = clipsectorlist[tempshortcnt];
+
+		startwall = sector[dasector].wallptr;
+		endwall = startwall + sector[dasector].wallnum - 1;
+		for(z=startwall,wal=&wall[startwall];z<=endwall;z++,wal++)
+		{
+			wal2 = &wall[wal->point2];
+			x1 = wal->x; y1 = wal->y; x2 = wal2->x; y2 = wal2->y;
+
+			nextsector = wal->nextsector;
+
+			good = 0;
+			if (nextsector >= 0)
+			{
+				if ((tagsearch&1) && sector[nextsector].lotag) good |= 1;
+				if ((tagsearch&2) && sector[nextsector].hitag) good |= 1;
+			}
+			if ((tagsearch&1) && wal->lotag) good |= 2;
+			if ((tagsearch&2) && wal->hitag) good |= 2;
+
+			if ((good == 0) && (nextsector < 0)) continue;
+			if ((x1-xs)*(y2-ys) < (x2-xs)*(y1-ys)) continue;
+
+			if (lintersect(xs,ys,zs,xe,ye,ze,x1,y1,x2,y2,&intx,&inty,&intz) == 1)
+			{
+				if (good != 0)
+				{
+					if (good&1) *neartagsector = nextsector;
+					if (good&2) *neartagwall = z;
+					*neartaghitdist = dmulscale14(intx-xs,sintable[(ange+2560)&2047],inty-ys,sintable[(ange+2048)&2047]);
+					xe = intx; ye = inty; ze = intz;
+				}
+				if (nextsector >= 0)
+				{
+					for(zz=tempshortnum-1;zz>=0;zz--)
+						if (clipsectorlist[zz] == nextsector) break;
+					if (zz < 0) clipsectorlist[tempshortnum++] = nextsector;
+				}
+			}
+		}
+
+		for(z=headspritesect[dasector];z>=0;z=nextspritesect[z])
+		{
+			spr = &sprite[z];
+
+			good = 0;
+			if ((tagsearch&1) && spr->lotag) good |= 1;
+			if ((tagsearch&2) && spr->hitag) good |= 1;
+			if (good != 0)
+			{
+				x1 = spr->x; y1 = spr->y; z1 = spr->z;
+
+				topt = vx*(x1-xs) + vy*(y1-ys);
+				if (topt > 0)
+				{
+					bot = vx*vx + vy*vy;
+					if (bot != 0)
+					{
+						intz = zs+scale(vz,topt,bot);
+						i = tilesizy[spr->picnum]*spr->yrepeat;
+						if (spr->cstat&128) z1 += (i<<1);
+						if (picanm[spr->picnum]&0x00ff0000) z1 -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+						if ((intz <= z1) && (intz >= z1-(i<<2)))
+						{
+							topu = vx*(y1-ys) - vy*(x1-xs);
+
+							offx = scale(vx,topu,bot);
+							offy = scale(vy,topu,bot);
+							dist = offx*offx + offy*offy;
+							i = (tilesizx[spr->picnum]*spr->xrepeat); i *= i;
+							if (dist <= (i>>7))
+							{
+								intx = xs + scale(vx,topt,bot);
+								inty = ys + scale(vy,topt,bot);
+								if (klabs(intx-xs)+klabs(inty-ys) < klabs(xe-xs)+klabs(ye-ys))
+								{
+									*neartagsprite = z;
+									*neartaghitdist = dmulscale14(intx-xs,sintable[(ange+2560)&2047],inty-ys,sintable[(ange+2048)&2047]);
+									xe = intx;
+									ye = inty;
+									ze = intz;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		tempshortcnt++;
+	} while (tempshortcnt < tempshortnum);
+	return(0);
+}
+
+
+void dragpoint(short pointhighlight, long dax, long day)
+{
+	short cnt, tempshort;
+
+	wall[pointhighlight].x = dax;
+	wall[pointhighlight].y = day;
+
+	cnt = MAXWALLS;
+	tempshort = pointhighlight;    /* search points CCW */
+	do
+	{
+		if (wall[tempshort].nextwall >= 0)
+		{
+			tempshort = wall[wall[tempshort].nextwall].point2;
+			wall[tempshort].x = dax;
+			wall[tempshort].y = day;
+		}
+		else
+		{
+			tempshort = pointhighlight;    /* search points CW if not searched all the way around */
+			do
+			{
+				if (wall[lastwall(tempshort)].nextwall >= 0)
+				{
+					tempshort = wall[lastwall(tempshort)].nextwall;
+					wall[tempshort].x = dax;
+					wall[tempshort].y = day;
+				}
+				else
+				{
+					break;
+				}
+				cnt--;
+			}
+			while ((tempshort != pointhighlight) && (cnt > 0));
+			break;
+		}
+		cnt--;
+	}
+	while ((tempshort != pointhighlight) && (cnt > 0));
+}
+
+
+int lastwall(short point)
+{
+	long i, j, cnt;
+
+	if ((point > 0) && (wall[point-1].point2 == point)) return(point-1);
+	i = point;
+	cnt = MAXWALLS;
+	do
+	{
+		j = wall[i].point2;
+		if (j == point) return(i);
+		i = j;
+		cnt--;
+	} while (cnt > 0);
+	return(point);
+}
+
+#define addclipline(dax1, day1, dax2, day2, daoval)      \
+{                                                        \
+	clipit[clipnum].x1 = dax1; clipit[clipnum].y1 = day1; \
+	clipit[clipnum].x2 = dax2; clipit[clipnum].y2 = day2; \
+	clipobjectval[clipnum] = daoval;                      \
+	clipnum++;                                            \
+}                                                        \
+
+
+static void keepaway (long *x, long *y, long w)
+{
+	long dx, dy, ox, oy, x1, y1;
+	char first;
+
+	x1 = clipit[w].x1; dx = clipit[w].x2-x1;
+	y1 = clipit[w].y1; dy = clipit[w].y2-y1;
+	ox = ksgn(-dy); oy = ksgn(dx);
+	first = (klabs(dx) <= klabs(dy));
+	while (1)
+	{
+		if (dx*(*y-y1) > (*x-x1)*dy) return;
+		if (first == 0) *x += ox; else *y += oy;
+		first ^= 1;
+	}
+}
+
+
+static int raytrace(long x3, long y3, long *x4, long *y4)
+{
+	long x1, y1, x2, y2, bot, topu, nintx, ninty, cnt, z, hitwall;
+	long x21, y21, x43, y43;
+
+	hitwall = -1;
+	for(z=clipnum-1;z>=0;z--)
+	{
+		x1 = clipit[z].x1; x2 = clipit[z].x2; x21 = x2-x1;
+		y1 = clipit[z].y1; y2 = clipit[z].y2; y21 = y2-y1;
+
+		topu = x21*(y3-y1) - (x3-x1)*y21; if (topu <= 0) continue;
+		if (x21*(*y4-y1) > (*x4-x1)*y21) continue;
+		x43 = *x4-x3; y43 = *y4-y3;
+		if (x43*(y1-y3) > (x1-x3)*y43) continue;
+		if (x43*(y2-y3) <= (x2-x3)*y43) continue;
+		bot = x43*y21 - x21*y43; if (bot == 0) continue;
+
+		cnt = 256;
+		do
+		{
+			cnt--; if (cnt < 0) { *x4 = x3; *y4 = y3; return(z); }
+			nintx = x3 + scale(x43,topu,bot);
+			ninty = y3 + scale(y43,topu,bot);
+			topu--;
+		} while (x21*(ninty-y1) <= (nintx-x1)*y21);
+
+		if (klabs(x3-nintx)+klabs(y3-ninty) < klabs(x3-*x4)+klabs(y3-*y4))
+			{ *x4 = nintx; *y4 = ninty; hitwall = z; }
+	}
+	return(hitwall);
+}
+
+
+/* !!! ugh...move this var into clipmove as a parameter, and update build2.txt! */
+long clipmoveboxtracenum = 3;
+int clipmove (long *x, long *y, long *z, short *sectnum,
+              long xvect, long yvect, long walldist, long ceildist,
+              long flordist, unsigned long cliptype)
+{
+	walltype *wal, *wal2;
+	spritetype *spr;
+	sectortype *sec, *sec2;
+	long i, j, templong1, templong2;
+	long oxvect, oyvect, goalx, goaly, intx, inty, lx, ly, retval;
+	long k, l, clipsectcnt, startwall, endwall, cstat, dasect;
+	long x1, y1, x2, y2, cx, cy, rad, xmin, ymin, xmax, ymax, daz, daz2;
+	long bsz, dax, day, xoff, yoff, xspan, yspan, cosang, sinang, tilenum;
+	long xrepeat, yrepeat, gx, gy, dx, dy, dasprclipmask, dawalclipmask;
+	long hitwall, cnt, clipyou;
+
+	if (((xvect|yvect) == 0) || (*sectnum < 0)) return(0);
+	retval = 0;
+
+	oxvect = xvect;
+	oyvect = yvect;
+
+	goalx = (*x) + (xvect>>14);
+	goaly = (*y) + (yvect>>14);
+
+
+	clipnum = 0;
+
+	cx = (((*x)+goalx)>>1);
+	cy = (((*y)+goaly)>>1);
+		/* Extra walldist for sprites on sector lines */
+	gx = goalx-(*x); gy = goaly-(*y);
+	rad = nsqrtasm(gx*gx + gy*gy) + MAXCLIPDIST+walldist + 8;
+	xmin = cx-rad; ymin = cy-rad;
+	xmax = cx+rad; ymax = cy+rad;
+
+	dawalclipmask = (cliptype&65535);        /* CLIPMASK0 = 0x00010001 */
+	dasprclipmask = (cliptype>>16);          /* CLIPMASK1 = 0x01000040 */
+
+	clipsectorlist[0] = (*sectnum);
+	clipsectcnt = 0; clipsectnum = 1;
+	do
+	{
+		dasect = clipsectorlist[clipsectcnt++];
+		sec = &sector[dasect];
+		startwall = sec->wallptr; endwall = startwall + sec->wallnum;
+		for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
+		{
+			wal2 = &wall[wal->point2];
+			if ((wal->x < xmin) && (wal2->x < xmin)) continue;
+			if ((wal->x > xmax) && (wal2->x > xmax)) continue;
+			if ((wal->y < ymin) && (wal2->y < ymin)) continue;
+			if ((wal->y > ymax) && (wal2->y > ymax)) continue;
+
+			x1 = wal->x; y1 = wal->y; x2 = wal2->x; y2 = wal2->y;
+
+			dx = x2-x1; dy = y2-y1;
+			if (dx*((*y)-y1) < ((*x)-x1)*dy) continue;  /* If wall's not facing you */
+
+			if (dx > 0) dax = dx*(ymin-y1); else dax = dx*(ymax-y1);
+			if (dy > 0) day = dy*(xmax-x1); else day = dy*(xmin-x1);
+			if (dax >= day) continue;
+
+			clipyou = 0;
+			if ((wal->nextsector < 0) || (wal->cstat&dawalclipmask)) clipyou = 1;
+			else if (editstatus == 0)
+			{
+				if (rintersect(*x,*y,0,gx,gy,0,x1,y1,x2,y2,&dax,&day,&daz) == 0)
+					dax = *x, day = *y;
+				daz = getflorzofslope((short)dasect,dax,day);
+				daz2 = getflorzofslope(wal->nextsector,dax,day);
+
+				sec2 = &sector[wal->nextsector];
+				if (daz2 < daz-(1<<8))
+					if ((sec2->floorstat&1) == 0)
+						if ((*z) >= daz2-(flordist-1)) clipyou = 1;
+				if (clipyou == 0)
+				{
+					daz = getceilzofslope((short)dasect,dax,day);
+					daz2 = getceilzofslope(wal->nextsector,dax,day);
+					if (daz2 > daz+(1<<8))
+						if ((sec2->ceilingstat&1) == 0)
+							if ((*z) <= daz2+(ceildist-1)) clipyou = 1;
+				}
+			}
+
+			if (clipyou)
+			{
+					/* Add 2 boxes at endpoints */
+				bsz = walldist; if (gx < 0) bsz = -bsz;
+				addclipline(x1-bsz,y1-bsz,x1-bsz,y1+bsz,(short)j+32768);
+				addclipline(x2-bsz,y2-bsz,x2-bsz,y2+bsz,(short)j+32768);
+				bsz = walldist; if (gy < 0) bsz = -bsz;
+				addclipline(x1+bsz,y1-bsz,x1-bsz,y1-bsz,(short)j+32768);
+				addclipline(x2+bsz,y2-bsz,x2-bsz,y2-bsz,(short)j+32768);
+
+				dax = walldist; if (dy > 0) dax = -dax;
+				day = walldist; if (dx < 0) day = -day;
+				addclipline(x1+dax,y1+day,x2+dax,y2+day,(short)j+32768);
+			}
+			else
+			{
+				for(i=clipsectnum-1;i>=0;i--)
+					if (wal->nextsector == clipsectorlist[i]) break;
+				if (i < 0) clipsectorlist[clipsectnum++] = wal->nextsector;
+			}
+		}
+
+		for(j=headspritesect[dasect];j>=0;j=nextspritesect[j])
+		{
+			spr = &sprite[j];
+			cstat = spr->cstat;
+			if ((cstat&dasprclipmask) == 0) continue;
+			x1 = spr->x; y1 = spr->y;
+			switch(cstat&48)
+			{
+				case 0:
+					if ((x1 >= xmin) && (x1 <= xmax) && (y1 >= ymin) && (y1 <= ymax))
+					{
+						k = ((tilesizy[spr->picnum]*spr->yrepeat)<<2);
+						if (cstat&128) daz = spr->z+(k>>1); else daz = spr->z;
+						if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+						if (((*z) < daz+ceildist) && ((*z) > daz-k-flordist))
+						{
+							bsz = (spr->clipdist<<2)+walldist; if (gx < 0) bsz = -bsz;
+							addclipline(x1-bsz,y1-bsz,x1-bsz,y1+bsz,(short)j+49152);
+							bsz = (spr->clipdist<<2)+walldist; if (gy < 0) bsz = -bsz;
+							addclipline(x1+bsz,y1-bsz,x1-bsz,y1-bsz,(short)j+49152);
+						}
+					}
+					break;
+				case 16:
+					k = ((tilesizy[spr->picnum]*spr->yrepeat)<<2);
+					if (cstat&128) daz = spr->z+(k>>1); else daz = spr->z;
+					if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+					daz2 = daz-k;
+					daz += ceildist; daz2 -= flordist;
+					if (((*z) < daz) && ((*z) > daz2))
+					{
+							/* 
+							 * These lines get the 2 points of the rotated sprite
+							 * Given: (x1, y1) starts out as the center point
+                             */
+                        tilenum = spr->picnum;
+						xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+						if ((cstat&4) > 0) xoff = -xoff;
+						k = spr->ang; l = spr->xrepeat;
+						dax = sintable[k&2047]*l; day = sintable[(k+1536)&2047]*l;
+						l = tilesizx[tilenum]; k = (l>>1)+xoff;
+						x1 -= mulscale16(dax,k); x2 = x1+mulscale16(dax,l);
+						y1 -= mulscale16(day,k); y2 = y1+mulscale16(day,l);
+						if (clipinsideboxline(cx,cy,x1,y1,x2,y2,rad) != 0)
+						{
+							dax = mulscale14(sintable[(spr->ang+256+512)&2047],walldist);
+							day = mulscale14(sintable[(spr->ang+256)&2047],walldist);
+
+							if ((x1-(*x))*(y2-(*y)) >= (x2-(*x))*(y1-(*y)))   /* Front */
+							{
+								addclipline(x1+dax,y1+day,x2+day,y2-dax,(short)j+49152);
+							}
+							else
+							{
+								if ((cstat&64) != 0) continue;
+								addclipline(x2-dax,y2-day,x1-day,y1+dax,(short)j+49152);
+							}
+
+								/* Side blocker */
+							if ((x2-x1)*((*x)-x1) + (y2-y1)*((*y)-y1) < 0)
+								{ addclipline(x1-day,y1+dax,x1+dax,y1+day,(short)j+49152); }
+							else if ((x1-x2)*((*x)-x2) + (y1-y2)*((*y)-y2) < 0)
+								{ addclipline(x2+day,y2-dax,x2-dax,y2-day,(short)j+49152); }
+						}
+					}
+					break;
+				case 32:
+					daz = spr->z+ceildist;
+					daz2 = spr->z-flordist;
+					if (((*z) < daz) && ((*z) > daz2))
+					{
+						if ((cstat&64) != 0)
+							if (((*z) > spr->z) == ((cstat&8)==0)) continue;
+
+						tilenum = spr->picnum;
+						xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+						yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
+						if ((cstat&4) > 0) xoff = -xoff;
+						if ((cstat&8) > 0) yoff = -yoff;
+
+						k = spr->ang;
+						cosang = sintable[(k+512)&2047]; sinang = sintable[k];
+						xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
+						yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;
+
+						dax = ((xspan>>1)+xoff)*xrepeat; day = ((yspan>>1)+yoff)*yrepeat;
+						rxi[0] = x1 + dmulscale16(sinang,dax,cosang,day);
+						ryi[0] = y1 + dmulscale16(sinang,day,-cosang,dax);
+						l = xspan*xrepeat;
+						rxi[1] = rxi[0] - mulscale16(sinang,l);
+						ryi[1] = ryi[0] + mulscale16(cosang,l);
+						l = yspan*yrepeat;
+						k = -mulscale16(cosang,l); rxi[2] = rxi[1]+k; rxi[3] = rxi[0]+k;
+						k = -mulscale16(sinang,l); ryi[2] = ryi[1]+k; ryi[3] = ryi[0]+k;
+
+						dax = mulscale14(sintable[(spr->ang-256+512)&2047],walldist);
+						day = mulscale14(sintable[(spr->ang-256)&2047],walldist);
+
+						if ((rxi[0]-(*x))*(ryi[1]-(*y)) < (rxi[1]-(*x))*(ryi[0]-(*y)))
+						{
+							if (clipinsideboxline(cx,cy,rxi[1],ryi[1],rxi[0],ryi[0],rad) != 0)
+								addclipline(rxi[1]-day,ryi[1]+dax,rxi[0]+dax,ryi[0]+day,(short)j+49152);
+						}
+						else if ((rxi[2]-(*x))*(ryi[3]-(*y)) < (rxi[3]-(*x))*(ryi[2]-(*y)))
+						{
+							if (clipinsideboxline(cx,cy,rxi[3],ryi[3],rxi[2],ryi[2],rad) != 0)
+								addclipline(rxi[3]+day,ryi[3]-dax,rxi[2]-dax,ryi[2]-day,(short)j+49152);
+						}
+
+						if ((rxi[1]-(*x))*(ryi[2]-(*y)) < (rxi[2]-(*x))*(ryi[1]-(*y)))
+						{
+							if (clipinsideboxline(cx,cy,rxi[2],ryi[2],rxi[1],ryi[1],rad) != 0)
+								addclipline(rxi[2]-dax,ryi[2]-day,rxi[1]-day,ryi[1]+dax,(short)j+49152);
+						}
+						else if ((rxi[3]-(*x))*(ryi[0]-(*y)) < (rxi[0]-(*x))*(ryi[3]-(*y)))
+						{
+							if (clipinsideboxline(cx,cy,rxi[0],ryi[0],rxi[3],ryi[3],rad) != 0)
+								addclipline(rxi[0]+dax,ryi[0]+day,rxi[3]+day,ryi[3]-dax,(short)j+49152);
+						}
+					}
+					break;
+			}
+		}
+	} while (clipsectcnt < clipsectnum);
+
+
+	hitwall = 0;
+	cnt = clipmoveboxtracenum;
+	do
+	{
+		intx = goalx; inty = goaly;
+		if ((hitwall = raytrace(*x, *y, &intx, &inty)) >= 0)
+		{
+			lx = clipit[hitwall].x2-clipit[hitwall].x1;
+			ly = clipit[hitwall].y2-clipit[hitwall].y1;
+			templong2 = lx*lx + ly*ly;
+			if (templong2 > 0)
+			{
+				templong1 = (goalx-intx)*lx + (goaly-inty)*ly;
+
+				if ((klabs(templong1)>>11) < templong2)
+					i = divscale20(templong1,templong2);
+				else
+					i = 0;
+				goalx = mulscale20(lx,i)+intx;
+				goaly = mulscale20(ly,i)+inty;
+			}
+
+			templong1 = dmulscale6(lx,oxvect,ly,oyvect);
+			for(i=cnt+1;i<=clipmoveboxtracenum;i++)
+			{
+				j = hitwalls[i];
+				templong2 = dmulscale6(clipit[j].x2-clipit[j].x1,oxvect,clipit[j].y2-clipit[j].y1,oyvect);
+				if ((templong1^templong2) < 0)
+				{
+					updatesector(*x,*y,sectnum);
+					return(retval);
+				}
+			}
+
+			keepaway(&goalx, &goaly, hitwall);
+			xvect = ((goalx-intx)<<14);
+			yvect = ((goaly-inty)<<14);
+
+			if (cnt == clipmoveboxtracenum) retval = clipobjectval[hitwall];
+			hitwalls[cnt] = hitwall;
+		}
+		cnt--;
+
+		*x = intx;
+		*y = inty;
+	} while (((xvect|yvect) != 0) && (hitwall >= 0) && (cnt > 0));
+
+	for(j=0;j<clipsectnum;j++)
+		if (inside(*x,*y,clipsectorlist[j]) == 1)
+		{
+			*sectnum = clipsectorlist[j];
+			return(retval);
+		}
+
+	*sectnum = -1; templong1 = 0x7fffffff;
+	for(j=numsectors-1;j>=0;j--)
+		if (inside(*x,*y,j) == 1)
+		{
+			if (sector[j].ceilingstat&2)
+				templong2 = (getceilzofslope((short)j,*x,*y)-(*z));
+			else
+				templong2 = (sector[j].ceilingz-(*z));
+
+			if (templong2 > 0)
+			{
+				if (templong2 < templong1)
+					{ *sectnum = j; templong1 = templong2; }
+			}
+			else
+			{
+				if (sector[j].floorstat&2)
+					templong2 = ((*z)-getflorzofslope((short)j,*x,*y));
+				else
+					templong2 = ((*z)-sector[j].floorz);
+
+				if (templong2 <= 0)
+				{
+					*sectnum = j;
+					return(retval);
+				}
+				if (templong2 < templong1)
+					{ *sectnum = j; templong1 = templong2; }
+			}
+		}
+
+	return(retval);
+}
+
+
+int pushmove(long *x, long *y, long *z, short *sectnum,
+             long walldist, long ceildist, long flordist,
+             unsigned long cliptype)
+{
+	sectortype *sec, *sec2;
+	walltype *wal;
+	long i, j, k, t, dx, dy, dax, day, daz, daz2, bad, dir;
+	long dasprclipmask, dawalclipmask;
+	short startwall, endwall, clipsectcnt;
+	char bad2;
+
+	if ((*sectnum) < 0) return(-1);
+
+	dawalclipmask = (cliptype&65535);
+	dasprclipmask = (cliptype>>16);
+
+	k = 32;
+	dir = 1;
+	do
+	{
+		bad = 0;
+
+		clipsectorlist[0] = *sectnum;
+		clipsectcnt = 0; clipsectnum = 1;
+		do
+		{
+
+#if 0
+			/*Push FACE sprites */
+			for(i=headspritesect[clipsectorlist[clipsectcnt]];i>=0;i=nextspritesect[i])
+			{
+				spr = &sprite[i];
+				if (((spr->cstat&48) != 0) && ((spr->cstat&48) != 48)) continue;
+				if ((spr->cstat&dasprclipmask) == 0) continue;
+
+				dax = (*x)-spr->x; day = (*y)-spr->y;
+				t = (spr->clipdist<<2)+walldist;
+				if ((klabs(dax) < t) && (klabs(day) < t))
+				{
+					t = ((tilesizy[spr->picnum]*spr->yrepeat)<<2);
+					if (spr->cstat&128) daz = spr->z+(t>>1); else daz = spr->z;
+					if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+					if (((*z) < daz+ceildist) && ((*z) > daz-t-flordist))
+					{
+						t = (spr->clipdist<<2)+walldist;
+
+						j = getangle(dax,day);
+						dx = (sintable[(j+512)&2047]>>11);
+						dy = (sintable[(j)&2047]>>11);
+						bad2 = 16;
+						do
+						{
+							*x = (*x) + dx; *y = (*y) + dy;
+							bad2--; if (bad2 == 0) break;
+						} while ((klabs((*x)-spr->x) < t) && (klabs((*y)-spr->y) < t));
+						bad = -1;
+						k--; if (k <= 0) return(bad);
+						updatesector(*x,*y,sectnum);
+					}
+				}
+			}
+#endif
+
+			sec = &sector[clipsectorlist[clipsectcnt]];
+			if (dir > 0)
+				startwall = sec->wallptr, endwall = startwall + sec->wallnum;
+			else
+				endwall = sec->wallptr, startwall = endwall + sec->wallnum;
+
+			for(i=startwall,wal=&wall[startwall];i!=endwall;i+=dir,wal+=dir)
+				if (clipinsidebox(*x,*y,i,walldist-4) == 1)
+				{
+					j = 0;
+					if (wal->nextsector < 0) j = 1;
+					if (wal->cstat&dawalclipmask) j = 1;
+					if (j == 0)
+					{
+						sec2 = &sector[wal->nextsector];
+
+
+							/* Find closest point on wall (dax, day) to (*x, *y) */
+						dax = wall[wal->point2].x-wal->x;
+						day = wall[wal->point2].y-wal->y;
+						daz = dax*((*x)-wal->x) + day*((*y)-wal->y);
+						if (daz <= 0)
+							t = 0;
+						else
+						{
+							daz2 = dax*dax+day*day;
+							if (daz >= daz2) t = (1<<30); else t = divscale30(daz,daz2);
+						}
+						dax = wal->x + mulscale30(dax,t);
+						day = wal->y + mulscale30(day,t);
+
+
+						daz = getflorzofslope(clipsectorlist[clipsectcnt],dax,day);
+						daz2 = getflorzofslope(wal->nextsector,dax,day);
+						if ((daz2 < daz-(1<<8)) && ((sec2->floorstat&1) == 0))
+							if (*z >= daz2-(flordist-1)) j = 1;
+
+						daz = getceilzofslope(clipsectorlist[clipsectcnt],dax,day);
+						daz2 = getceilzofslope(wal->nextsector,dax,day);
+						if ((daz2 > daz+(1<<8)) && ((sec2->ceilingstat&1) == 0))
+							if (*z <= daz2+(ceildist-1)) j = 1;
+					}
+					if (j != 0)
+					{
+						j = getangle(wall[wal->point2].x-wal->x,wall[wal->point2].y-wal->y);
+						dx = (sintable[(j+1024)&2047]>>11);
+						dy = (sintable[(j+512)&2047]>>11);
+						bad2 = 16;
+						do
+						{
+							*x = (*x) + dx; *y = (*y) + dy;
+							bad2--; if (bad2 == 0) break;
+						} while (clipinsidebox(*x,*y,i,walldist-4) != 0);
+						bad = -1;
+						k--; if (k <= 0) return(bad);
+						updatesector(*x,*y,sectnum);
+					}
+					else
+					{
+						for(j=clipsectnum-1;j>=0;j--)
+							if (wal->nextsector == clipsectorlist[j]) break;
+						if (j < 0) clipsectorlist[clipsectnum++] = wal->nextsector;
+					}
+				}
+
+			clipsectcnt++;
+		} while (clipsectcnt < clipsectnum);
+		dir = -dir;
+	} while (bad != 0);
+
+	return(bad);
+}
+
+
+void updatesector(long x, long y, short *sectnum)
+{
+	walltype *wal;
+	long i, j;
+
+	if (inside(x,y,*sectnum) == 1) return;
+
+	if ((*sectnum >= 0) && (*sectnum < numsectors))
+	{
+		wal = &wall[sector[*sectnum].wallptr];
+		j = sector[*sectnum].wallnum;
+		do
+		{
+			i = wal->nextsector;
+			if (i >= 0)
+				if (inside(x,y,(short)i) == 1)
+				{
+					*sectnum = i;
+					return;
+				}
+			wal++;
+			j--;
+		} while (j != 0);
+	}
+
+	for(i=numsectors-1;i>=0;i--)
+		if (inside(x,y,(short)i) == 1)
+		{
+			*sectnum = i;
+			return;
+		}
+
+	*sectnum = -1;
+}
+
+
+void rotatepoint(long xpivot, long ypivot, long x, long y, short daang, long *x2, long *y2)
+{
+	long dacos, dasin;
+
+	dacos = sintable[(daang+2560)&2047];
+	dasin = sintable[(daang+2048)&2047];
+	x -= xpivot;
+	y -= ypivot;
+	*x2 = dmulscale14(x,dacos,-y,dasin) + xpivot;
+	*y2 = dmulscale14(y,dacos,x,dasin) + ypivot;
+}
+
+
+int initmouse(void)
+{
+	return(moustat = setupmouse());
+}
+
+
+void getmousevalues(short *mousx, short *mousy, short *bstatus)
+{
+	if (moustat == 0) { *mousx = 0; *mousy = 0; *bstatus = 0; return; }
+	readmousexy(mousx,mousy);
+	readmousebstatus(bstatus);
+}
+
+void draw2dgrid(long posxe, long posye, short ange, long zoome, short gride)
+{
+	long i, xp1, yp1, xp2=0, yp2, tempy, templong;
+	char mask;
+
+	if (gride > 0)
+	{
+		yp1 = 200-mulscale14(posye+131072,zoome);
+		if (yp1 < 0) yp1 = 0;
+		yp2 = 200-mulscale14(posye-131072,zoome);
+		if (yp2 >= ydim16) yp2 = ydim16-1;
+
+		if ((yp1 < ydim16) && (yp2 >= 0) && (yp2 >= yp1))
+		{
+			setcolor16(8);
+                        #ifdef PLATFORM_DOS
+			koutp(0x3ce,0x8);
+                        #endif
+                        templong = ((yp1*640+pageoffset)>>3)+(long)_getVideoBase();
+			tempy = yp2-yp1+1;
+			mask = 0;
+			xp1 = 320-mulscale14(posxe+131072,zoome);
+
+			for(i=-131072;i<=131072;i+=(2048>>gride))
+			{
+				xp2 = xp1;
+				xp1 = 320-mulscale14(posxe-i,zoome);
+
+				if (xp1 >= 640) break;
+				if (xp1 >= 0)
+				{
+
+                    #if (defined PLATFORM_DOS)
+	    				if ((xp1|7) != (xp2|7))
+    					{
+		    				koutp(0x3cf,mask);
+			    			if (((xp2>>3) >= 0) && ((xp2>>3) < 80))
+				    			vlin16first(templong+(xp2>>3),tempy);
+					    	mask = 0;
+    					}
+    					mask |= pow2char[(xp1&7)^7];
+                    #else
+                        drawline16(xp1, 0, xp1, tempy, 8);
+                    #endif
+				}
+			}
+			if ((i >= 131072) && (xp1 < 640))
+				xp2 = xp1;
+			if ((mask != 0) && ((xp2>>3) >= 0) && ((xp2>>3) < 80))
+			{
+                /* !!! Does this code ever get hit? Do something with this! */
+#ifdef PLATFORM_DOS
+				koutp(0x3cf,mask);
+				vlin16first(templong+(xp2>>3),tempy);
+#else
+				fprintf (stderr, "STUB: %s:%d\n",__FILE__,__LINE__);
+#endif
+			}
+		}
+
+		xp1 = mulscale14(posxe+131072,zoome);
+		xp2 = mulscale14(posxe-131072,zoome);
+		tempy = 0x80000000;
+		for(i=-131072;i<=131072;i+=(2048>>gride))
+		{
+			yp1 = (((posye-i)*zoome)>>14);
+			if (yp1 != tempy)
+			{
+				if ((yp1 > 200-ydim16) && (yp1 <= 200))
+				{
+					drawline16(320-xp1,200-yp1,320-xp2,200-yp1,8);
+					tempy = yp1;
+				}
+			}
+		}
+	}
+}
+
+void draw2dscreen(long posxe, long posye, short ange, long zoome, short gride)
+{
+	walltype *wal;
+	long i, j, xp1, yp1, xp2, yp2, templong;
+	char col;
+
+	if (qsetmode == 200) return;
+
+	if (editstatus == 0)
+	{
+		faketimerhandler();
+		clear2dscreen();
+
+		faketimerhandler();
+		draw2dgrid(posxe,posye,ange,zoome,gride);
+	}
+
+	faketimerhandler();
+	for(i=numwalls-1,wal=&wall[i];i>=0;i--,wal--)
+	{
+		if (editstatus == 0)
+		{
+			if ((show2dwall[i>>3]&pow2char[i&7]) == 0) continue;
+			j = wal->nextwall;
+			if ((j >= 0) && (i > j))
+				if ((show2dwall[j>>3]&pow2char[j&7]) > 0) continue;
+		}
+		else
+		{
+			j = wal->nextwall;
+			if ((j >= 0) && (i > j)) continue;
+		}
+
+		if (j < 0)
+		{
+			col = 7;
+			if (i == linehighlight) col += ((numframes&2)<<2);
+		}
+		else
+		{
+			col = 4;
+			if ((wal->cstat&1) != 0) col = 5;
+			if ((i == linehighlight) || ((linehighlight >= 0) && (i == wall[linehighlight].nextwall)))
+				col += ((numframes&2)<<2);
+		}
+
+		xp1 = mulscale14(wal->x-posxe,zoome);
+		yp1 = mulscale14(wal->y-posye,zoome);
+		xp2 = mulscale14(wall[wal->point2].x-posxe,zoome);
+		yp2 = mulscale14(wall[wal->point2].y-posye,zoome);
+
+		if ((wal->cstat&64) > 0)
+		{
+			if (klabs(xp2-xp1) >= klabs(yp2-yp1))
+			{
+				drawline16(320+xp1,200+yp1+1,320+xp2,200+yp2+1,col);
+				drawline16(320+xp1,200+yp1-1,320+xp2,200+yp2-1,col);
+			}
+			else
+			{
+				drawline16(320+xp1+1,200+yp1,320+xp2+1,200+yp2,col);
+				drawline16(320+xp1-1,200+yp1,320+xp2-1,200+yp2,col);
+			}
+			col += 8;
+		}
+		drawline16(320+xp1,200+yp1,320+xp2,200+yp2,col);
+
+		if ((zoome >= 256) && (editstatus == 1))
+			if (((320+xp1) >= 2) && ((320+xp1) <= 637))
+				if (((200+yp1) >= 2) && ((200+yp1) <= ydim16-3))
+				{
+					col = 2;
+					if (i == pointhighlight) col += ((numframes&2)<<2);
+					else if ((highlightcnt > 0) && (editstatus == 1))
+					{
+						if (show2dwall[i>>3]&pow2char[i&7])
+							col += ((numframes&2)<<2);
+					}
+
+#ifdef PLATFORM_DOS
+					templong = (mul5(200+yp1)<<7)+(320+xp1)+pageoffset;
+#else
+					templong = (mul5(200+yp1)<<7)+(320+xp1);
+#endif
+
+					setcolor16((long)col);
+
+					drawpixel16(templong-2-1280);
+					drawpixel16(templong-1-1280);
+					drawpixel16(templong+0-1280);
+					drawpixel16(templong+1-1280);
+					drawpixel16(templong+2-1280);
+
+					drawpixel16(templong-2+1280);
+					drawpixel16(templong-1+1280);
+					drawpixel16(templong+0+1280);
+					drawpixel16(templong+1+1280);
+					drawpixel16(templong+2+1280);
+
+					drawpixel16(templong-2-640);
+					drawpixel16(templong-2+0);
+					drawpixel16(templong-2+640);
+
+					drawpixel16(templong+2-640);
+					drawpixel16(templong+2+0);
+					drawpixel16(templong+2+640);
+				}
+	}
+	faketimerhandler();
+
+	if ((zoome >= 256) || (editstatus == 0))
+		for(i=0;i<numsectors;i++)
+			for(j=headspritesect[i];j>=0;j=nextspritesect[j])
+				if ((editstatus == 1) || (show2dsprite[j>>3]&pow2char[j&7]))
+				{
+					col = 3;
+					if ((sprite[j].cstat&1) > 0) col = 5;
+					if (editstatus == 1)
+					{
+						if (j+16384 == pointhighlight)
+							col += ((numframes&2)<<2);
+						else if ((highlightcnt > 0) && (editstatus == 1))
+						{
+							if (show2dsprite[j>>3]&pow2char[j&7])
+								col += ((numframes&2)<<2);
+						}
+					}
+
+					xp1 = mulscale14(sprite[j].x-posxe,zoome);
+					yp1 = mulscale14(sprite[j].y-posye,zoome);
+					if (((320+xp1) >= 2) && ((320+xp1) <= 637))
+						if (((200+yp1) >= 2) && ((200+yp1) <= ydim16-3))
+						{
+#ifdef PLATFORM_DOS
+							templong = (mul5(200+yp1)<<7)+(320+xp1)+pageoffset;
+#else
+							templong = (mul5(200+yp1)<<7)+(320+xp1);
+#endif
+
+							setcolor16((long)col);
+							drawpixel16(templong-1-1280);
+							drawpixel16(templong+0-1280);
+							drawpixel16(templong+1-1280);
+
+							drawpixel16(templong-1+1280);
+							drawpixel16(templong+0+1280);
+							drawpixel16(templong+1+1280);
+
+							drawpixel16(templong-2-640);
+							drawpixel16(templong-2+0);
+							drawpixel16(templong-2+640);
+
+       						drawpixel16(templong+2-640);
+							drawpixel16(templong+2+0);
+							drawpixel16(templong+2+640);
+
+							drawpixel16(templong+1+640);
+							drawpixel16(templong-1+640);
+							drawpixel16(templong+1-640);
+							drawpixel16(templong-1-640);
+
+							xp2 = mulscale11(sintable[(sprite[j].ang+2560)&2047],zoome) / 768;
+							yp2 = mulscale11(sintable[(sprite[j].ang+2048)&2047],zoome) / 768;
+
+							if ((sprite[j].cstat&256) > 0)
+							{
+								if (((sprite[j].ang+256)&512) == 0)
+								{
+									drawline16(320+xp1,200+yp1-1,320+xp1+xp2,200+yp1+yp2-1,col);
+									drawline16(320+xp1,200+yp1+1,320+xp1+xp2,200+yp1+yp2+1,col);
+								}
+								else
+								{
+									drawline16(320+xp1-1,200+yp1,320+xp1+xp2-1,200+yp1+yp2,col);
+									drawline16(320+xp1+1,200+yp1,320+xp1+xp2+1,200+yp1+yp2,col);
+								}
+								col += 8;
+							}
+							drawline16(320+xp1,200+yp1,320+xp1+xp2,200+yp1+yp2,col);
+						}
+				}
+
+	faketimerhandler();
+	xp1 = mulscale11(sintable[(ange+2560)&2047],zoome) / 768; /* Draw white arrow */
+	yp1 = mulscale11(sintable[(ange+2048)&2047],zoome) / 768;
+	drawline16(320+xp1,200+yp1,320-xp1,200-yp1,15);
+	drawline16(320+xp1,200+yp1,320+yp1,200-xp1,15);
+	drawline16(320+xp1,200+yp1,320-yp1,200+xp1,15);
+}
+
+
+/*
+ * This is ryan's change. SDL requires me to call SDL_UpdateRect() to force
+ *  vid updates without a SDL_Flip() call, but there's no such thing in the
+ *  DOS version of this program, so text won't show up sometimes without
+ *  my update call in Linux.  However, to get a nice shadow effect on some
+ *  text, Ken draws a string at an offset and darker, and then on top of it
+ *  draws the actual string. Two SDL_UpdateRect() calls in over top of each
+ *  other cause flicker, so I have this function here so the shadow can
+ *  be drawn with _noupdate, and the actual string is draw with an update.
+ */
+static void __printext256(long xpos, long ypos, short col, short backcol, char name[82], char fontsize, int should_update)
+{
+	long stx, i, x, y, charxsiz;
+	char *fontptr, *letptr, *ptr;
+
+	stx = xpos;
+
+	if (fontsize) { fontptr = smalltextfont; charxsiz = 4; }
+				else { fontptr = textfont; charxsiz = 8; }
+
+	for(i=0;name[i];i++)
+	{
+		letptr = &fontptr[name[i]<<3];
+		ptr = (char *)(ylookup[ypos+7]+(stx-fontsize)+frameplace);
+		for(y=7;y>=0;y--)
+		{
+			for(x=charxsiz-1;x>=0;x--)
+			{
+				if (letptr[y]&pow2char[7-fontsize-x])
+					ptr[x] = (char)col;
+				else if (backcol >= 0)
+					ptr[x] = (char)backcol;
+			}
+			ptr -= ylookup[1];
+		}
+		stx += charxsiz;
+	}
+
+    if (should_update)
+        _updateScreenRect(xpos, ypos, charxsiz * i, 8);
+}
+
+
+void printext256(long xpos, long ypos, short col, short backcol, char name[82], char fontsize)
+{
+    __printext256(xpos, ypos, col, backcol, name, fontsize, 1);
+}
+
+
+void printext256_noupdate(long xpos, long ypos, short col, short backcol, char name[82], char fontsize)
+{
+    __printext256(xpos, ypos, col, backcol, name, fontsize, 0);
+}
+
+
+#ifdef DBGRECORD
+int krand(int line, char* file)
+{
+	int i, k=0;
+	char filename[2048];
+	FILE *pFile;
+#else
+int krand()
+{
+#endif
+
+	randomseed = (randomseed*27584621)+1;
+
+#ifdef  DBGRECORD
+	for(i=0; file[i]; i++)
+	{
+		if(file[i]=='\\')
+		{
+			i++;
+			k = 0;
+		}
+		filename[k++]=(file[i]=='.')?0:file[i];
+	}
+	// printf("%-5d %-8s %-8x\n", line, filename, randomseed);
+	pFile = fopen("c:\\temp\\krand.txt","a");
+	fprintf(pFile,"%-4d %-5s %-6x\n", line, filename, randomseed);
+	fclose(pFile);
+#endif
+
+	return(((unsigned long)randomseed)>>16);
+}
+
+
+void getzrange(long x, long y, long z, short sectnum,
+               long *ceilz, long *ceilhit, long *florz, long *florhit,
+               long walldist, unsigned long cliptype)
+{
+	sectortype *sec;
+	walltype *wal, *wal2;
+	spritetype *spr;
+	long clipsectcnt, startwall, endwall, tilenum, xoff, yoff, dax, day;
+	long xmin, ymin, xmax, ymax, i, j, k, l, daz, daz2, dx, dy;
+	long x1, y1, x2, y2, x3, y3, x4, y4, ang, cosang, sinang;
+	long xspan, yspan, xrepeat, yrepeat, dasprclipmask, dawalclipmask;
+	short cstat;
+	char clipyou;
+
+	if (sectnum < 0)
+	{
+		*ceilz = 0x80000000; *ceilhit = -1;
+		*florz = 0x7fffffff; *florhit = -1;
+		return;
+	}
+
+		/* Extra walldist for sprites on sector lines */
+	i = walldist+MAXCLIPDIST+1;
+	xmin = x-i; ymin = y-i;
+	xmax = x+i; ymax = y+i;
+
+	getzsofslope(sectnum,x,y,ceilz,florz);
+	*ceilhit = sectnum+16384; *florhit = sectnum+16384;
+
+	dawalclipmask = (cliptype&65535);
+	dasprclipmask = (cliptype>>16);
+
+	clipsectorlist[0] = sectnum;
+	clipsectcnt = 0; clipsectnum = 1;
+
+	do  /* Collect sectors inside your square first */
+	{
+		sec = &sector[clipsectorlist[clipsectcnt]];
+		startwall = sec->wallptr; endwall = startwall + sec->wallnum;
+		for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
+		{
+			k = wal->nextsector;
+			if (k >= 0)
+			{
+				wal2 = &wall[wal->point2];
+				x1 = wal->x; x2 = wal2->x;
+				if ((x1 < xmin) && (x2 < xmin)) continue;
+				if ((x1 > xmax) && (x2 > xmax)) continue;
+				y1 = wal->y; y2 = wal2->y;
+				if ((y1 < ymin) && (y2 < ymin)) continue;
+				if ((y1 > ymax) && (y2 > ymax)) continue;
+
+				dx = x2-x1; dy = y2-y1;
+				if (dx*(y-y1) < (x-x1)*dy) continue; /* back */
+				if (dx > 0) dax = dx*(ymin-y1); else dax = dx*(ymax-y1);
+				if (dy > 0) day = dy*(xmax-x1); else day = dy*(xmin-x1);
+				if (dax >= day) continue;
+
+				if (wal->cstat&dawalclipmask) continue;
+				sec = &sector[k];
+				if (editstatus == 0)
+				{
+					if (((sec->ceilingstat&1) == 0) && (z <= sec->ceilingz+(3<<8))) continue;
+					if (((sec->floorstat&1) == 0) && (z >= sec->floorz-(3<<8))) continue;
+				}
+
+				for(i=clipsectnum-1;i>=0;i--) if (clipsectorlist[i] == k) break;
+				if (i < 0) clipsectorlist[clipsectnum++] = k;
+
+				if ((x1 < xmin+MAXCLIPDIST) && (x2 < xmin+MAXCLIPDIST)) continue;
+				if ((x1 > xmax-MAXCLIPDIST) && (x2 > xmax-MAXCLIPDIST)) continue;
+				if ((y1 < ymin+MAXCLIPDIST) && (y2 < ymin+MAXCLIPDIST)) continue;
+				if ((y1 > ymax-MAXCLIPDIST) && (y2 > ymax-MAXCLIPDIST)) continue;
+				if (dx > 0) dax += dx*MAXCLIPDIST; else dax -= dx*MAXCLIPDIST;
+				if (dy > 0) day -= dy*MAXCLIPDIST; else day += dy*MAXCLIPDIST;
+				if (dax >= day) continue;
+
+					/* It actually got here, through all the continue's! */
+				getzsofslope((short)k,x,y,&daz,&daz2);
+				if (daz > *ceilz) { *ceilz = daz; *ceilhit = k+16384; }
+				if (daz2 < *florz) { *florz = daz2; *florhit = k+16384; }
+			}
+		}
+		clipsectcnt++;
+	} while (clipsectcnt < clipsectnum);
+
+	for(i=0;i<clipsectnum;i++)
+	{
+		for(j=headspritesect[clipsectorlist[i]];j>=0;j=nextspritesect[j])
+		{
+			spr = &sprite[j];
+			cstat = spr->cstat;
+			if (cstat&dasprclipmask)
+			{
+				x1 = spr->x; y1 = spr->y;
+
+				clipyou = 0;
+				switch(cstat&48)
+				{
+					case 0:
+						k = walldist+(spr->clipdist<<2)+1;
+						if ((klabs(x1-x) <= k) && (klabs(y1-y) <= k))
+						{
+							daz = spr->z;
+							k = ((tilesizy[spr->picnum]*spr->yrepeat)<<1);
+							if (cstat&128) daz += k;
+							if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+							daz2 = daz - (k<<1);
+							clipyou = 1;
+						}
+						break;
+					case 16:
+						tilenum = spr->picnum;
+						xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+						if ((cstat&4) > 0) xoff = -xoff;
+						k = spr->ang; l = spr->xrepeat;
+						dax = sintable[k&2047]*l; day = sintable[(k+1536)&2047]*l;
+						l = tilesizx[tilenum]; k = (l>>1)+xoff;
+						x1 -= mulscale16(dax,k); x2 = x1+mulscale16(dax,l);
+						y1 -= mulscale16(day,k); y2 = y1+mulscale16(day,l);
+						if (clipinsideboxline(x,y,x1,y1,x2,y2,walldist+1) != 0)
+						{
+							daz = spr->z; k = ((tilesizy[spr->picnum]*spr->yrepeat)<<1);
+							if (cstat&128) daz += k;
+							if (picanm[spr->picnum]&0x00ff0000) daz -= ((long)((signed char)((picanm[spr->picnum]>>16)&255))*spr->yrepeat<<2);
+							daz2 = daz-(k<<1);
+							clipyou = 1;
+						}
+						break;
+					case 32:
+						daz = spr->z; daz2 = daz;
+
+						if ((cstat&64) != 0)
+							if ((z > daz) == ((cstat&8)==0)) continue;
+
+						tilenum = spr->picnum;
+						xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+						yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
+						if ((cstat&4) > 0) xoff = -xoff;
+						if ((cstat&8) > 0) yoff = -yoff;
+
+						ang = spr->ang;
+						cosang = sintable[(ang+512)&2047]; sinang = sintable[ang];
+						xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
+						yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;
+
+						dax = ((xspan>>1)+xoff)*xrepeat; day = ((yspan>>1)+yoff)*yrepeat;
+						x1 += dmulscale16(sinang,dax,cosang,day)-x;
+						y1 += dmulscale16(sinang,day,-cosang,dax)-y;
+						l = xspan*xrepeat;
+						x2 = x1 - mulscale16(sinang,l);
+						y2 = y1 + mulscale16(cosang,l);
+						l = yspan*yrepeat;
+						k = -mulscale16(cosang,l); x3 = x2+k; x4 = x1+k;
+						k = -mulscale16(sinang,l); y3 = y2+k; y4 = y1+k;
+
+						dax = mulscale14(sintable[(spr->ang-256+512)&2047],walldist+4);
+						day = mulscale14(sintable[(spr->ang-256)&2047],walldist+4);
+						x1 += dax; x2 -= day; x3 -= dax; x4 += day;
+						y1 += day; y2 += dax; y3 -= day; y4 -= dax;
+
+						if ((y1^y2) < 0)
+						{
+							if ((x1^x2) < 0) clipyou ^= (x1*y2<x2*y1)^(y1<y2);
+							else if (x1 >= 0) clipyou ^= 1;
+						}
+						if ((y2^y3) < 0)
+						{
+							if ((x2^x3) < 0) clipyou ^= (x2*y3<x3*y2)^(y2<y3);
+							else if (x2 >= 0) clipyou ^= 1;
+						}
+						if ((y3^y4) < 0)
+						{
+							if ((x3^x4) < 0) clipyou ^= (x3*y4<x4*y3)^(y3<y4);
+							else if (x3 >= 0) clipyou ^= 1;
+						}
+						if ((y4^y1) < 0)
+						{
+							if ((x4^x1) < 0) clipyou ^= (x4*y1<x1*y4)^(y4<y1);
+							else if (x4 >= 0) clipyou ^= 1;
+						}
+						break;
+				}
+
+				if (clipyou != 0)
+				{
+					if ((z > daz) && (daz > *ceilz)) { *ceilz = daz; *ceilhit = j+49152; }
+					if ((z < daz2) && (daz2 < *florz)) { *florz = daz2; *florhit = j+49152; }
+				}
+			}
+		}
+	}
+}
+
+
+void setview(long x1, long y1, long x2, long y2)
+{
+	long i;
+
+	windowx1 = x1; wx1 = (x1<<12);
+	windowy1 = y1; wy1 = (y1<<12);
+	windowx2 = x2; wx2 = ((x2+1)<<12);
+	windowy2 = y2; wy2 = ((y2+1)<<12);
+
+	xdimen = (x2-x1)+1; halfxdimen = (xdimen>>1);
+	xdimenrecip = divscale32(1L,xdimen);
+	ydimen = (y2-y1)+1;
+
+	setaspect(65536L,(long)divscale16(ydim*320L,xdim*200L));
+
+	for(i=0;i<windowx1;i++) { startumost[i] = 1, startdmost[i] = 0; }
+	for(i=windowx1;i<=windowx2;i++)
+		{ startumost[i] = windowy1, startdmost[i] = windowy2+1; }
+	for(i=windowx2+1;i<xdim;i++) { startumost[i] = 1, startdmost[i] = 0; }
+
+	viewoffset = windowy1*bytesperline + windowx1;
+
+	if ((stereomode) || (vidoption == 6))
+	{
+		ostereopixelwidth = stereopixelwidth;
+		xdimen = (windowx2-windowx1+1)+(stereopixelwidth<<1); halfxdimen = (xdimen>>1);
+		xdimenrecip = divscale32(1L,xdimen);
+		setaspect((long)divscale16(xdimen,windowx2-windowx1+1),yxaspect);
+	}
+}
+
+
+void setaspect(long daxrange, long daaspect)
+{
+	viewingrange = daxrange;
+	viewingrangerecip = divscale32(1L,daxrange);
+
+	yxaspect = daaspect;
+	xyaspect = divscale32(1,yxaspect);
+	xdimenscale = scale(xdimen,yxaspect,320);
+	xdimscale = scale(320,xyaspect,xdimen);
+}
+
+
+void flushperms(void)
+{
+	permhead = permtail = 0;
+}
+
+
+void rotatesprite(long sx, long sy, long z, short a, short picnum,
+                  signed char dashade, char dapalnum, char dastat,
+                  long cx1, long cy1, long cx2, long cy2)
+{
+	long i;
+	permfifotype *per, *per2;
+
+	if ((cx1 > cx2) || (cy1 > cy2)) return;
+	if (z <= 16) return;
+	if (picanm[picnum]&192) picnum += animateoffs(picnum,(short)0xc000);
+	if ((tilesizx[picnum] <= 0) || (tilesizy[picnum] <= 0)) return;
+
+	if (((dastat&128) == 0) || (numpages < 2) || (beforedrawrooms != 0))
+		dorotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,cx1,cy1,cx2,cy2);
+
+	if ((dastat&64) && (cx1 <= 0) && (cy1 <= 0) && (cx2 >= xdim-1) && (cy2 >= ydim-1) &&
+		 (sx == (160<<16)) && (sy == (100<<16)) && (z == 65536L) && (a == 0) && ((dastat&1) == 0))
+		permhead = permtail = 0;
+
+	if ((dastat&128) == 0) return;
+	if (numpages >= 2)
+	{
+		per = &permfifo[permhead];
+		per->sx = sx; per->sy = sy; per->z = z; per->a = a;
+		per->picnum = picnum;
+		per->dashade = dashade; per->dapalnum = dapalnum;
+		per->dastat = dastat;
+		per->pagesleft = numpages+((beforedrawrooms&1)<<7);
+		per->cx1 = cx1; per->cy1 = cy1; per->cx2 = cx2; per->cy2 = cy2;
+
+			/* Would be better to optimize out true bounding boxes */
+		if (dastat&64)  /* If non-masking write, checking for overlapping cases */
+		{
+			for(i=permtail;i!=permhead;i=((i+1)&(MAXPERMS-1)))
+			{
+				per2 = &permfifo[i];
+				if ((per2->pagesleft&127) == 0) continue;
+				if (per2->sx != per->sx) continue;
+				if (per2->sy != per->sy) continue;
+				if (per2->z != per->z) continue;
+				if (per2->a != per->a) continue;
+				if (tilesizx[per2->picnum] > tilesizx[per->picnum]) continue;
+				if (tilesizy[per2->picnum] > tilesizy[per->picnum]) continue;
+				if (per2->cx1 < per->cx1) continue;
+				if (per2->cy1 < per->cy1) continue;
+				if (per2->cx2 > per->cx2) continue;
+				if (per2->cy2 > per->cy2) continue;
+				per2->pagesleft = 0;
+			}
+			if ((per->z == 65536) && (per->a == 0))
+				for(i=permtail;i!=permhead;i=((i+1)&(MAXPERMS-1)))
+				{
+					per2 = &permfifo[i];
+					if ((per2->pagesleft&127) == 0) continue;
+					if (per2->z != 65536) continue;
+					if (per2->a != 0) continue;
+					if (per2->cx1 < per->cx1) continue;
+					if (per2->cy1 < per->cy1) continue;
+					if (per2->cx2 > per->cx2) continue;
+					if (per2->cy2 > per->cy2) continue;
+					if ((per2->sx>>16) < (per->sx>>16)) continue;
+					if ((per2->sy>>16) < (per->sy>>16)) continue;
+					if ((per2->sx>>16)+tilesizx[per2->picnum] > (per->sx>>16)+tilesizx[per->picnum]) continue;
+					if ((per2->sy>>16)+tilesizy[per2->picnum] > (per->sy>>16)+tilesizy[per->picnum]) continue;
+					per2->pagesleft = 0;
+				}
+		}
+
+		permhead = ((permhead+1)&(MAXPERMS-1));
+	}
+}
+
+
+static int getclosestcol(long r, long g, long b)
+{
+	long i, j, k, dist, mindist, retcol;
+	char *pal1;
+
+	j = (r>>3)*FASTPALGRIDSIZ*FASTPALGRIDSIZ+(g>>3)*FASTPALGRIDSIZ+(b>>3)+FASTPALGRIDSIZ*FASTPALGRIDSIZ+FASTPALGRIDSIZ+1;
+	mindist = min(rdist[coldist[r&7]+64+8],gdist[coldist[g&7]+64+8]);
+	mindist = min(mindist,bdist[coldist[b&7]+64+8]);
+	mindist++;
+
+	r = 64-r; g = 64-g; b = 64-b;
+
+	retcol = -1;
+	for(k=26;k>=0;k--)
+	{
+		i = colscan[k]+j; if ((colhere[i>>3]&pow2char[i&7]) == 0) continue;
+		i = colhead[i];
+		do
+		{
+			pal1 = (char *)&palette[i*3];
+			dist = gdist[pal1[1]+g];
+			if (dist < mindist)
+			{
+				dist += rdist[pal1[0]+r];
+				if (dist < mindist)
+				{
+					dist += bdist[pal1[2]+b];
+					if (dist < mindist) { mindist = dist; retcol = i; }
+				}
+			}
+			i = colnext[i];
+		} while (i >= 0);
+	}
+	if (retcol >= 0) return(retcol);
+
+	mindist = 0x7fffffff;
+	pal1 = (char *)&palette[768-3];
+	for(i=255;i>=0;i--,pal1-=3)
+	{
+		dist = gdist[pal1[1]+g]; if (dist >= mindist) continue;
+		dist += rdist[pal1[0]+r]; if (dist >= mindist) continue;
+		dist += bdist[pal1[2]+b]; if (dist >= mindist) continue;
+		mindist = dist; retcol = i;
+	}
+	return(retcol);
+}
+
+
+void makepalookup(long palnum, char *remapbuf, signed char r,
+                  signed char g, signed char b, char dastat)
+{
+	long i, j, dist, palscale;
+	char *ptr, *ptr2;
+
+	if (paletteloaded == 0) return;
+
+	if (palookup[palnum] == NULL)
+	{
+			/* Allocate palookup buffer */
+		if ((palookup[palnum] = (char *)kkmalloc(numpalookups<<8)) == NULL)
+			allocache((long *)&palookup[palnum],numpalookups<<8,&permanentlock);
+	}
+
+	if (dastat == 0) return;
+	if ((r|g|b|63) != 63) return;
+
+	if ((r|g|b) == 0)
+	{
+		for(i=0;i<256;i++)
+		{
+			ptr = (char *)(FP_OFF(palookup[0])+remapbuf[i]);
+			ptr2 = (char *)(FP_OFF(palookup[palnum])+i);
+			for(j=0;j<numpalookups;j++)
+				{ *ptr2 = *ptr; ptr += 256; ptr2 += 256; }
+		}
+	}
+	else
+	{
+		ptr2 = (char *)FP_OFF(palookup[palnum]);
+		for(i=0;i<numpalookups;i++)
+		{
+			palscale = divscale16(i,numpalookups);
+			for(j=0;j<256;j++)
+			{
+				ptr = (char *)&palette[remapbuf[j]*3];
+				*ptr2++ = getclosestcol((long)ptr[0]+mulscale16(r-ptr[0],palscale),
+												(long)ptr[1]+mulscale16(g-ptr[1],palscale),
+												(long)ptr[2]+mulscale16(b-ptr[2],palscale));
+			}
+		}
+	}
+
+	if ((vidoption == 6) && (qsetmode == 200))
+	{
+		for(i=0;i<256;i++)
+		{
+			dist = palette[i*3]*3+palette[i*3+1]*5+palette[i*3+2]*2;
+			ptr = (char *)(FP_OFF(palookup[palnum])+i);
+			for(j=0;j<32;j++)
+				ptr[j<<8] = (char)min(max(mulscale10(dist,32-j),0),15);
+		}
+	}
+}
+
+
+void setbrightness(char dabrightness, unsigned char *dapal)
+{
+	long i, j, k;
+
+	curbrightness = min(max((long)dabrightness,0),15);
+
+	k = 0;
+	if (vidoption == 6)
+	{
+		for(j=0;j<16;j++)
+			for(i=0;i<16;i++)
+			{
+				tempbuf[k++] = britable[curbrightness][j<<2];
+				tempbuf[k++] = 0;
+				tempbuf[k++] = britable[curbrightness][i<<2];
+				tempbuf[k++] = 0;
+			}
+	}
+	else
+	{
+		for(i=0;i<256;i++)
+		{
+			tempbuf[k++] = britable[curbrightness][dapal[i*3+2]];
+			tempbuf[k++] = britable[curbrightness][dapal[i*3+1]];
+			tempbuf[k++] = britable[curbrightness][dapal[i*3+0]];
+			tempbuf[k++] = 0;
+		}
+	}
+
+	VBE_setPalette(0, 256, (char *) tempbuf);
+}
+
+
+static void fillpolygon(long npoints)
+{
+	long z, zz, x1, y1, x2, y2, miny, maxy, y, xinc, cnt;
+	long ox, oy, bx, by, p, day1, day2;
+	short *ptr, *ptr2;
+
+	miny = 0x7fffffff; maxy = 0x80000000;
+	for(z=npoints-1;z>=0;z--)
+		{ y = ry1[z]; miny = min(miny,y); maxy = max(maxy,y); }
+	miny = (miny>>12); maxy = (maxy>>12);
+	if (miny < 0) miny = 0;
+	if (maxy >= ydim) maxy = ydim-1;
+	ptr = smost;    /* They're pointers! - watch how you optimize this thing */
+	for(y=miny;y<=maxy;y++)
+	{
+		dotp1[y] = ptr; dotp2[y] = ptr+(MAXNODESPERLINE>>1);
+		ptr += MAXNODESPERLINE;
+	}
+
+	for(z=npoints-1;z>=0;z--)
+	{
+		zz = xb1[z];
+		y1 = ry1[z]; day1 = (y1>>12);
+		y2 = ry1[zz]; day2 = (y2>>12);
+		if (day1 != day2)
+		{
+			x1 = rx1[z]; x2 = rx1[zz];
+			xinc = divscale12(x2-x1,y2-y1);
+			if (day2 > day1)
+			{
+				x1 += mulscale12((day1<<12)+4095-y1,xinc);
+				for(y=day1;y<day2;y++) { *dotp2[y]++ = (x1>>12); x1 += xinc; }
+			}
+			else
+			{
+				x2 += mulscale12((day2<<12)+4095-y2,xinc);
+				for(y=day2;y<day1;y++) { *dotp1[y]++ = (x2>>12); x2 += xinc; }
+			}
+		}
+	}
+
+	globalx1 = mulscale16(globalx1,xyaspect);
+	globaly2 = mulscale16(globaly2,xyaspect);
+
+	oy = miny+1-(ydim>>1);
+	globalposx += oy*globalx1;
+	globalposy += oy*globaly2;
+
+	setuphlineasm4(asm1,asm2);
+
+	ptr = smost;
+	for(y=miny;y<=maxy;y++)
+	{
+		cnt = dotp1[y]-ptr; ptr2 = ptr+(MAXNODESPERLINE>>1);
+		for(z=cnt-1;z>=0;z--)
+		{
+			day1 = 0; day2 = 0;
+			for(zz=z;zz>0;zz--)
+			{
+				if (ptr[zz] < ptr[day1]) day1 = zz;
+				if (ptr2[zz] < ptr2[day2]) day2 = zz;
+			}
+			x1 = ptr[day1]; ptr[day1] = ptr[z];
+			x2 = ptr2[day2]-1; ptr2[day2] = ptr2[z];
+			if (x1 > x2) continue;
+
+			if (globalpolytype < 1)
+			{
+					/* maphline */
+				ox = x2+1-(xdim>>1);
+				bx = ox*asm1 + globalposx;
+				by = ox*asm2 - globalposy;
+
+				p = ylookup[y]+x2+frameplace;
+				hlineasm4(x2-x1,-1L,globalshade<<8,by,bx,p);
+			}
+			else
+			{
+					/* maphline */
+				ox = x1+1-(xdim>>1);
+				bx = ox*asm1 + globalposx;
+				by = ox*asm2 - globalposy;
+
+				p = ylookup[y]+x1+frameplace;
+				if (globalpolytype == 1)
+					mhline(globalbufplc,bx,(x2-x1)<<16,0L,by,p);
+				else
+				{
+					thline(globalbufplc,bx,(x2-x1)<<16,0L,by,p);
+					transarea += (x2-x1);
+				}
+			}
+		}
+		globalposx += globalx1;
+		globalposy += globaly2;
+		ptr += MAXNODESPERLINE;
+	}
+	faketimerhandler();
+}
+
+
+static int clippoly (long npoints, long clipstat)
+{
+	long z, zz, s1, s2, t, npoints2, start2, z1, z2, z3, z4, splitcnt;
+	long cx1, cy1, cx2, cy2;
+
+	cx1 = windowx1;
+	cy1 = windowy1;
+	cx2 = windowx2+1;
+	cy2 = windowy2+1;
+	cx1 <<= 12; cy1 <<= 12; cx2 <<= 12; cy2 <<= 12;
+
+	if (clipstat&0xa)   /* Need to clip top or left */
+	{
+		npoints2 = 0; start2 = 0; z = 0; splitcnt = 0;
+		do
+		{
+			s2 = cx1-rx1[z];
+			do
+			{
+				zz = xb1[z]; xb1[z] = -1;
+				s1 = s2; s2 = cx1-rx1[zz];
+				if (s1 < 0)
+				{
+					rx2[npoints2] = rx1[z]; ry2[npoints2] = ry1[z];
+					xb2[npoints2] = npoints2+1; npoints2++;
+				}
+				if ((s1^s2) < 0)
+				{
+					rx2[npoints2] = rx1[z]+scale(rx1[zz]-rx1[z],s1,s1-s2);
+					ry2[npoints2] = ry1[z]+scale(ry1[zz]-ry1[z],s1,s1-s2);
+					if (s1 < 0) p2[splitcnt++] = npoints2;
+					xb2[npoints2] = npoints2+1;
+					npoints2++;
+				}
+				z = zz;
+			} while (xb1[z] >= 0);
+
+			if (npoints2 >= start2+3)
+				xb2[npoints2-1] = start2, start2 = npoints2;
+			else
+				npoints2 = start2;
+
+			z = 1;
+			while ((z < npoints) && (xb1[z] < 0)) z++;
+		} while (z < npoints);
+		if (npoints2 <= 2) return(0);
+
+		for(z=1;z<splitcnt;z++)
+			for(zz=0;zz<z;zz++)
+			{
+				z1 = p2[z]; z2 = xb2[z1]; z3 = p2[zz]; z4 = xb2[z3];
+				s1  = klabs(rx2[z1]-rx2[z2])+klabs(ry2[z1]-ry2[z2]);
+				s1 += klabs(rx2[z3]-rx2[z4])+klabs(ry2[z3]-ry2[z4]);
+				s2  = klabs(rx2[z1]-rx2[z4])+klabs(ry2[z1]-ry2[z4]);
+				s2 += klabs(rx2[z3]-rx2[z2])+klabs(ry2[z3]-ry2[z2]);
+				if (s2 < s1)
+					{ t = xb2[p2[z]]; xb2[p2[z]] = xb2[p2[zz]]; xb2[p2[zz]] = t; }
+			}
+
+
+		npoints = 0; start2 = 0; z = 0; splitcnt = 0;
+		do
+		{
+			s2 = cy1-ry2[z];
+			do
+			{
+				zz = xb2[z]; xb2[z] = -1;
+				s1 = s2; s2 = cy1-ry2[zz];
+				if (s1 < 0)
+				{
+					rx1[npoints] = rx2[z]; ry1[npoints] = ry2[z];
+					xb1[npoints] = npoints+1; npoints++;
+				}
+				if ((s1^s2) < 0)
+				{
+					rx1[npoints] = rx2[z]+scale(rx2[zz]-rx2[z],s1,s1-s2);
+					ry1[npoints] = ry2[z]+scale(ry2[zz]-ry2[z],s1,s1-s2);
+					if (s1 < 0) p2[splitcnt++] = npoints;
+					xb1[npoints] = npoints+1;
+					npoints++;
+				}
+				z = zz;
+			} while (xb2[z] >= 0);
+
+			if (npoints >= start2+3)
+				xb1[npoints-1] = start2, start2 = npoints;
+			else
+				npoints = start2;
+
+			z = 1;
+			while ((z < npoints2) && (xb2[z] < 0)) z++;
+		} while (z < npoints2);
+		if (npoints <= 2) return(0);
+
+		for(z=1;z<splitcnt;z++)
+			for(zz=0;zz<z;zz++)
+			{
+				z1 = p2[z]; z2 = xb1[z1]; z3 = p2[zz]; z4 = xb1[z3];
+				s1  = klabs(rx1[z1]-rx1[z2])+klabs(ry1[z1]-ry1[z2]);
+				s1 += klabs(rx1[z3]-rx1[z4])+klabs(ry1[z3]-ry1[z4]);
+				s2  = klabs(rx1[z1]-rx1[z4])+klabs(ry1[z1]-ry1[z4]);
+				s2 += klabs(rx1[z3]-rx1[z2])+klabs(ry1[z3]-ry1[z2]);
+				if (s2 < s1)
+					{ t = xb1[p2[z]]; xb1[p2[z]] = xb1[p2[zz]]; xb1[p2[zz]] = t; }
+			}
+	}
+	if (clipstat&0x5)   /* Need to clip bottom or right */
+	{
+		npoints2 = 0; start2 = 0; z = 0; splitcnt = 0;
+		do
+		{
+			s2 = rx1[z]-cx2;
+			do
+			{
+				zz = xb1[z]; xb1[z] = -1;
+				s1 = s2; s2 = rx1[zz]-cx2;
+				if (s1 < 0)
+				{
+					rx2[npoints2] = rx1[z]; ry2[npoints2] = ry1[z];
+					xb2[npoints2] = npoints2+1; npoints2++;
+				}
+				if ((s1^s2) < 0)
+				{
+					rx2[npoints2] = rx1[z]+scale(rx1[zz]-rx1[z],s1,s1-s2);
+					ry2[npoints2] = ry1[z]+scale(ry1[zz]-ry1[z],s1,s1-s2);
+					if (s1 < 0) p2[splitcnt++] = npoints2;
+					xb2[npoints2] = npoints2+1;
+					npoints2++;
+				}
+				z = zz;
+			} while (xb1[z] >= 0);
+
+			if (npoints2 >= start2+3)
+				xb2[npoints2-1] = start2, start2 = npoints2;
+			else
+				npoints2 = start2;
+
+			z = 1;
+			while ((z < npoints) && (xb1[z] < 0)) z++;
+		} while (z < npoints);
+		if (npoints2 <= 2) return(0);
+
+		for(z=1;z<splitcnt;z++)
+			for(zz=0;zz<z;zz++)
+			{
+				z1 = p2[z]; z2 = xb2[z1]; z3 = p2[zz]; z4 = xb2[z3];
+				s1  = klabs(rx2[z1]-rx2[z2])+klabs(ry2[z1]-ry2[z2]);
+				s1 += klabs(rx2[z3]-rx2[z4])+klabs(ry2[z3]-ry2[z4]);
+				s2  = klabs(rx2[z1]-rx2[z4])+klabs(ry2[z1]-ry2[z4]);
+				s2 += klabs(rx2[z3]-rx2[z2])+klabs(ry2[z3]-ry2[z2]);
+				if (s2 < s1)
+					{ t = xb2[p2[z]]; xb2[p2[z]] = xb2[p2[zz]]; xb2[p2[zz]] = t; }
+			}
+
+
+		npoints = 0; start2 = 0; z = 0; splitcnt = 0;
+		do
+		{
+			s2 = ry2[z]-cy2;
+			do
+			{
+				zz = xb2[z]; xb2[z] = -1;
+				s1 = s2; s2 = ry2[zz]-cy2;
+				if (s1 < 0)
+				{
+					rx1[npoints] = rx2[z]; ry1[npoints] = ry2[z];
+					xb1[npoints] = npoints+1; npoints++;
+				}
+				if ((s1^s2) < 0)
+				{
+					rx1[npoints] = rx2[z]+scale(rx2[zz]-rx2[z],s1,s1-s2);
+					ry1[npoints] = ry2[z]+scale(ry2[zz]-ry2[z],s1,s1-s2);
+					if (s1 < 0) p2[splitcnt++] = npoints;
+					xb1[npoints] = npoints+1;
+					npoints++;
+				}
+				z = zz;
+			} while (xb2[z] >= 0);
+
+			if (npoints >= start2+3)
+				xb1[npoints-1] = start2, start2 = npoints;
+			else
+				npoints = start2;
+
+			z = 1;
+			while ((z < npoints2) && (xb2[z] < 0)) z++;
+		} while (z < npoints2);
+		if (npoints <= 2) return(0);
+
+		for(z=1;z<splitcnt;z++)
+			for(zz=0;zz<z;zz++)
+			{
+				z1 = p2[z]; z2 = xb1[z1]; z3 = p2[zz]; z4 = xb1[z3];
+				s1  = klabs(rx1[z1]-rx1[z2])+klabs(ry1[z1]-ry1[z2]);
+				s1 += klabs(rx1[z3]-rx1[z4])+klabs(ry1[z3]-ry1[z4]);
+				s2  = klabs(rx1[z1]-rx1[z4])+klabs(ry1[z1]-ry1[z4]);
+				s2 += klabs(rx1[z3]-rx1[z2])+klabs(ry1[z3]-ry1[z2]);
+				if (s2 < s1)
+					{ t = xb1[p2[z]]; xb1[p2[z]] = xb1[p2[zz]]; xb1[p2[zz]] = t; }
+			}
+	}
+	return(npoints);
+}
+
+
+void drawmapview(long dax, long day, long zoome, short ang)
+{
+	walltype *wal;
+	sectortype *sec;
+	spritetype *spr;
+	long tilenum, xoff, yoff, i, j, k, l, cosang, sinang, xspan, yspan;
+	long xrepeat, yrepeat, x, y, x1, y1, x2, y2, x3, y3, x4, y4, bakx1, baky1;
+	long s, w, ox, oy, startwall, cx1, cy1, cx2, cy2;
+	long bakgxvect, bakgyvect, sortnum, gap, npoints;
+	long xvect, yvect, xvect2, yvect2, daslope;
+
+	beforedrawrooms = 0;
+	totalarea += (windowx2+1-windowx1)*(windowy2+1-windowy1);
+
+	clearbuf(&gotsector[0],(long)((numsectors+31)>>5),0L);
+
+	cx1 = (windowx1<<12); cy1 = (windowy1<<12);
+	cx2 = ((windowx2+1)<<12)-1; cy2 = ((windowy2+1)<<12)-1;
+	zoome <<= 8;
+	bakgxvect = divscale28(sintable[(1536-ang)&2047],zoome);
+	bakgyvect = divscale28(sintable[(2048-ang)&2047],zoome);
+	xvect = mulscale8(sintable[(2048-ang)&2047],zoome);
+	yvect = mulscale8(sintable[(1536-ang)&2047],zoome);
+	xvect2 = mulscale16(xvect,yxaspect);
+	yvect2 = mulscale16(yvect,yxaspect);
+
+	sortnum = 0;
+	for(s=0,sec=&sector[s];s<numsectors;s++,sec++)
+		if (show2dsector[s>>3]&pow2char[s&7])
+		{
+			npoints = 0; i = 0;
+			startwall = sec->wallptr;
+			for(w=sec->wallnum,wal=&wall[startwall];w>0;w--,wal++)
+			{
+				ox = wal->x - dax; oy = wal->y - day;
+				x = dmulscale16(ox,xvect,-oy,yvect) + (xdim<<11);
+				y = dmulscale16(oy,xvect2,ox,yvect2) + (ydim<<11);
+				i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
+				rx1[npoints] = x;
+				ry1[npoints] = y;
+				xb1[npoints] = wal->point2 - startwall;
+				npoints++;
+			}
+			if ((i&0xf0) != 0xf0) continue;
+			bakx1 = rx1[0]; baky1 = mulscale16(ry1[0]-(ydim<<11),xyaspect)+(ydim<<11);
+			if (i&0x0f)
+			{
+				npoints = clippoly(npoints,i);
+				if (npoints < 3) continue;
+			}
+
+				/* Collect floor sprites to draw */
+			for(i=headspritesect[s];i>=0;i=nextspritesect[i])
+				if ((sprite[i].cstat&48) == 32)
+				{
+					if ((sprite[i].cstat&(64+8)) == (64+8)) continue;
+					tsprite[sortnum++].owner = i;
+				}
+
+			gotsector[s>>3] |= pow2char[s&7];
+
+			globalorientation = (long)sec->floorstat;
+			if ((globalorientation&1) != 0) continue;
+
+			if (palookup[sec->floorpal] != globalpalwritten)
+			{
+				globalpalwritten = palookup[sec->floorpal];
+				setpalookupaddress(globalpalwritten);
+			}
+			globalpicnum = sec->floorpicnum;
+			if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+			setgotpic(globalpicnum);
+			if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) continue;
+			if ((picanm[globalpicnum]&192) != 0) globalpicnum += animateoffs((short)globalpicnum,s);
+			if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+			globalbufplc = waloff[globalpicnum];
+			globalshade = max(min(sec->floorshade,numpalookups-1),0);
+			globvis = globalhisibility;
+			if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+			globalpolytype = 0;
+			if ((globalorientation&64) == 0)
+			{
+				globalposx = dax; globalx1 = bakgxvect; globaly1 = bakgyvect;
+				globalposy = day; globalx2 = bakgxvect; globaly2 = bakgyvect;
+			}
+			else
+			{
+				ox = wall[wall[startwall].point2].x - wall[startwall].x;
+				oy = wall[wall[startwall].point2].y - wall[startwall].y;
+				i = nsqrtasm(ox*ox+oy*oy); if (i == 0) continue;
+				i = 1048576/i;
+				globalx1 = mulscale10(dmulscale10(ox,bakgxvect,oy,bakgyvect),i);
+				globaly1 = mulscale10(dmulscale10(ox,bakgyvect,-oy,bakgxvect),i);
+				ox = (bakx1>>4)-(xdim<<7); oy = (baky1>>4)-(ydim<<7);
+				globalposx = dmulscale28(-oy,globalx1,-ox,globaly1);
+				globalposy = dmulscale28(-ox,globalx1,oy,globaly1);
+				globalx2 = -globalx1;
+				globaly2 = -globaly1;
+
+				daslope = sector[s].floorheinum;
+				i = nsqrtasm(daslope*daslope+16777216);
+				globalposy = mulscale12(globalposy,i);
+				globalx2 = mulscale12(globalx2,i);
+				globaly2 = mulscale12(globaly2,i);
+			}
+			globalxshift = (8-(picsiz[globalpicnum]&15));
+			globalyshift = (8-(picsiz[globalpicnum]>>4));
+			if (globalorientation&8) {globalxshift++; globalyshift++; }
+
+			sethlinesizes(picsiz[globalpicnum]&15,picsiz[globalpicnum]>>4,globalbufplc);
+
+			if ((globalorientation&0x4) > 0)
+			{
+				i = globalposx; globalposx = -globalposy; globalposy = -i;
+				i = globalx2; globalx2 = globaly1; globaly1 = i;
+				i = globalx1; globalx1 = -globaly2; globaly2 = -i;
+			}
+			if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx;
+			if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalposy = -globalposy;
+			asm1 = (globaly1<<globalxshift);
+			asm2 = (globalx2<<globalyshift);
+			globalx1 <<= globalxshift;
+			globaly2 <<= globalyshift;
+			globalposx = (globalposx<<(20+globalxshift))+(((long)sec->floorxpanning)<<24);
+			globalposy = (globalposy<<(20+globalyshift))-(((long)sec->floorypanning)<<24);
+
+			fillpolygon(npoints);
+		}
+
+		/* Sort sprite list */
+	gap = 1; while (gap < sortnum) gap = (gap<<1)+1;
+	for(gap>>=1;gap>0;gap>>=1)
+		for(i=0;i<sortnum-gap;i++)
+			for(j=i;j>=0;j-=gap)
+			{
+				if (sprite[tsprite[j].owner].z <= sprite[tsprite[j+gap].owner].z) break;
+				swapshort(&tsprite[j].owner,&tsprite[j+gap].owner);
+			}
+
+	for(s=sortnum-1;s>=0;s--)
+	{
+		spr = &sprite[tsprite[s].owner];
+		if ((spr->cstat&48) == 32)
+		{
+			npoints = 0;
+
+			tilenum = spr->picnum;
+			xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+			yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
+			if ((spr->cstat&4) > 0) xoff = -xoff;
+			if ((spr->cstat&8) > 0) yoff = -yoff;
+
+			k = spr->ang;
+			cosang = sintable[(k+512)&2047]; sinang = sintable[k];
+			xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
+			yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;
+
+			ox = ((xspan>>1)+xoff)*xrepeat; oy = ((yspan>>1)+yoff)*yrepeat;
+			x1 = spr->x + mulscale(sinang,ox,16) + mulscale(cosang,oy,16);
+			y1 = spr->y + mulscale(sinang,oy,16) - mulscale(cosang,ox,16);
+			l = xspan*xrepeat;
+			x2 = x1 - mulscale(sinang,l,16);
+			y2 = y1 + mulscale(cosang,l,16);
+			l = yspan*yrepeat;
+			k = -mulscale(cosang,l,16); x3 = x2+k; x4 = x1+k;
+			k = -mulscale(sinang,l,16); y3 = y2+k; y4 = y1+k;
+
+			xb1[0] = 1; xb1[1] = 2; xb1[2] = 3; xb1[3] = 0;
+			npoints = 4;
+
+			i = 0;
+
+			ox = x1 - dax; oy = y1 - day;
+			x = dmulscale16(ox,xvect,-oy,yvect) + (xdim<<11);
+			y = dmulscale16(oy,xvect2,ox,yvect2) + (ydim<<11);
+			i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
+			rx1[0] = x; ry1[0] = y;
+
+			ox = x2 - dax; oy = y2 - day;
+			x = dmulscale16(ox,xvect,-oy,yvect) + (xdim<<11);
+			y = dmulscale16(oy,xvect2,ox,yvect2) + (ydim<<11);
+			i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
+			rx1[1] = x; ry1[1] = y;
+
+			ox = x3 - dax; oy = y3 - day;
+			x = dmulscale16(ox,xvect,-oy,yvect) + (xdim<<11);
+			y = dmulscale16(oy,xvect2,ox,yvect2) + (ydim<<11);
+			i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
+			rx1[2] = x; ry1[2] = y;
+
+			x = rx1[0]+rx1[2]-rx1[1];
+			y = ry1[0]+ry1[2]-ry1[1];
+			i |= getclipmask(x-cx1,cx2-x,y-cy1,cy2-y);
+			rx1[3] = x; ry1[3] = y;
+
+			if ((i&0xf0) != 0xf0) continue;
+			bakx1 = rx1[0]; baky1 = mulscale16(ry1[0]-(ydim<<11),xyaspect)+(ydim<<11);
+			if (i&0x0f)
+			{
+				npoints = clippoly(npoints,i);
+				if (npoints < 3) continue;
+			}
+
+			globalpicnum = spr->picnum;
+			if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0;
+			setgotpic(globalpicnum);
+			if ((tilesizx[globalpicnum] <= 0) || (tilesizy[globalpicnum] <= 0)) continue;
+			if ((picanm[globalpicnum]&192) != 0) globalpicnum += animateoffs((short)globalpicnum,s);
+			if (waloff[globalpicnum] == 0) loadtile(globalpicnum);
+			globalbufplc = waloff[globalpicnum];
+			if ((sector[spr->sectnum].ceilingstat&1) > 0)
+				globalshade = ((long)sector[spr->sectnum].ceilingshade);
+			else
+				globalshade = ((long)sector[spr->sectnum].floorshade);
+			globalshade = max(min(globalshade+spr->shade+6,numpalookups-1),0);
+			asm3 = (long) FP_OFF(palookup[spr->pal]+(globalshade<<8));
+			globvis = globalhisibility;
+			if (sec->visibility != 0) globvis = mulscale4(globvis,(long)((unsigned char)(sec->visibility+16)));
+			globalpolytype = ((spr->cstat&2)>>1)+1;
+
+				/* relative alignment stuff */
+			ox = x2-x1; oy = y2-y1;
+			i = ox*ox+oy*oy; if (i == 0) continue; i = (65536*16384)/i;
+			globalx1 = mulscale10(dmulscale10(ox,bakgxvect,oy,bakgyvect),i);
+			globaly1 = mulscale10(dmulscale10(ox,bakgyvect,-oy,bakgxvect),i);
+			ox = y1-y4; oy = x4-x1;
+			i = ox*ox+oy*oy; if (i == 0) continue; i = (65536*16384)/i;
+			globalx2 = mulscale10(dmulscale10(ox,bakgxvect,oy,bakgyvect),i);
+			globaly2 = mulscale10(dmulscale10(ox,bakgyvect,-oy,bakgxvect),i);
+
+			ox = picsiz[globalpicnum]; oy = ((ox>>4)&15); ox &= 15;
+			if (pow2long[ox] != xspan)
+			{
+				ox++;
+				globalx1 = mulscale(globalx1,xspan,ox);
+				globaly1 = mulscale(globaly1,xspan,ox);
+			}
+
+			bakx1 = (bakx1>>4)-(xdim<<7); baky1 = (baky1>>4)-(ydim<<7);
+			globalposx = dmulscale28(-baky1,globalx1,-bakx1,globaly1);
+			globalposy = dmulscale28(bakx1,globalx2,-baky1,globaly2);
+
+			if ((spr->cstat&2) == 0)
+				msethlineshift(ox,oy);
+			else
+				tsethlineshift(ox,oy);
+
+			if ((spr->cstat&0x4) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx;
+			asm1 = (globaly1<<2); globalx1 <<= 2; globalposx <<= (20+2);
+			asm2 = (globalx2<<2); globaly2 <<= 2; globalposy <<= (20+2);
+
+			fillpolygon(npoints);
+		}
+	}
+}
+
+
+void clearview(long dacol)
+{
+	long p, y, dx;
+
+	if (qsetmode != 200) return;
+
+	dx = windowx2-windowx1+1;
+	dacol += (dacol<<8); dacol += (dacol<<16);
+	if (vidoption == 6)
+	{
+		p = (long) FP_OFF(screen)+ylookup[windowy1]+windowx1;
+		for(y=windowy1;y<=windowy2;y++)
+		{
+			clearbufbyte((void *)p,dx,dacol);
+                        clearbufbyte((void *)(p+65536),dx,dacol);
+			p += ylookup[1];
+		}
+		faketimerhandler();
+		return;
+	}
+	p = frameplace+ylookup[windowy1]+windowx1;
+	for(y=windowy1;y<=windowy2;y++)
+		{ clearbufbyte((void *)p,dx,dacol); p += ylookup[1]; }
+	faketimerhandler();
+}
+
+
+void clearallviews(long dacol)
+{
+	long i;
+
+	if (qsetmode != 200) return;
+	dacol += (dacol<<8); dacol += (dacol<<16);
+
+	switch(vidoption)
+	{
+		case 1:
+			for(i=0;i<numpages;i++)
+			{
+				setactivepage(i);
+				clearbufbyte((void *)frameplace,imageSize,0L);
+			}
+			setactivepage(activepage);
+		case 2:
+			clearbuf((void *)frameplace,(xdim*ydim)>>2,0L);
+			break;
+		case 6:
+			clearbuf(screen,128000L>>2,dacol);
+			break;
+	}
+	faketimerhandler();
+}
+
+
+void plotpixel(long x, long y, char col)
+{
+	drawpixel(ylookup[y]+x+frameplace,(long)col);
+}
+
+
+unsigned char getpixel(long x, long y)
+{
+	return(readpixel(ylookup[y]+x+frameplace));
+}
+
+	/* MUST USE RESTOREFORDRAWROOMS AFTER DRAWING */
+static long setviewcnt = 0;
+static long bakvidoption[4];
+static long bakframeplace[4], bakxsiz[4], bakysiz[4];
+static long bakwindowx1[4], bakwindowy1[4];
+static long bakwindowx2[4], bakwindowy2[4];
+
+
+void setviewtotile(short tilenume, long xsiz, long ysiz)
+{
+	long i, j;
+
+		/* DRAWROOMS TO TILE BACKUP&SET CODE */
+	tilesizx[tilenume] = xsiz; tilesizy[tilenume] = ysiz;
+	bakxsiz[setviewcnt] = xsiz; bakysiz[setviewcnt] = ysiz;
+	bakvidoption[setviewcnt] = vidoption; vidoption = 2;
+	bakframeplace[setviewcnt] = frameplace; frameplace = waloff[tilenume];
+	bakwindowx1[setviewcnt] = windowx1; bakwindowy1[setviewcnt] = windowy1;
+	bakwindowx2[setviewcnt] = windowx2; bakwindowy2[setviewcnt] = windowy2;
+	copybufbyte(&startumost[windowx1],&bakumost[windowx1],(windowx2-windowx1+1)*sizeof(bakumost[0]));
+	copybufbyte(&startdmost[windowx1],&bakdmost[windowx1],(windowx2-windowx1+1)*sizeof(bakdmost[0]));
+	setview(0,0,ysiz-1,xsiz-1);
+	setaspect(65536,65536);
+	j = 0; for(i=0;i<=xsiz;i++) { ylookup[i] = j, j += ysiz; }
+	setvlinebpl(ysiz);
+	setviewcnt++;
+}
+
+
+void setviewback(void)
+{
+	long i, j, k;
+
+	if (setviewcnt <= 0) return;
+	setviewcnt--;
+
+	setview(bakwindowx1[setviewcnt],bakwindowy1[setviewcnt],
+			  bakwindowx2[setviewcnt],bakwindowy2[setviewcnt]);
+	copybufbyte(&bakumost[windowx1],&startumost[windowx1],(windowx2-windowx1+1)*sizeof(startumost[0]));
+	copybufbyte(&bakdmost[windowx1],&startdmost[windowx1],(windowx2-windowx1+1)*sizeof(startdmost[0]));
+	vidoption = bakvidoption[setviewcnt];
+	frameplace = bakframeplace[setviewcnt];
+	if (setviewcnt == 0)
+		k = bakxsiz[0];
+	else
+		k = max(bakxsiz[setviewcnt-1],bakxsiz[setviewcnt]);
+	j = 0; for(i=0;i<=k;i++) ylookup[i] = j, j += bytesperline;
+	setvlinebpl(bytesperline);
+}
+
+
+void squarerotatetile(short tilenume)
+{
+	long i, j, k, xsiz, ysiz;
+	unsigned char *ptr1, *ptr2;
+
+	xsiz = tilesizx[tilenume]; ysiz = tilesizy[tilenume];
+
+		/* supports square tiles only for rotation part */
+	if (xsiz == ysiz)
+	{
+		k = (xsiz<<1);
+		for(i=xsiz-1;i>=0;i--)
+		{
+			ptr1 = (unsigned char *) (waloff[tilenume]+i*(xsiz+1));
+            ptr2 = ptr1;
+			if ((i&1) != 0) { ptr1--; ptr2 -= xsiz; swapchar(ptr1,ptr2); }
+			for(j=(i>>1)-1;j>=0;j--)
+				{ ptr1 -= 2; ptr2 -= k; swapchar2(ptr1,ptr2,xsiz); }
+		}
+	}
+}
+
+
+void preparemirror(long dax, long day, long daz,
+                   short daang, long dahoriz, short dawall,
+                   short dasector, long *tposx, long *tposy,
+                   short *tang)
+{
+	long i, j, x, y, dx, dy;
+
+	x = wall[dawall].x; dx = wall[wall[dawall].point2].x-x;
+	y = wall[dawall].y; dy = wall[wall[dawall].point2].y-y;
+	j = dx*dx + dy*dy; if (j == 0) return;
+	i = (((dax-x)*dx + (day-y)*dy)<<1);
+	*tposx = (x<<1) + scale(dx,i,j) - dax;
+	*tposy = (y<<1) + scale(dy,i,j) - day;
+	*tang = (((getangle(dx,dy)<<1)-daang)&2047);
+
+	inpreparemirror = 1;
+}
+
+
+void completemirror(void)
+{
+	long i, dy, p;
+
+		/* Can't reverse with uninitialized data */
+	if (inpreparemirror) { inpreparemirror = 0; return; }
+	if (mirrorsx1 > 0) mirrorsx1--;
+	if (mirrorsx2 < windowx2-windowx1-1) mirrorsx2++;
+	if (mirrorsx2 < mirrorsx1) return;
+
+	transarea += (mirrorsx2-mirrorsx1)*(windowy2-windowy1);
+
+	p = frameplace+ylookup[windowy1+mirrorsy1]+windowx1+mirrorsx1;
+	i = windowx2-windowx1-mirrorsx2-mirrorsx1; mirrorsx2 -= mirrorsx1;
+	// FIX_00085: Optimized Video driver. FPS increases by +20%.
+	for(dy=mirrorsy2-mirrorsy1-1;dy>=0;dy--)
+	{
+                copybufbyte((void *)(p),tempbuf,mirrorsx2+1);
+		tempbuf[mirrorsx2] = tempbuf[mirrorsx2-1];
+                copybufreverse(&tempbuf[mirrorsx2],(void *)(p+i),mirrorsx2+1);
+		p += ylookup[1];
+		faketimerhandler();
+	}
+}
+
+
+int sectorofwall(short theline)
+{
+	long i, gap;
+
+	if ((theline < 0) || (theline >= numwalls)) return(-1);
+	i = wall[theline].nextwall; if (i >= 0) return(wall[i].nextsector);
+
+	gap = (numsectors>>1); i = gap;
+	while (gap > 1)
+	{
+		gap >>= 1;
+		if (sector[i].wallptr < theline) i += gap; else i -= gap;
+	}
+	while (sector[i].wallptr > theline) i--;
+	while (sector[i].wallptr+sector[i].wallnum <= theline) i++;
+	return(i);
+}
+
+
+int getceilzofslope(short sectnum, long dax, long day)
+{
+	long dx, dy, i, j;
+	walltype *wal;
+
+	if (!(sector[sectnum].ceilingstat&2)) return(sector[sectnum].ceilingz);
+	wal = &wall[sector[sectnum].wallptr];
+	dx = wall[wal->point2].x-wal->x; dy = wall[wal->point2].y-wal->y;
+	i = (nsqrtasm(dx*dx+dy*dy)<<5); if (i == 0) return(sector[sectnum].ceilingz);
+	j = dmulscale3(dx,day-wal->y,-dy,dax-wal->x);
+	return(sector[sectnum].ceilingz+scale(sector[sectnum].ceilingheinum,j,i));
+}
+
+
+int getflorzofslope(short sectnum, long dax, long day)
+{
+	long dx, dy, i, j;
+	walltype *wal;
+
+	if (!(sector[sectnum].floorstat&2)) return(sector[sectnum].floorz);
+	wal = &wall[sector[sectnum].wallptr];
+	dx = wall[wal->point2].x-wal->x; dy = wall[wal->point2].y-wal->y;
+	i = (nsqrtasm(dx*dx+dy*dy)<<5); if (i == 0) return(sector[sectnum].floorz);
+	j = dmulscale3(dx,day-wal->y,-dy,dax-wal->x);
+	return(sector[sectnum].floorz+scale(sector[sectnum].floorheinum,j,i));
+}
+
+
+void getzsofslope(short sectnum, long dax, long day, long *ceilz, long *florz)
+{
+	long dx, dy, i, j;
+	walltype *wal, *wal2;
+	sectortype *sec;
+
+	sec = &sector[sectnum];
+	*ceilz = sec->ceilingz; *florz = sec->floorz;
+	if ((sec->ceilingstat|sec->floorstat)&2)
+	{
+		wal = &wall[sec->wallptr]; wal2 = &wall[wal->point2];
+		dx = wal2->x-wal->x; dy = wal2->y-wal->y;
+		i = (nsqrtasm(dx*dx+dy*dy)<<5); if (i == 0) return;
+		j = dmulscale3(dx,day-wal->y,-dy,dax-wal->x);
+		if (sec->ceilingstat&2) *ceilz = (*ceilz)+scale(sec->ceilingheinum,j,i);
+		if (sec->floorstat&2) *florz = (*florz)+scale(sec->floorheinum,j,i);
+	}
+}
+
+
+void alignceilslope(short dasect, long x, long y, long z)
+{
+	long i, dax, day;
+	walltype *wal;
+
+	wal = &wall[sector[dasect].wallptr];
+	dax = wall[wal->point2].x-wal->x;
+	day = wall[wal->point2].y-wal->y;
+
+	i = (y-wal->y)*dax - (x-wal->x)*day; if (i == 0) return;
+	sector[dasect].ceilingheinum = scale((z-sector[dasect].ceilingz)<<8,
+													 nsqrtasm(dax*dax+day*day),i);
+
+	if (sector[dasect].ceilingheinum == 0) sector[dasect].ceilingstat &= ~2;
+												 else sector[dasect].ceilingstat |= 2;
+}
+
+
+void alignflorslope(short dasect, long x, long y, long z)
+{
+	long i, dax, day;
+	walltype *wal;
+
+	wal = &wall[sector[dasect].wallptr];
+	dax = wall[wal->point2].x-wal->x;
+	day = wall[wal->point2].y-wal->y;
+
+	i = (y-wal->y)*dax - (x-wal->x)*day; if (i == 0) return;
+	sector[dasect].floorheinum = scale((z-sector[dasect].floorz)<<8,
+												  nsqrtasm(dax*dax+day*day),i);
+
+	if (sector[dasect].floorheinum == 0) sector[dasect].floorstat &= ~2;
+											  else sector[dasect].floorstat |= 2;
+}
+
+
+int loopnumofsector(short sectnum, short wallnum)
+{
+	long i, numloops, startwall, endwall;
+
+	numloops = 0;
+	startwall = sector[sectnum].wallptr;
+	endwall = startwall + sector[sectnum].wallnum;
+	for(i=startwall;i<endwall;i++)
+	{
+		if (i == wallnum) return(numloops);
+		if (wall[i].point2 < i) numloops++;
+	}
+	return(-1);
+}
+
+
+void setfirstwall(short sectnum, short newfirstwall)
+{
+	long i, j, k, numwallsofloop;
+	long startwall, endwall, danumwalls, dagoalloop;
+
+	startwall = sector[sectnum].wallptr;
+	danumwalls = sector[sectnum].wallnum;
+	endwall = startwall+danumwalls;
+	if ((newfirstwall < startwall) || (newfirstwall >= startwall+danumwalls)) return;
+	for(i=0;i<danumwalls;i++)
+		memcpy(&wall[i+numwalls],&wall[i+startwall],sizeof(walltype));
+
+	numwallsofloop = 0;
+	i = newfirstwall;
+	do
+	{
+		numwallsofloop++;
+		i = wall[i].point2;
+	} while (i != newfirstwall);
+
+		/* Put correct loop at beginning */
+	dagoalloop = loopnumofsector(sectnum,newfirstwall);
+	if (dagoalloop > 0)
+	{
+		j = 0;
+		while (loopnumofsector(sectnum,j+startwall) != dagoalloop) j++;
+		for(i=0;i<danumwalls;i++)
+		{
+			k = i+j; if (k >= danumwalls) k -= danumwalls;
+			memcpy(&wall[startwall+i],&wall[numwalls+k],sizeof(walltype));
+
+			wall[startwall+i].point2 += danumwalls-startwall-j;
+			if (wall[startwall+i].point2 >= danumwalls)
+				wall[startwall+i].point2 -= danumwalls;
+			wall[startwall+i].point2 += startwall;
+		}
+		newfirstwall += danumwalls-j;
+		if (newfirstwall >= startwall+danumwalls) newfirstwall -= danumwalls;
+	}
+
+	for(i=0;i<numwallsofloop;i++)
+		memcpy(&wall[i+numwalls],&wall[i+startwall],sizeof(walltype));
+	for(i=0;i<numwallsofloop;i++)
+	{
+		k = i+newfirstwall-startwall;
+		if (k >= numwallsofloop) k -= numwallsofloop;
+		memcpy(&wall[startwall+i],&wall[numwalls+k],sizeof(walltype));
+
+		wall[startwall+i].point2 += numwallsofloop-newfirstwall;
+		if (wall[startwall+i].point2 >= numwallsofloop)
+			wall[startwall+i].point2 -= numwallsofloop;
+		wall[startwall+i].point2 += startwall;
+	}
+
+	for(i=startwall;i<endwall;i++)
+		if (wall[i].nextwall >= 0) wall[wall[i].nextwall].nextwall = i;
+}
+
+/* end of engine.c ... */
+
+
--- /dev/null
+++ b/Engine/src/engine.h
@@ -1,0 +1,163 @@
+/*
+ * A list of all symbols exported from engine.c for a game's use.
+ *
+ *  Put together by Ryan C. Gordon (icculus@clutteredmind.org)
+ *
+ * Please do NOT harrass Ken Silverman about any code modifications
+ *  (including this file) to BUILD.
+ */
+
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file IS NOT A PART OF Ken Silverman's original release
+ */
+
+#ifndef _INCLUDE_ENGINE_H_
+#define _INCLUDE_ENGINE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#define SUPERBUILD  /* don't touch this. */
+
+/*
+ * this must be implemented by every program that uses the BUILD engine.
+ *  engine.c calls this function in several places.
+ *  See Ken's test game (in game.c), and his editor (in bstub.c) for examples.
+ */
+void faketimerhandler(void);
+
+/*
+ * exported engine functions, in absolutely no particular order.
+ *  See build.txt, build2.txt...and anything else with a .txt extention for
+ *  information on using these functions that may or may not be accurate. :)
+ */
+int initmouse(void);
+int setgamemode(char davidoption, long daxdim, long daydim);
+int getceilzofslope(short sectnum, long dax, long day);
+int getflorzofslope(short sectnum, long dax, long day);
+void getzsofslope(short sectnum, long dax, long day, long *ceilz, long *florz);
+void setaspect(long daxrange, long daaspect);
+int insertsprite(short sectnum, short statnum);
+void updatesector(long x, long y, short *sectnum);
+int lastwall(short point);
+void initspritelists(void);
+int deletesprite(short spritenum);
+int insertspritesect(short sectnum);
+int deletespritesect(short deleteme);
+int deletespritestat (short deleteme);
+int insertspritestat(short statnum);
+int changespritesect(short spritenum, short newsectnum);
+int changespritestat(short spritenum, short newstatnum);
+void loadtile(short tilenume);
+void setmmxoverlay(int isenabled);
+int getmmxoverlay(void);
+void nextpage(void);
+void drawrooms(long daposx, long daposy, long daposz,
+               short daang, long dahoriz, short dacursectnum);
+int loadboard(char *filename, long *daposx, long *daposy,
+			  long *daposz, short *daang, short *dacursectnum);
+void drawmasks(void);
+void printext256(long xpos, long ypos, short col, short backcol,
+			char name[82], char fontsize);
+void printext256_noupdate(long xpos, long ypos, short col, short backcol,
+			char name[82], char fontsize);
+void initengine(void);
+void uninitengine(void);
+int loadpics(char *filename, char* gamedir);
+int saveboard(char *filename, long *daposx, long *daposy, long *daposz,
+                         short *daang, short *dacursectnum);
+void plotpixel(long x, long y, char col);
+unsigned char getpixel(long x, long y);
+void setbrightness(char dabrightness, unsigned char *dapal);
+int screencapture(char *filename, char inverseit);
+void getmousevalues(short *mousx, short *mousy, short *bstatus);
+int clipmove (long *x, long *y, long *z, short *sectnum, long xvect,
+			long yvect, long walldist, long ceildist,
+			long flordist, unsigned long cliptype);
+void getzrange(long x, long y, long z, short sectnum,
+			long *ceilz, long *ceilhit, long *florz, long *florhit,
+			long walldist, unsigned long cliptype);
+int getangle(long xvect, long yvect);
+void alignceilslope(short dasect, long x, long y, long z);
+void alignflorslope(short dasect, long x, long y, long z);
+int hitscan(long xs, long ys, long zs, short sectnum,
+            long vx, long vy, long vz,
+	        short *hitsect, short *hitwall, short *hitsprite,
+	        long *hitx, long *hity, long *hitz, unsigned long cliptype);
+int inside (long x, long y, short sectnum);
+void setfirstwall(short sectnum, short newfirstwall);
+void rotatepoint(long xpivot, long ypivot, long x, long y, short daang,
+			long *x2, long *y2);
+int drawtilescreen(long pictopleft, long picbox);
+void clearview(long dacol);
+void clearallviews(long dacol);
+void draw2dgrid(long posxe, long posye, short ange, long zoome,
+			short gride);
+void draw2dscreen(long posxe, long posye, short ange, long zoome,
+			short gride);
+int sectorofwall(short theline);
+int setsprite(short spritenum, long newx, long newy, long newz);
+void dragpoint(short pointhighlight, long dax, long day);
+int ksqrt(long num);
+int loopnumofsector(short sectnum, short wallnum);
+int cansee(long x1, long y1, long z1, short sect1,
+            long x2, long y2, long z2, short sect2);
+int lintersect(long x1, long y1, long z1, long x2, long y2, long z2,
+               long x3, long y3, long x4, long y4, long *intx,
+               long *inty, long *intz);
+int rintersect(long x1, long y1, long z1, long vx, long vy, long vz,
+               long x3, long y3, long x4, long y4, long *intx,
+               long *inty, long *intz);
+int allocatepermanenttile(short tilenume, long xsiz, long ysiz);
+void drawline256 (long x1, long y1, long x2, long y2, unsigned char col);
+void copytilepiece(long tilenume1, long sx1, long sy1, long xsiz, long ysiz,
+                   long tilenume2, long sx2, long sy2);
+int nextsectorneighborz(short sectnum, long thez,
+                        short topbottom, short direction);
+int neartag(long xs, long ys, long zs, short sectnum, short ange,
+            short *neartagsector, short *neartagwall, short *neartagsprite,
+            long *neartaghitdist, long neartagrange, char tagsearch);
+int pushmove(long *x, long *y, long *z, short *sectnum,
+             long walldist, long ceildist, long flordist,
+             unsigned long cliptype);
+#ifdef DBGRECORD
+int krand(int line, char* file);
+#else
+int krand(void);
+#endif
+void flushperms(void);
+void rotatesprite(long sx, long sy, long z, short a, short picnum,
+                  signed char dashade, char dapalnum, char dastat,
+                  long cx1, long cy1, long cx2, long cy2);
+void makepalookup(long palnum, char *remapbuf, signed char r,
+                  signed char g, signed char b, char dastat);
+void drawmapview(long dax, long day, long zoome, short ang);
+void setview(long x1, long y1, long x2, long y2);
+void setviewtotile(short tilenume, long xsiz, long ysiz);
+void setviewback(void);
+void squarerotatetile(short tilenume);
+void preparemirror(long dax, long day, long daz,
+                   short daang, long dahoriz, short dawall,
+                   short dasector, long *tposx, long *tposy,
+                   short *tang);
+void completemirror(void);
+int clipinsidebox(long x, long y, short wallnum, long walldist);
+
+#ifdef SUPERBUILD
+void qloadkvx(long voxindex, char *filename);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined _INCLUDE_ENGINE_H_ */
+
+/* end of engine.h ... */
+
+
+
--- /dev/null
+++ b/Engine/src/engine_protos.h
@@ -1,0 +1,263 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+//-------------------------------------------------------------------------
+
+/* Prototypes for the build engine */
+
+#if 0
+/* game.c */
+extern void initsb(char dadigistat, char damusistat, long dasamplerate, char danumspeakers, char dabytespersample, char daintspersec, char daquality);
+extern void uninitsb(void);
+extern int loadsong(char *filename);
+extern void musicon(void);
+extern void musicoff(void);
+extern void wsayfollow(char *dafilename, long dafreq, long davol, long *daxplc, long *dayplc, char followstat);
+extern void wsay(char *dafilename, long dafreq, long volume1, long volume2);
+extern void preparesndbuf(void);
+extern void setears(long daposx, long daposy, long daxvect, long dayvect);
+extern void timerhandler(void);
+extern void keyhandler(void);
+extern void initlava(void);
+extern void movelava(char *dapic);
+extern void drawtilebackground(long thex, long they, short tilenum, signed char shade, long cx1, long cy1, long cx2, long cy2, char dapalnum);
+extern void printext(long x, long y, char *buffer, short tilenum, char invisiblecol);
+extern void drawstatusflytime(short snum);
+extern void drawstatusbar(short snum);
+extern void setup3dscreen(void);
+extern void setinterpolation(long *posptr);
+extern void stopinterpolation(long *posptr);
+extern void updateinterpolations(void);
+extern void restoreinterpolations(void);
+extern void searchmap(short startsector);
+extern void prepareboard(char *daboardfilename);
+extern long changehealth(short snum, short deltahealth);
+extern void changenumbombs(short snum, short deltanumbombs);
+extern void changenummissiles(short snum, short deltanummissiles);
+extern void changenumgrabbers(short snum, short deltanumgrabbers);
+extern void findrandomspot(long *x, long *y, short *sectnum);
+extern void operatesector(short dasector);
+extern void shootgun(short snum, long x, long y, long z, short daang, long dahoriz, short dasectnum, char guntype);
+extern void operatesprite(short dasprite);
+extern void checktouchsprite(short snum, short sectnum);
+extern void checkgrabbertouchsprite(short snum, short sectnum);
+extern void activatehitag(short dahitag);
+extern void processinput(short snum);
+extern void movethings(void);
+extern void fakedomovethings(void);
+extern void fakedomovethingscorrect(void);
+extern void doanimations(void);
+extern void warp(long *x, long *y, long *z, short *daang, short *dasector);
+extern void warpsprite(short spritenum);
+extern int testneighborsectors(short sect1, short sect2);
+extern void tagcode(void);
+extern void bombexplode(long i);
+extern void statuslistcode(void);
+extern void checkmasterslaveswitch(void);
+extern void getpackets(void);
+extern void initplayersprite(short snum);
+extern void analyzesprites(long dax, long day);
+extern void updatesectorz(long x, long y, long z, short *sectnum);
+extern void drawoverheadmap(long cposx, long cposy, long czoom, short cang);
+extern void drawscreen(short snum, long dasmoothratio);
+extern int loadgame(void);
+extern int savegame(void);
+extern void faketimerhandler(void);
+extern void waitforeverybody(void);
+#endif
+
+/* cache1d.c */
+extern void initcache(long dacachestart, long dacachesize);
+extern void allocache(long *newhandle, long newbytes, unsigned char *newlockptr);
+extern void suckcache(long *suckptr);
+extern void agecache(void);
+extern void reportandexit(char *errormessage);
+extern long initgroupfile(const char *filename);
+extern void uninitgroupfile(void);
+extern long kopen4load(const char *filename,int readfromGRP);
+extern long kread(long handle, void *buffer, long leng);
+extern long klseek(long handle, long offset, long whence);
+extern long kfilelength(long handle);
+extern void kclose(long handle);
+extern void kdfread(void *buffer, size_t dasizeof, size_t count, long fil);
+extern void dfread(void *buffer, size_t dasizeof, size_t count, FILE *fil);
+extern void dfwrite(void *buffer, size_t dasizeof, size_t count, FILE *fil);
+extern long compress(char *lzwinbuf, long uncompleng, char *lzwoutbuf);
+extern long uncompress(char *lzwinbuf, long compleng, char *lzwoutbuf);
+
+/* sdl_driver.c */
+extern int using_opengl(void);
+extern void _handle_events(void);
+extern unsigned char _readlastkeyhit(void);
+extern int mprotect_align(const void *addr, size_t len, int prot);
+extern void unprotect_ASM_pages(void);
+extern void _platform_init(int argc, char **argv, const char *title, const char *icon);
+extern int setvesa(long x, long y);
+extern int screencapture(char *filename, char inverseit);
+extern void setvmode(int mode);
+extern int _setgamemode(char davidoption, long daxdim, long daydim);
+extern void getvalidvesamodes(void);
+extern int VBE_setPalette(long start, long num, char *palettebuffer);
+extern int VBE_getPalette(long start, long num, char *palettebuffer);
+extern void _uninitengine(void);
+extern void uninitvesa(void);
+extern int setupmouse(void);
+extern void readmousexy(short *x, short *y);
+extern void readmousebstatus(short *bstatus);
+extern void _updateScreenRect(long x, long y, long w, long h);
+extern void _nextpage(void);
+extern unsigned char readpixel(long offset);
+extern void drawpixel(long offset, unsigned char pixel);
+extern void drawpixels(long offset, unsigned short pixels);
+extern void drawpixelses(long offset, unsigned int pixelses);
+extern void setcolor16(int col);
+extern void drawpixel16(long offset);
+extern void fillscreen16(long offset, long color, long blocksize);
+extern void drawline16(long XStart, long YStart, long XEnd, long YEnd, char Color);
+extern void clear2dscreen(void);
+extern void _idle(void);
+extern void *_getVideoBase(void);
+extern void setactivepage(long dapagenum);
+extern void limitrate(void);
+extern int inittimer(int);
+extern void uninittimer(void);
+extern void initkeys(void);
+extern void uninitkeys(void);
+extern void set16color_palette(void);
+extern void restore256_palette(void);
+extern unsigned long getticks(void);
+
+/* mmulti.c */
+// converted to function pointers
+/*
+void (*callcommit)(void);
+void (*initcrc)(void);
+long (*getcrc)(char *buffer, short bufleng);
+void (*initmultiplayers)(char damultioption, char dacomrateoption, char dapriority);
+void (*sendpacket)(long other, char *bufptr, long messleng);
+void (*setpackettimeout)(long datimeoutcount, long daresendagaincount);
+void (*uninitmultiplayers)(void);
+void (*sendlogon)(void);
+void (*sendlogoff)(void);
+int (*getoutputcirclesize)(void);
+void (*setsocket)(short newsocket);
+short (*getpacket)(short *other, char *bufptr);
+void (*flushpackets)(void);
+void (*genericmultifunction)(long other, char *bufptr, long messleng, long command);
+*/
+void callcommit(void);
+void initcrc(void);
+long getcrc(char *buffer, short bufleng);
+void initmultiplayers(char damultioption, char dacomrateoption, char dapriority);
+void sendpacket(long other, char *bufptr, long messleng);
+void setpackettimeout(long datimeoutcount, long daresendagaincount);
+void uninitmultiplayers(void);
+void sendlogon(void);
+void sendlogoff(void);
+int getoutputcirclesize(void);
+void setsocket(short newsocket);
+short getpacket(short *other, char *bufptr);
+void flushpackets(void);
+void genericmultifunction(long other, char *bufptr, long messleng, long command);
+
+/* engine.c */
+extern int setgotpic(long i1);
+//extern static __inline long getclipmask(long a, long b, long c, long d);
+extern int wallfront(long l1, long l2);
+extern void drawrooms(long daposx, long daposy, long daposz, short daang, long dahoriz, short dacursectnum);
+extern int loadboard(char *filename, long *daposx, long *daposy, long *daposz, short *daang, short *dacursectnum);
+extern int saveboard(char *filename, long *daposx, long *daposy, long *daposz, short *daang, short *dacursectnum);
+extern int setgamemode(char davidoption, long daxdim, long daydim);
+extern void setmmxoverlay(int isenabled);
+extern int getmmxoverlay(void);
+extern void initengine(void);
+extern void uninitengine(void);
+extern void nextpage(void);
+extern void loadtile(short tilenume);
+extern int allocatepermanenttile(short tilenume, long xsiz, long ysiz);
+extern int loadpics(char *filename, char* gamedir);
+extern void qloadkvx(long voxindex, char *filename);
+extern int clipinsidebox(long x, long y, short wallnum, long walldist);
+extern void drawline256(long x1, long y1, long x2, long y2, unsigned char col);
+extern int inside(long x, long y, short sectnum);
+extern int getangle(long xvect, long yvect);
+extern int ksqrt(long num);
+extern void copytilepiece(long tilenume1, long sx1, long sy1, long xsiz, long ysiz, long tilenume2, long sx2, long sy2);
+extern void drawmasks(void);
+extern int setsprite(short spritenum, long newx, long newy, long newz);
+extern void initspritelists(void);
+extern int insertsprite(short sectnum, short statnum);
+extern int insertspritesect(short sectnum);
+extern int insertspritestat(short statnum);
+extern int deletesprite(short spritenum);
+extern int deletespritesect(short deleteme);
+extern int deletespritestat(short deleteme);
+extern int changespritesect(short spritenum, short newsectnum);
+extern int changespritestat(short spritenum, short newstatnum);
+extern int nextsectorneighborz(short sectnum, long thez, short topbottom, short direction);
+extern int cansee(long x1, long y1, long z1, short sect1, long x2, long y2, long z2, short sect2);
+extern int lintersect(long x1, long y1, long z1, long x2, long y2, long z2, long x3, long y3, long x4, long y4, long *intx, long *inty, long *intz);
+extern int rintersect(long x1, long y1, long z1, long vx, long vy, long vz, long x3, long y3, long x4, long y4, long *intx, long *inty, long *intz);
+extern int hitscan(long xs, long ys, long zs, short sectnum, long vx, long vy, long vz, short *hitsect, short *hitwall, short *hitsprite, long *hitx, long *hity, long *hitz, unsigned long cliptype);
+extern int neartag(long xs, long ys, long zs, short sectnum, short ange, short *neartagsector, short *neartagwall, short *neartagsprite, long *neartaghitdist, long neartagrange, char tagsearch);
+extern void dragpoint(short pointhighlight, long dax, long day);
+extern int lastwall(short point);
+extern int clipmove(long *x, long *y, long *z, short *sectnum, long xvect, long yvect, long walldist, long ceildist, long flordist, unsigned long cliptype);
+extern int pushmove(long *x, long *y, long *z, short *sectnum, long walldist, long ceildist, long flordist, unsigned long cliptype);
+extern void updatesector(long x, long y, short *sectnum);
+extern void rotatepoint(long xpivot, long ypivot, long x, long y, short daang, long *x2, long *y2);
+extern int initmouse(void);
+extern void getmousevalues(short *mousx, short *mousy, short *bstatus);
+extern void draw2dgrid(long posxe, long posye, short ange, long zoome, short gride);
+extern void draw2dscreen(long posxe, long posye, short ange, long zoome, short gride);
+extern void printext256(long xpos, long ypos, short col, short backcol, char name[82], char fontsize);
+extern void printext256_noupdate(long xpos, long ypos, short col, short backcol, char name[82], char fontsize);
+#ifdef DBGRECORD
+extern int krand(int line, char* file);
+#else
+extern int krand(void);
+#endif
+extern void getzrange(long x, long y, long z, short sectnum, long *ceilz, long *ceilhit, long *florz, long *florhit, long walldist, unsigned long cliptype);
+extern void setview(long x1, long y1, long x2, long y2);
+extern void setaspect(long daxrange, long daaspect);
+extern void flushperms(void);
+extern void rotatesprite(long sx, long sy, long z, short a, short picnum, signed char dashade, char dapalnum, char dastat, long cx1, long cy1, long cx2, long cy2);
+extern void makepalookup(long palnum, char *remapbuf, signed char r, signed char g, signed char b, char dastat);
+extern void setbrightness(char dabrightness, unsigned char *dapal);
+extern void drawmapview(long dax, long day, long zoome, short ang);
+extern void clearview(long dacol);
+extern void clearallviews(long dacol);
+extern void plotpixel(long x, long y, char col);
+extern unsigned char getpixel(long x, long y);
+extern void setviewtotile(short tilenume, long xsiz, long ysiz);
+extern void setviewback(void);
+extern void squarerotatetile(short tilenume);
+extern void preparemirror(long dax, long day, long daz, short daang, long dahoriz, short dawall, short dasector, long *tposx, long *tposy, short *tang);
+extern void completemirror(void);
+extern int sectorofwall(short theline);
+extern int getceilzofslope(short sectnum, long dax, long day);
+extern int getflorzofslope(short sectnum, long dax, long day);
+extern void getzsofslope(short sectnum, long dax, long day, long *ceilz, long *florz);
+extern void alignceilslope(short dasect, long x, long y, long z);
+extern void alignflorslope(short dasect, long x, long y, long z);
+extern int loopnumofsector(short sectnum, short wallnum);
+extern void setfirstwall(short sectnum, short newfirstwall);
--- /dev/null
+++ b/Engine/src/icon.h
@@ -1,0 +1,165 @@
+unsigned char iconBMP[]=
+ {
+  0x42,0x4d,0x36,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x04,0x00,
+  0x00,0x28,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
+  0x01,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
+  0x00,0x00,0xff,0xff,0xff,0x00,0x04,0x6a,0xa4,0x00,0x5c,0x62,0x64,
+  0x00,0x1c,0x92,0xb4,0x00,0x1c,0x32,0x44,0x00,0x34,0x62,0x8c,0x00,
+  0x4c,0x42,0x44,0x00,0x24,0x4a,0x64,0x00,0x04,0x7a,0xac,0x00,0x04,
+  0x1a,0x1c,0x00,0x1c,0xaa,0xe4,0x00,0x2c,0x7a,0xa4,0x00,0x34,0x4a,
+  0x54,0x00,0x24,0x1a,0x1c,0x00,0x1c,0x7a,0xa4,0x00,0x34,0x32,0x2c,
+  0x00,0x2c,0x92,0xc4,0x00,0x1c,0x8a,0xb4,0x00,0x1c,0x62,0x84,0x00,
+  0x2c,0x2a,0x24,0x00,0x1c,0x72,0x9c,0x00,0x04,0x92,0xd4,0x00,0x2c,
+  0x72,0x9c,0x00,0x74,0x72,0x74,0x00,0x44,0x52,0x5c,0x00,0x34,0x8a,
+  0xc4,0x00,0x1c,0x82,0xb4,0x00,0x1c,0x5a,0x7c,0x00,0x34,0x5a,0x6c,
+  0x00,0x34,0x42,0x54,0x00,0x3c,0x7a,0x9c,0x00,0x1c,0xa2,0xdc,0x00,
+  0x64,0x62,0x74,0x00,0x1c,0x3a,0x4c,0x00,0x44,0x62,0x7c,0x00,0x24,
+  0x22,0x1c,0x00,0x34,0x3a,0x44,0x00,0x24,0x62,0x9c,0x00,0x1c,0x2a,
+  0x3c,0x00,0x34,0xb2,0xe4,0x00,0x2c,0x82,0xac,0x00,0x1c,0x7a,0xb4,
+  0x00,0x1c,0x9a,0xd4,0x00,0x1c,0x5a,0x8c,0x00,0x4c,0x4a,0x4c,0x00,
+  0x1c,0x1a,0x1c,0x00,0x1c,0xb2,0xf4,0x00,0x34,0x52,0x6c,0x00,0x1c,
+  0x8a,0xcc,0x00,0x34,0x2a,0x2c,0x00,0x24,0x72,0x9c,0x00,0x64,0x5a,
+  0x5c,0x00,0x2c,0x22,0x24,0x00,0x4c,0x72,0x94,0x00,0x24,0x32,0x3c,
+  0x00,0x2c,0x6a,0x94,0x00,0x34,0x7a,0xa4,0x00,0x24,0x7a,0xa4,0x00,
+  0x3c,0x32,0x34,0x00,0x24,0xa2,0xdc,0x00,0x3c,0x72,0x9c,0x00,0x54,
+  0x52,0x54,0x00,0x24,0x82,0xac,0x00,0x24,0x5a,0x7c,0x00,0x34,0x5a,
+  0x84,0x00,0x1c,0x6a,0x9c,0x00,0x1c,0x9a,0xe4,0x00,0x1c,0x22,0x2c,
+  0x00,0x1c,0x02,0x04,0x00,0x54,0x62,0x74,0x00,0x1c,0x92,0xcc,0x00,
+  0x2c,0x52,0x6c,0x00,0x24,0xaa,0xe4,0x00,0x34,0x52,0x64,0x00,0x2c,
+  0x9a,0xd4,0x00,0x1c,0x8a,0xc4,0x00,0x1c,0x6a,0x94,0x00,0x2c,0x2a,
+  0x34,0x00,0x34,0x72,0x9c,0x00,0x84,0x82,0x84,0x00,0x44,0x5a,0x6c,
+  0x00,0x3c,0x5a,0x74,0x00,0x6c,0x72,0x7c,0x00,0x24,0x3a,0x4c,0x00,
+  0x3c,0x6a,0x8c,0x00,0x24,0x22,0x2c,0x00,0x3c,0x3a,0x3c,0x00,0x54,
+  0x4a,0x44,0x00,0x24,0x72,0xac,0x00,0x34,0x6a,0x8c,0x00,0x24,0x6a,
+  0x9c,0x00,0x04,0x72,0xa4,0x00,0x1c,0x92,0xbc,0x00,0x1c,0x32,0x4c,
+  0x00,0x4c,0x42,0x4c,0x00,0x1c,0x52,0x7c,0x00,0x04,0x82,0xb4,0x00,
+  0x1c,0xaa,0xec,0x00,0x2c,0x7a,0xac,0x00,0x34,0x4a,0x64,0x00,0x24,
+  0x1a,0x24,0x00,0x1c,0x7a,0xac,0x00,0x34,0x32,0x34,0x00,0x1c,0x8a,
+  0xbc,0x00,0x2c,0x2a,0x2c,0x00,0x1c,0x72,0xac,0x00,0x04,0x9a,0xec,
+  0x00,0x44,0x52,0x64,0x00,0x3c,0x8a,0xc4,0x00,0x1c,0x82,0xbc,0x00,
+  0x1c,0x5a,0x84,0x00,0x34,0x5a,0x74,0x00,0x3c,0x42,0x4c,0x00,0x3c,
+  0x7a,0xac,0x00,0x1c,0xa2,0xe4,0x00,0x6c,0x6a,0x6c,0x00,0x1c,0x3a,
+  0x54,0x00,0x24,0x22,0x24,0x00,0x34,0x82,0xb4,0x00,0x1c,0x7a,0xbc,
+  0x00,0x1c,0x9a,0xdc,0x00,0x1c,0x5a,0x94,0x00,0x4c,0x4a,0x54,0x00,
+  0x1c,0x1a,0x24,0x00,0x24,0xb2,0xf4,0x00,0x34,0x52,0x74,0x00,0x1c,
+  0x8a,0xd4,0x00,0x34,0x2a,0x34,0x00,0x24,0x72,0xa4,0x00,0x1c,0x02,
+  0x1c,0x00,0x34,0x9a,0xcc,0x00,0x8c,0x82,0x8c,0x00,0x24,0x6a,0x94,
+  0x00,0x2c,0x22,0x2c,0x00,0x24,0x32,0x44,0x00,0x2c,0x6a,0x9c,0x00,
+  0x34,0x7a,0xac,0x00,0x24,0x7a,0xac,0x00,0x3c,0x32,0x3c,0x00,0x24,
+  0x82,0xb4,0x00,0x24,0x5a,0x84,0x00,0x1c,0x9a,0xec,0x00,0x1c,0x22,
+  0x34,0x00,0x54,0x62,0x7c,0x00,0x34,0x72,0xa4,0x00,0x3c,0x6a,0x94,
+  0x00,0x3c,0x3a,0x44,0x00,0x24,0x6a,0xa4,0x00,0xc7,0x67,0x18,0x00,
+  0x37,0x04,0xeb,0x00,0x91,0xd7,0x12,0x00,0x7c,0x77,0x00,0x00,0x08,
+  0x30,0x6f,0x00,0x06,0x88,0x03,0x00,0x15,0xd4,0xc3,0x00,0x00,0x77,
+  0x03,0x00,0x28,0xff,0x23,0x00,0x00,0xff,0x04,0x00,0xab,0xff,0x00,
+  0x00,0x00,0xff,0x00,0x00,0x00,0x2a,0x60,0x00,0x00,0x88,0x9d,0x00,
+  0x00,0xd4,0xd4,0x00,0x00,0x77,0x77,0x00,0x00,0xc0,0x00,0x00,0x00,
+  0xb4,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x77,0x00,0x00,0x58,0x00,
+  0x01,0x00,0xed,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0xfd,0x4f,0x00,0x00,0x00,0xe5,0x00,0x00,0x81,0xd6,0x00,0x00,
+  0x7c,0x77,0x00,0x00,0x05,0xdc,0x00,0x00,0x01,0x07,0x00,0x00,0x81,
+  0x10,0x00,0x00,0x7c,0x00,0x00,0x00,0xb0,0x82,0x00,0x00,0x3e,0x00,
+  0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x70,
+  0x00,0x00,0x00,0x02,0x00,0x81,0x00,0xda,0x00,0x7c,0x00,0x77,0x00,
+  0x00,0x00,0x02,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x28,0x4c,0x00,0x00,0x00,0xad,0x00,0x00,0xab,0x74,
+  0xd4,0x00,0x00,0x00,0x77,0x00,0x09,0xcb,0xdc,0x00,0x03,0xb4,0x07,
+  0x00,0x00,0xd4,0x10,0x00,0x00,0x77,0x00,0x00,0x01,0x2a,0xdc,0x00,
+  0x00,0x02,0x07,0x00,0x00,0x79,0x10,0x00,0x00,0x00,0x00,0x00,0x28,
+  0xa8,0x90,0x00,0xed,0xec,0x5b,0x00,0x12,0x12,0x33,0x00,0x00,0x00,
+  0x00,0x00,0xf8,0x00,0x60,0x00,0xf7,0x00,0xeb,0x00,0x12,0x00,0x12,
+  0x00,0x00,0x00,0x00,0x00,0xf8,0x01,0x23,0x00,0xf7,0x00,0x36,0x00,
+  0x12,0x00,0xd7,0x00,0x00,0x00,0x5a,0x00,0xf3,0x00,0xdc,0x00,0x99,
+  0x00,0x07,0x00,0x83,0x00,0x10,0x00,0x7c,0x00,0x00,0x00,0x08,0x3c,
+  0x01,0x00,0x01,0xec,0x00,0x00,0x81,0x12,0x00,0x00,0x7c,0x00,0x00,
+  0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,
+  0xff,0x00,0x00,0x00,0x05,0xf8,0x00,0x00,0x01,0xf7,0x00,0x00,0x81,
+  0x12,0x00,0x00,0x7c,0x00,0x00,0x00,0x24,0x67,0xdc,0x00,0xfe,0x04,
+  0x07,0x00,0x41,0xd7,0x10,0x00,0x00,0x77,0x00,0x00,0x90,0xd0,0x4f,
+  0x00,0xd8,0xb4,0xe5,0x00,0x15,0xd4,0xd6,0x00,0x00,0x77,0x77,0x00,
+  0x2f,0xff,0x29,0x00,0xfe,0xff,0x4e,0x00,0x80,0xff,0xd7,0x00,0x7c,
+  0xff,0x5a,0x00,0x82,0xcb,0xdc,0x00,0x00,0xb4,0x07,0x00,0x81,0xd4,
+  0x10,0x00,0x7c,0x77,0x00,0x00,0x58,0xbd,0x02,0x00,0xd6,0xda,0x00,
+  0x00,0xd4,0xd4,0x00,0x00,0x77,0x77,0x00,0x00,0x2a,0x38,0x4f,0x00,
+  0x02,0xad,0xe5,0x00,0x79,0x74,0xd6,0x00,0x00,0x00,0x77,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x73,0x06,0x24,0x26,0x36,0x36,0x2c,0x02,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2d,0x26,0x1b,0x5b,0x08,
+  0x12,0x01,0x08,0x5b,0x1b,0x26,0x2d,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2d,
+  0x21,0x12,0x60,0x0e,0x4c,0x1b,0x51,0x3f,0x1b,0x4c,0x0e,0x60,0x4c,
+  0x04,0x2d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x2d,0x1b,0x03,0x14,0x07,0x24,0x24,0x06,0x06,
+  0x06,0x06,0x24,0x66,0x47,0x4c,0x03,0x12,0x09,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x01,0x11,0x1b,
+  0x66,0x3a,0x0f,0x31,0x13,0x23,0x31,0x31,0x13,0x0f,0x3a,0x24,0x1b,
+  0x11,0x4c,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x7b,0x0e,0x14,0x84,0x1b,0x2d,0x2d,0x0d,0x13,0x13,0x31,0x13,
+  0x31,0x23,0x34,0x2d,0x2d,0x1b,0x12,0x80,0x14,0x43,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x14,0x1a,0x12,0x84,0x45,0x04,
+  0x44,0x23,0x55,0x34,0x13,0x31,0x75,0x68,0x2d,0x44,0x04,0x45,0x84,
+  0x4c,0x1a,0x4c,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2d,0x1b,
+  0x11,0x84,0x1a,0x0e,0x4c,0x4c,0x43,0x2d,0x13,0x4d,0x13,0x13,0x31,
+  0x55,0x2d,0x43,0x4c,0x4c,0x0e,0x65,0x32,0x11,0x1b,0x2d,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x26,0x03,0x14,0x1a,0x65,0x0e,0x1a,0x60,0x1b,
+  0x2d,0x64,0x68,0x85,0x68,0x7f,0x23,0x2d,0x1b,0x60,0x65,0x0e,0x60,
+  0x1a,0x14,0x03,0x26,0x00,0x00,0x00,0x00,0x00,0x2d,0x4c,0x60,0x5a,
+  0x11,0x60,0x4b,0x46,0x1a,0x1a,0x21,0x2d,0x68,0x0d,0x31,0x68,0x2d,
+  0x21,0x1a,0x1a,0x1a,0x1a,0x65,0x11,0x41,0x60,0x4c,0x2d,0x00,0x00,
+  0x00,0x00,0x04,0x1a,0x4c,0x65,0x1a,0x1a,0x84,0x4b,0x65,0x39,0x41,
+  0x7b,0x64,0x75,0x31,0x2d,0x7b,0x41,0x0e,0x65,0x65,0x08,0x1a,0x1a,
+  0x65,0x4c,0x65,0x04,0x00,0x00,0x00,0x73,0x1b,0x11,0x84,0x30,0x16,
+  0x14,0x29,0x46,0x08,0x1a,0x54,0x07,0x34,0x2d,0x0d,0x0d,0x07,0x35,
+  0x45,0x29,0x65,0x65,0x14,0x16,0x46,0x84,0x11,0x1b,0x73,0x00,0x00,
+  0x2c,0x0e,0x1a,0x05,0x62,0x14,0x29,0x1a,0x65,0x1a,0x4e,0x50,0x0f,
+  0x2d,0x7b,0x43,0x0d,0x0f,0x50,0x16,0x65,0x60,0x1a,0x69,0x69,0x0b,
+  0x91,0x08,0x0e,0x3d,0x00,0x00,0x1d,0x11,0x0e,0x11,0x65,0x11,0x4e,
+  0x1a,0x1a,0x1a,0x14,0x24,0x0d,0x04,0x14,0x29,0x04,0x2d,0x24,0x14,
+  0x6d,0x1a,0x65,0x38,0x11,0x14,0x67,0x39,0x11,0x24,0x00,0x00,0x1d,
+  0x4b,0x0e,0x39,0x0b,0x4e,0x05,0x0b,0x84,0x65,0x05,0x0f,0x7b,0x65,
+  0x4b,0x4b,0x65,0x64,0x66,0x40,0x39,0x16,0x16,0x54,0x4e,0x28,0x39,
+  0x0e,0x46,0x53,0x00,0x00,0x66,0x41,0x60,0x13,0x06,0x2c,0x2c,0x2c,
+  0x06,0x2c,0x2c,0x66,0x55,0x1a,0x15,0x6d,0x08,0x4d,0x0f,0x7a,0x2c,
+  0x06,0x2c,0x06,0x06,0x06,0x13,0x4c,0x46,0x0c,0x00,0x00,0x5e,0x08,
+  0x08,0x2d,0x13,0x68,0x68,0x0f,0x0f,0x75,0x34,0x31,0x13,0x47,0x1a,
+  0x1a,0x49,0x34,0x31,0x8a,0x0f,0x0f,0x0f,0x24,0x24,0x23,0x44,0x4c,
+  0x2a,0x0c,0x00,0x00,0x02,0x11,0x1a,0x2d,0x2d,0x68,0x68,0x31,0x4d,
+  0x66,0x0f,0x66,0x31,0x34,0x1d,0x24,0x13,0x31,0x66,0x0f,0x66,0x68,
+  0x31,0x68,0x0f,0x2d,0x2d,0x65,0x67,0x02,0x00,0x00,0x4f,0x37,0x28,
+  0x09,0x2d,0x8a,0x66,0x8a,0x66,0x66,0x8a,0x85,0x2d,0x2d,0x2d,0x2d,
+  0x7b,0x2d,0x7f,0x7f,0x8a,0x7f,0x8a,0x66,0x7f,0x2d,0x43,0x28,0x12,
+  0x4f,0x00,0x00,0x00,0x47,0x10,0x5f,0x44,0x75,0x66,0x8a,0x7f,0x3a,
+  0x66,0x64,0x5d,0x1a,0x01,0x12,0x1a,0x5d,0x7b,0x3a,0x66,0x8a,0x66,
+  0x7f,0x68,0x44,0x5f,0x5c,0x1c,0x00,0x00,0x00,0x00,0x2c,0x67,0x1a,
+  0x2d,0x2d,0x24,0x8a,0x66,0x8a,0x4d,0x64,0x08,0x2a,0x2a,0x1f,0x1f,
+  0x65,0x7b,0x85,0x3a,0x7f,0x8a,0x56,0x2d,0x09,0x29,0x4b,0x2c,0x00,
+  0x00,0x00,0x00,0x00,0x47,0x42,0x6e,0x81,0x7b,0x66,0x56,0x66,0x0d,
+  0x07,0x78,0x46,0x1f,0x2a,0x46,0x6a,0x07,0x0d,0x66,0x56,0x66,0x7b,
+  0x81,0x6e,0x42,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x41,0x15,
+  0x04,0x2d,0x85,0x66,0x68,0x7b,0x67,0x1f,0x1f,0x1f,0x1f,0x1f,0x2a,
+  0x67,0x7b,0x68,0x66,0x34,0x2d,0x04,0x15,0x93,0x56,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x24,0x29,0x30,0x8e,0x2d,0x2d,0x2d,0x1b,0x61,
+  0x2e,0x1f,0x15,0x2a,0x0a,0x2e,0x2e,0x1b,0x0d,0x2d,0x2d,0x8e,0x7e,
+  0x29,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x6a,
+  0x7e,0x21,0x2d,0x7b,0x67,0x61,0x2e,0x0a,0x19,0x19,0x0a,0x2e,0x61,
+  0x67,0x7b,0x2d,0x74,0x30,0x6a,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x18,0x4b,0x6a,0x2b,0x47,0x2a,0x2e,0x1f,
+  0x0a,0x1f,0x0a,0x1f,0x1f,0x2e,0x2a,0x47,0x2b,0x6a,0x4b,0x18,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5e,
+  0x1a,0x72,0x42,0x82,0x19,0x19,0x48,0x2e,0x2e,0x27,0x28,0x19,0x10,
+  0x42,0x61,0x65,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x22,0x10,0x61,0x61,0x27,0x4a,
+  0x19,0x8b,0x4a,0x27,0x2e,0x61,0x10,0x51,0x33,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x50,0x22,0x1e,0x2a,0x10,0x38,0x1e,0x4a,0x46,0x38,0x22,0x02,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x52,0x02,
+  0x02,0x52,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  };
\ No newline at end of file
--- /dev/null
+++ b/Engine/src/mmulti.c
@@ -1,0 +1,1709 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "mmulti_unstable.h"
+#include <enet.h>
+
+//STL
+//#include <vector>
+//#include "buildqueue.h"
+
+#include "platform.h"
+
+#include "pragmas.h"
+
+#define MAXPLAYERS 16
+#define BAKSIZ 16384
+#define SIMULATEERRORS 0
+#define SHOWSENDPACKETS 0
+#define SHOWGETPACKETS 0
+#define PRINTERRORS 0
+
+#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])
+
+static long incnt[MAXPLAYERS], outcntplc[MAXPLAYERS], outcntend[MAXPLAYERS];
+static char errorgotnum[MAXPLAYERS];
+static char errorfixnum[MAXPLAYERS];
+static char errorresendnum[MAXPLAYERS];
+#if (PRINTERRORS)
+	static char lasterrorgotnum[MAXPLAYERS];
+#endif
+
+long crctable[256];
+int tmpmax[8]; //addfaz variable addition (you could probs think of something better)
+int itmp = 0; //addfaz router fix STUN
+
+static char lastpacket[576], inlastpacket = 0;
+static short lastpacketfrom, lastpacketleng;
+
+extern long totalclock;  /* MUST EXTERN 1 ANNOYING VARIABLE FROM GAME */
+static long timeoutcount = 60, resendagaincount = 4, lastsendtime[MAXPLAYERS];
+
+extern unsigned short g_bStun;
+
+static short bakpacketptr[MAXPLAYERS][256], bakpacketlen[MAXPLAYERS][256];
+static char bakpacketbuf[BAKSIZ];
+static long bakpacketplc = 0;
+
+short myconnectindex, numplayers;
+short connecthead, connectpoint2[MAXPLAYERS];
+char syncstate = 0;
+
+extern int _argc;
+extern char **_argv;
+
+#define MAXPACKETSIZE 2048
+typedef struct
+{
+	short intnum;                /* communication between Game and the driver */
+	short command;               /* 1-send, 2-get */
+	short other;                 /* dest for send, set by get (-1 = no packet) */
+	short numbytes;
+	short myconnectindex;
+	short numplayers;
+	short gametype;              /* gametype: 1-serial,2-modem,3-net */
+	short filler;
+	char buffer[MAXPACKETSIZE];
+	long longcalladdress;
+} gcomtype;
+static gcomtype *gcom;
+
+/*
+//
+// Packet queue data structs
+//
+typedef struct packet_wrapper
+{
+	unsigned long packetnumber;
+	gcomtype packet;
+} PACKETWRAPPER;
+
+typedef PACKETWRAPPER packet_buffer;
+
+unsigned long currentpacketnumber[MAXPLAYERS];
+
+//
+// Packet Buffer
+//
+packet_buffer pBuff[256];
+*/
+
+
+/*
+typedef struct 
+{
+	unsigned char buffer[MAXPACKETSIZE];
+}PACKET;
+*/
+
+//typedef std::vector<PACKET> PacketQueue;
+
+enum ECommitCMDs
+{
+	COMMIT_CMD_SEND				= 1,
+	COMMIT_CMD_GET              = 2,
+	COMMIT_CMD_SENDTOALL        = 3,
+	COMMIT_CMD_SENDTOALLOTHERS  = 4,
+	COMMIT_CMD_SCORE            = 5,
+};
+
+
+// Queue of out going packets.
+//PacketQueue outgoingPacketQueue;
+//outgoingPacketQueue.reserve(128);
+
+gcomtype *init_network_transport(char **ARGV, int argpos);
+void deinit_network_transport(gcomtype *gcom);
+//void unstable_callcommit(void);
+void dosendpackets(long other);
+
+
+void unstable_initcrc(void)
+{
+	long i, j, k, a;
+
+	for(j=0;j<256;j++)      /* Calculate CRC table */
+	{
+		k = (j<<8); a = 0;
+		for(i=7;i>=0;i--)
+		{
+			if (((k^a)&0x8000) > 0)
+				a = ((a<<1)&65535) ^ 0x1021;   /* 0x1021 = genpoly */
+			else
+				a = ((a<<1)&65535);
+			k = ((k<<1)&65535);
+		}
+		crctable[j] = (a&65535);
+	}
+}
+
+
+long unstable_getcrc(char *buffer, short bufleng)
+{
+	long i, j;
+
+	j = 0;
+	for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
+	return(j&65535);
+}
+
+void unstable_initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
+{
+	long i;
+
+	unstable_initcrc();
+	for(i=0;i<MAXPLAYERS;i++)
+	{
+		incnt[i] = 0L;
+		outcntplc[i] = 0L;
+		outcntend[i] = 0L;
+		bakpacketlen[i][255] = -1;
+	}
+
+	// clear out the packet ordering
+//	memset(&currentpacketnumber, 0, sizeof(unsigned long) * MAXPLAYERS);
+
+	for (i = _argc - 1; i > 0; i--)
+    {
+        const char *arg = _argv[i];
+        char ch = *arg;
+        if ((ch == '-') || (ch == '/'))
+        {
+			if (stricmp(arg + 1, "net") == 0)
+                break;
+        }
+    }
+
+	if ((i == 0) || (i+1 == _argc))
+	{
+		numplayers = 1; myconnectindex = 0;
+		connecthead = 0; connectpoint2[0] = -1;
+		return;
+	}
+
+    gcom = init_network_transport(_argv, i+1);
+    if (gcom == NULL)
+	{
+        Error(EXIT_SUCCESS, "Network transport initialization failed!\n"
+							"Be sure you have closed *all* the previous instances of\n"
+							"the xDuke and that no other application is using the\n"
+							"same port# as Duke. If you can't solve this problem, try\n"
+							"changing the port# or reboot.\n");
+	}
+
+	numplayers = gcom->numplayers;
+	myconnectindex = gcom->myconnectindex-1;
+#if (SIMULATEERRORS != 0)
+	srand(myconnectindex*24572457+345356);
+#endif
+	connecthead = 0;
+	for(i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
+	connectpoint2[numplayers-1] = -1;
+
+	for(i=0;i<numplayers;i++) lastsendtime[i] = totalclock;
+}
+
+
+void dosendpackets(long other)
+{
+	long i, j, k, messleng;
+	unsigned short dacrc;
+
+	if (outcntplc[other] == outcntend[other]) return;
+
+#if (PRINTERRORS)
+	if (errorgotnum[other] > lasterrorgotnum[other])
+	{
+		lasterrorgotnum[other]++;
+		printf(" MeWant %ld",incnt[other]&255);
+	}
+#endif
+
+	if (outcntplc[other]+1 == outcntend[other])
+	{     /* Send 1 sub-packet */
+		k = 0;
+		gcom->buffer[k++] = (outcntplc[other]&255);
+		gcom->buffer[k++] = (errorgotnum[other]&7)+((errorresendnum[other]&7)<<3);
+		gcom->buffer[k++] = (incnt[other]&255);
+
+		j = bakpacketptr[other][outcntplc[other]&255];
+		messleng = bakpacketlen[other][outcntplc[other]&255];
+		for(i=0;i<messleng;i++)
+			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
+		outcntplc[other]++;
+	}
+	else
+	{     /* Send 2 sub-packets */
+		k = 0;
+		gcom->buffer[k++] = (outcntplc[other]&255);
+		gcom->buffer[k++] = (errorgotnum[other]&7)+((errorresendnum[other]&7)<<3)+128;
+		gcom->buffer[k++] = (incnt[other]&255);
+
+			/* First half-packet */
+		j = bakpacketptr[other][outcntplc[other]&255];
+		messleng = bakpacketlen[other][outcntplc[other]&255];
+		gcom->buffer[k++] = (char)(messleng&255);
+		gcom->buffer[k++] = (char)(messleng>>8);
+		for(i=0;i<messleng;i++)
+			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
+		outcntplc[other]++;
+
+			/* Second half-packet */
+		j = bakpacketptr[other][outcntplc[other]&255];
+		messleng = bakpacketlen[other][outcntplc[other]&255];
+		for(i=0;i<messleng;i++)
+			gcom->buffer[k++] = bakpacketbuf[(i+j)&(BAKSIZ-1)];
+		outcntplc[other]++;
+
+	}
+
+	dacrc = getcrc(gcom->buffer,(short)k);
+	gcom->buffer[k++] = (dacrc&255);
+	gcom->buffer[k++] = (dacrc>>8);
+
+	gcom->other = other+1;
+	gcom->numbytes = k;
+
+#if (SHOWSENDPACKETS)
+	printf("Send(%ld): ",gcom->other);
+	for(i=0;i<gcom->numbytes;i++) printf("%2x ",gcom->buffer[i]);
+	printf("\n");
+#endif
+
+#if (SIMULATEERRORS != 0)
+	if (!(rand()&SIMULATEERRORS)) gcom->buffer[rand()%gcom->numbytes] = (rand()&255);
+	if (rand()&SIMULATEERRORS)
+#endif
+		{ 
+			gcom->command = COMMIT_CMD_SEND; 
+			unstable_callcommit(); 
+		}
+}
+
+
+void unstable_sendpacket(long other, char *bufptr, long messleng)
+{
+	long i = 0;
+    long j = 0;
+
+	if (numplayers < 2) return;
+
+	i = 0;
+	if (bakpacketlen[other][(outcntend[other]-1)&255] == messleng)
+	{
+		j = bakpacketptr[other][(outcntend[other]-1)&255];
+		for(i=messleng-1;i>=0;i--)
+			if (bakpacketbuf[(i+j)&(BAKSIZ-1)] != bufptr[i]) break;
+	}
+	bakpacketlen[other][outcntend[other]&255] = messleng;
+
+	if (i < 0)   /* Point to last packet to save space on bakpacketbuf */
+		bakpacketptr[other][outcntend[other]&255] = j;
+	else
+	{
+		bakpacketptr[other][outcntend[other]&255] = bakpacketplc;
+		for(i=0;i<messleng;i++)
+			bakpacketbuf[(bakpacketplc+i)&(BAKSIZ-1)] = bufptr[i];
+		bakpacketplc = ((bakpacketplc+messleng)&(BAKSIZ-1));
+	}
+	outcntend[other]++;
+
+	lastsendtime[other] = totalclock;
+	dosendpackets(other);
+}
+
+
+void unstable_setpackettimeout(long datimeoutcount, long daresendagaincount)
+{
+	// Don't do this it keeps '/f4' from working
+	// Though /f4 feels weird on my mouse.... slugish is the word...
+	/*
+	long i;
+
+	timeoutcount = datimeoutcount;
+	resendagaincount = daresendagaincount;
+	for(i=0;i<numplayers;i++) lastsendtime[i] = totalclock;
+	*/
+}
+
+
+void unstable_uninitmultiplayers(void)
+{
+    deinit_network_transport(gcom);
+    gcom = NULL;
+}
+
+void unstable_sendlogon(void)
+{
+}
+
+void unstable_sendlogoff(void)
+{
+	long i;
+	char tempbuf[2];
+
+	tempbuf[0] = 255;
+	tempbuf[1] = myconnectindex;
+	for(i=connecthead;i>=0;i=connectpoint2[i])
+		if (i != myconnectindex)
+			sendpacket(i,tempbuf,2L);
+}
+
+int unstable_getoutputcirclesize(void)
+{
+	return(0);
+}
+
+void unstable_setsocket(short newsocket)
+{
+}
+
+
+short unstable_getpacket (short *other, char *bufptr)
+{
+	long i, messleng;
+	unsigned short dacrc;
+
+	if (numplayers < 2) return(0);
+
+	for(i=connecthead;i>=0;i=connectpoint2[i])
+		if (i != myconnectindex)
+		{
+			if (totalclock < lastsendtime[i]) lastsendtime[i] = totalclock;
+			if (totalclock > lastsendtime[i]+timeoutcount)
+			{
+#if (PRINTERRORS)
+					printf(" TimeOut!");
+#endif
+					errorgotnum[i] = errorfixnum[i]+1;
+
+					if ((outcntplc[i] == outcntend[i]) && (outcntplc[i] > 0))
+						{ outcntplc[i]--; lastsendtime[i] = totalclock; }
+					else
+						lastsendtime[i] += resendagaincount;
+					dosendpackets(i);
+				/* } */
+			}
+		}
+
+	if (inlastpacket != 0)
+	{
+			/* 2ND half of good double-packet */
+		inlastpacket = 0;
+		*other = lastpacketfrom;
+		memcpy(bufptr,lastpacket,lastpacketleng);
+		return(lastpacketleng);
+	}
+
+	gcom->command = COMMIT_CMD_GET;
+	unstable_callcommit();
+
+#if (SHOWGETPACKETS)
+	if (gcom->other != -1)
+	{
+		printf(" Get(%ld): ",gcom->other);
+		for(i=0;i<gcom->numbytes;i++) printf("%2x ",gcom->buffer[i]);
+		printf("\n");
+	}
+#endif
+
+	if (gcom->other < 0) return(0);
+	*other = gcom->other-1;
+
+	messleng = gcom->numbytes;
+
+	dacrc = ((unsigned short)gcom->buffer[messleng-2]);
+	dacrc += (((unsigned short)gcom->buffer[messleng-1])<<8);
+	if (dacrc != getcrc(gcom->buffer,(short)(messleng-2)))        /* CRC check */
+	{
+#if (PRINTERRORS)
+		printf("\n%ld CRC",gcom->buffer[0]);
+#endif
+		errorgotnum[*other] = errorfixnum[*other]+1;
+		return(0);
+	}
+
+	while ((errorfixnum[*other]&7) != ((gcom->buffer[1]>>3)&7))
+		errorfixnum[*other]++;
+
+	if ((gcom->buffer[1]&7) != (errorresendnum[*other]&7))
+	{
+		errorresendnum[*other]++;
+		outcntplc[*other] = (outcntend[*other]&0xffffff00)+gcom->buffer[2];
+		if (outcntplc[*other] > outcntend[*other]) outcntplc[*other] -= 256;
+	}
+
+	if (gcom->buffer[0] != (incnt[*other]&255))   /* CNT check */
+	{
+		if (((incnt[*other]-gcom->buffer[0])&255) > 32)
+		{
+			errorgotnum[*other] = errorfixnum[*other]+1;
+#if (PRINTERRORS)
+			printf("\n%ld CNT",gcom->buffer[0]);
+#endif
+		}
+#if (PRINTERRORS)
+		else
+		{
+			if (!(gcom->buffer[1]&128))           /* single else double packet */
+				printf("\n%ld cnt",gcom->buffer[0]);
+			else
+			{
+				if (((gcom->buffer[0]+1)&255) == (incnt[*other]&255))
+				{
+								 /* GOOD! Take second half of double packet */
+#if (PRINTERRORS)
+					printf("\n%ld-%ld .� ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
+#endif
+					messleng = ((long)gcom->buffer[3]) + (((long)gcom->buffer[4])<<8);
+					lastpacketleng = gcom->numbytes-7-messleng;
+					memcpy(bufptr,&gcom->buffer[messleng+5],lastpacketleng);
+					incnt[*other]++;
+					return(lastpacketleng);
+				}
+				else
+					printf("\n%ld-%ld cnt ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
+			}
+		}
+#endif
+		return(0);
+	}
+
+		/* PACKET WAS GOOD! */
+	if ((gcom->buffer[1]&128) == 0)           /* Single packet */
+	{
+#if (PRINTERRORS)
+		printf("\n%ld �  ",gcom->buffer[0]);
+#endif
+
+		messleng = gcom->numbytes-5;
+
+		memcpy(bufptr,&gcom->buffer[3],messleng);
+
+		incnt[*other]++;
+		return(messleng);
+	}
+
+														 /* Double packet */
+#if (PRINTERRORS)
+	printf("\n%ld-%ld �� ",gcom->buffer[0],(gcom->buffer[0]+1)&255);
+#endif
+
+	messleng = ((long)gcom->buffer[3]) + (((long)gcom->buffer[4])<<8);
+	lastpacketleng = gcom->numbytes-7-messleng;
+	inlastpacket = 1; lastpacketfrom = *other;
+
+	memcpy(bufptr,&gcom->buffer[5],messleng);
+	memcpy(lastpacket,&gcom->buffer[messleng+5],lastpacketleng);
+
+	incnt[*other] += 2;
+	return(messleng);
+}
+
+void unstable_flushpackets()
+{
+#if 0
+	long i;
+
+	if (numplayers < 2) return;
+
+	do
+	{
+		gcom->command = COMMIT_CMD_GET;
+		callcommit();
+	} while (gcom->other >= 0);
+
+	for(i=connecthead;i>=0;i=connectpoint2[i])
+	{
+		incnt[i] = 0L;
+		outcntplc[i] = 0L;
+		outcntend[i] = 0L;
+		errorgotnum[i] = 0;
+		errorfixnum[i] = 0;
+		errorresendnum[i] = 0;
+		lastsendtime[i] = totalclock;
+	}
+#endif
+}
+
+void unstable_genericmultifunction(long other, char *bufptr, long messleng, long command)
+{
+	if (numplayers < 2) return;
+
+	gcom->command = command;
+	gcom->numbytes = min(messleng,MAXPACKETSIZE);
+	copybuf(bufptr,gcom->buffer,(gcom->numbytes+3)>>2);
+	gcom->other = other+1;
+	callcommit();
+	
+}
+
+
+#if STUB_NETWORKING
+gcomtype *init_network_transport(char **ARGV, int argpos)
+{
+    printf("No networking support built in.\n");
+    return NULL;
+} /* init_network_transport */
+
+void deinit_network_transport(gcomtype *gcom)
+{
+}
+
+void callcommit(void)
+{
+}
+
+#elif (defined PLATFORM_DOS)
+gcomtype *init_network_transport(char **ARGV, int argpos)
+{
+    /*
+     * How to talk to COMMIT is passed as a pointer to a block of memory
+     *  that COMMIT.EXE configures...
+     */
+	return((gcomtype *)atol(ARGV[argpos]));  /* UGH!  --ryan. */
+} /* init_network_transport */
+
+static union REGS regs;
+
+#pragma aux longcall =\
+	"call eax",\
+	parm [eax]
+
+void callcommit(void)
+{
+	if (gcom->intnum&0xff00)
+		longcall(gcom->longcalladdress);
+	else
+		int386(gcom->intnum,&regs,&regs);
+}
+
+void deinit_network_transport(gcomtype *gcom)
+{
+    /* no-op, apparently. */
+}
+
+
+#elif UDP_NETWORKING
+
+#if PLATFORM_WIN32
+#  include <winsock.h>
+#  define EAGAIN WSAEWOULDBLOCK
+#  define EWOULDBLOCK WSAEWOULDBLOCK
+#  define ECONNREFUSED WSAECONNRESET
+#  define socklen_t size_t
+#  define netstrerror() win32netstrerror()
+#  define neterrno() WSAGetLastError()
+#  define sockettype SOCKET
+#  define socketclose(x) closesocket(x)
+#else
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  include <netdb.h>
+#  include <sys/uio.h>
+#  include <sys/ioctl.h>
+#  include <sys/time.h>
+#  include <errno.h>
+#  include <fcntl.h>
+#  include <time.h>
+#  define netstrerror() strerror(errno)
+#  define neterrno() errno
+#  define sockettype int
+#  define socketclose(x) close(x)
+#  ifndef MSG_ERRQUEUE  /* legacy glibc header workaround... */
+#    define MSG_ERRQUEUE 0x2000
+#  endif
+#endif
+
+#define SOCKET_SHUTDOWN_BOTH 2
+
+#include <signal.h>
+#include "cache1d.h"  /* kopen4load for cfg file. */
+#include "display.h"  /* getticks */
+
+#define IPSEG1(ip) ((((unsigned int) ip) & 0xFF000000) >> 24)
+#define IPSEG2(ip) ((((unsigned int) ip) & 0x00FF0000) >> 16)
+#define IPSEG3(ip) ((((unsigned int) ip) & 0x0000FF00) >>  8)
+#define IPSEG4(ip) ((((unsigned int) ip) & 0x000000FF)      )
+
+#define MAX_PLAYERS 16
+#define BUILD_DEFAULT_UDP_PORT 1635  /* eh...why not? */
+#define CLIENT_POLL_DELAY 3000  /* ms between pings at peer-to-peer startup. */
+#define HEADER_PEER_GREETING 245
+
+static sockettype udpsocket = -1;
+static short udpport = BUILD_DEFAULT_UDP_PORT;
+
+static struct {
+  int host;
+  short port;
+} allowed_addresses[MAX_PLAYERS];  /* only respond to these IPs. */
+
+volatile int ctrlc_pressed = 0;
+static void siginthandler(int sigint)
+{
+    ctrlc_pressed = 1;
+}
+
+#if PLATFORM_WIN32
+/*
+ * Figure out what the last failing Win32 API call was, and
+ *  generate a human-readable string for the error message.
+ *
+ * The return value is a static buffer that is overwritten with
+ *  each call to this function.
+ *
+ * Code lifted from PhysicsFS: http://icculus.org/physfs/
+ */
+static const char *win32netstrerror(void)
+{
+    static TCHAR msgbuf[255];
+    TCHAR *ptr = msgbuf;
+
+    FormatMessage(
+        FORMAT_MESSAGE_FROM_SYSTEM |
+        FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL,
+        WSAGetLastError(), /*GetLastError(),*/
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+        msgbuf,
+        sizeof (msgbuf) / sizeof (TCHAR),
+        NULL 
+    );
+
+        /* chop off newlines. */
+    for (ptr = msgbuf; *ptr; ptr++)
+    {
+        if ((*ptr == '\n') || (*ptr == '\r'))
+        {
+            *ptr = ' ';
+            break;
+        } /* if */
+    } /* for */
+
+    return((const char *) msgbuf);
+} /* win32strerror */
+#endif
+
+
+typedef enum
+{
+    udpmode_peer,
+    udpmode_server,
+    udpmode_client
+} udpmodes;
+static udpmodes udpmode = udpmode_peer;
+
+
+static char *static_ipstring(int ip)
+{
+    static char s[16];
+    sprintf(s, "%u.%u.%u.%u", IPSEG1(ip), IPSEG2(ip), IPSEG3(ip), IPSEG4(ip));
+    return(s);
+}
+
+
+static int send_udp_packet(int ip, short port, void *pkt, size_t pktsize)
+{
+    /* !!! FIXME: See if this would ever block. */
+    /* !!! FIXME: See if this would send a partial packet. */
+    struct sockaddr_in addr;
+    int rc;
+
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(ip);
+	addr.sin_port = htons(port);
+
+    rc = sendto(udpsocket, pkt, pktsize, 0,
+                (struct sockaddr *) &addr, sizeof (addr));
+
+    if (rc != (int) pktsize)
+    {
+        printf("sendto err rc==%d when sending %d to %s:%d [%s].\n",
+                rc, (int) pktsize, static_ipstring(ip), (int) port,
+                netstrerror());
+        return(0);
+    }
+
+/*printf("Sent %d byte packet to %s:%d\n", (int) pktsize, static_ipstring(ip), (int) port);*/
+
+    return(1);
+}
+
+
+static void process_udp_send_queue(void)
+{
+    /* Fill this in if the non-blocking socket becomes an issue. */
+}
+
+
+static int get_udp_packet(int *ip, short *_port, void *pkt, size_t pktsize)
+{
+    int err = 0;
+    struct sockaddr_in addr;
+    short port;
+    socklen_t fromlen = sizeof (addr);
+    int valid = 0;
+    int i;
+
+    /* FIXME: Will this ever receive a partial packet? */
+    int rc = recvfrom(udpsocket, pkt, pktsize, 0,
+                      (struct sockaddr *) &addr, (int *)&fromlen);
+	
+	if (rc == -1)
+        err = neterrno();
+
+#if !PLATFORM_WIN32
+	/* !!! FIXME: Linux specific? */
+    if (rc == -1)  /* fill in the addr structure on error... */
+    {
+        socklen_t l = sizeof (addr);
+        recvfrom(udpsocket, NULL, 0, MSG_ERRQUEUE,
+                 (struct sockaddr *) &addr, &l);
+    }
+#endif
+
+    *ip = ntohl(addr.sin_addr.s_addr);
+    port = ntohs(addr.sin_port);
+    if (_port)
+        *_port = port;
+
+    /*
+     * Reject packets from unallowed IPs. Prevents (ha) DoS attacks and
+     *  other confusion...
+     */
+    if (gcom == NULL)
+	{
+		if(g_bStun) //if stun is enabled
+		{
+			if ((allowed_addresses[itmp].host == *ip)
+			&& (allowed_addresses[itmp].port == port))
+			{
+				valid = 1; //only accept packets from the current player (itmp) and no one else
+			}
+			else
+			{
+				return 0; //reject packets from other players
+			}
+		}
+		else
+        {
+			valid = 1;
+		}
+	}
+    else
+    {
+        for (i = 1; i <= gcom->numplayers; i++)
+        {
+            if ((allowed_addresses[i].host == *ip)
+				&& (allowed_addresses[i].port == port))
+
+            {
+				valid = i;
+
+				//printf ("Packet received from %s:%d\n",static_ipstring(*ip), (int) port);
+				
+                break;
+            }
+
+		}
+    }
+
+    /*if (!valid)
+    {
+        
+        static int unallowed_ip_spam = 0;
+        if (unallowed_ip_spam <= 100)
+        {
+            printf("Packet received from unallowed IP %s:%d\n",
+                    static_ipstring(*ip), (int) port);
+            if (unallowed_ip_spam == 100)
+                printf("(Disabling further unallowed IP spam.)\n");
+            unallowed_ip_spam++;
+        }
+        
+        return(0);
+    } */
+
+    if (rc == -1)
+    {
+        if ((err == EAGAIN) || (err == EWOULDBLOCK))
+            rc = 0;
+
+        else if (err == ECONNREFUSED)   //"connection reset by peer" in winsock 
+        {
+            
+             //  This means that we sent a packet to an unopened port, and
+             //  it responded by telling us to piss off. Take them out of the
+             //  allowed list. We check gcom so that we don't worry about this
+             //  during detection when game might still be loading elsewhere.
+              
+            if (gcom != NULL)
+            {
+                allowed_addresses[valid].host = 0;
+                printf("%s:%d refused packets. He either crashed or quited abruptly. Please restart.\n",
+                        static_ipstring(*ip), (int) port);
+            }
+            // !!! FIXME: Actually boot player, too. 
+        }
+
+        else
+        {
+            printf("recvfrom err rc==%d when getting %d from %s:%d [%s].\n",
+                    rc, (int) pktsize, static_ipstring(*ip), (int) port,
+                    netstrerror());
+        }
+    } 
+//else printf("Got %d byte packet from %s:%d\n", (int) rc, static_ipstring(*ip), (int) port);
+	//printf( "IP from client %d", *ip);
+    return(rc);
+}
+
+
+static char *read_whole_file(const char *cfgfile)
+{
+    char *buf;
+    long len, rc;
+    long handle;
+
+    if (cfgfile == NULL)
+        return(NULL);
+
+    handle = kopen4load(cfgfile,0);
+    if (handle == -1)
+    {
+        printf("ERROR: Failed to open config file [%s].\n", cfgfile);
+        return(NULL);
+    }
+
+    len = kfilelength(handle);
+    buf = (char *) malloc(len + 2);
+    if (!buf)
+    {
+        kclose(handle);
+        return(NULL);
+    }
+
+    rc = kread(handle, buf, len);
+    kclose(handle);
+    if (rc != len)
+    {
+        free(buf);
+        return(NULL);
+    }
+
+    buf[len] = '\0';
+    buf[len+1] = '\0';
+    return(buf);
+}
+
+static char *get_token(char **ptr)
+{
+    char *retval;
+    char *p = *ptr;
+    if (*p == '\0')
+        return(NULL);
+
+    while ((*p != '\0') && (isspace(*p)))
+        p++;
+
+    if (*p == '\0')  /* nothing but whitespace. */
+        return(NULL);
+
+    retval = p;
+    while ((*p != '\0') && (!isspace(*p)))
+        p++;
+
+    *p = '\0';
+    *ptr = p + 1;
+
+    /*printf("Got token [%s].\n", retval);*/
+    return(retval);
+}
+
+static int set_socket_blockmode(int onOrOff)
+{
+    unsigned long flags;
+    int rc = 0;
+
+    /* set socket to be (non-)blocking. */
+
+#if PLATFORM_WIN32
+    flags = (onOrOff) ? 0 : 1;
+    rc = (ioctlsocket(udpsocket, FIONBIO, &flags) == 0);
+#else
+    flags = fcntl(udpsocket, F_GETFL, 0);
+    if (flags != -1)
+    {
+        if (onOrOff)
+            flags &= ~O_NONBLOCK;
+        else
+    	    flags |= O_NONBLOCK;
+	    rc = (fcntl(udpsocket, F_SETFL, flags) == 0);
+    }
+#endif
+
+    if (!rc)
+    {
+        printf("set socket %sblocking failed: %s\n",
+            ((onOrOff) ? "" : "non-"), netstrerror());
+    }
+
+    return(rc);
+}
+
+
+static int set_socket_broadcast(int onOrOff)
+{
+    int f = (onOrOff) ? 1 : 0;
+    int rc;
+
+    /* give socket clearance to broadcast. */
+    rc = setsockopt(udpsocket, SOL_SOCKET, SO_BROADCAST, (char *)(&f), sizeof (f)) == 0;
+    if (!rc)
+    {
+        printf("%sset SO_BROADCAST failed: %s\n",
+            ((onOrOff) ? "" : "un"), netstrerror());
+    }
+
+    return(rc);
+}
+
+
+static int open_udp_socket(int ip, int port)
+{
+    struct sockaddr_in addr;
+
+    printf("Setting up UDP interface %s:%d...\n", static_ipstring(ip), port);
+	printf("Stun is currently %s\n", (g_bStun) ? "Enabled":"Disabled");
+
+    udpsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (udpsocket == -1)
+    {
+        printf("socket creation failed: %s\n", netstrerror());
+        return(0);
+    }
+
+    if (!set_socket_blockmode(0))
+        return(0);
+
+    #if !PLATFORM_WIN32
+    {
+        /* !!! FIXME: Might be Linux (not Unix, not BSD, not WinSock) specific. */
+        int flags = 1;
+        setsockopt(udpsocket, SOL_IP, IP_RECVERR, &flags, sizeof (flags));
+    }
+    #endif
+
+    memset(&addr, '\0', sizeof (addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(ip);
+    addr.sin_port = htons((unsigned short)port);
+    if (bind(udpsocket, (struct sockaddr *) &addr, sizeof (addr)) == -1)
+    {
+        printf("socket binding failed: %s\n", netstrerror());
+        return(0);
+    }
+
+    return(1);
+}
+
+/* server init. */
+static int wait_for_other_players(gcomtype *gcom, int myip)
+{
+    printf("Server code NOT implemented!\n");
+    return(0);
+}
+
+/* client init. */
+static int connect_to_server(gcomtype *gcom, int myip)
+{
+    printf("Client code NOT implemented!\n");
+    return(0);
+}
+
+typedef struct
+{
+    unsigned char dummy1;   /* so these don't confuse game after load. */
+    unsigned char dummy2;   /* so these don't confuse game after load. */
+    unsigned char dummy3;   /* so these don't confuse game after load. */
+    unsigned char header;   /* always HEADER_PEER_GREETING (245). */
+    unsigned short id;
+} PacketPeerGreeting;
+
+
+static void send_peer_greeting(int ip, short port, short myid)
+{
+    PacketPeerGreeting packet;
+	memset(&packet, '\0', sizeof (packet));
+    packet.header = HEADER_PEER_GREETING;
+    packet.id = BUILDSWAP_INTEL16(myid);
+    send_udp_packet(ip, port, &packet, sizeof (packet));
+}
+
+
+/* peer to peer init. */
+static int connect_to_everyone(gcomtype *gcom, int myip, int bcast)
+{
+    PacketPeerGreeting packet;
+    unsigned short my_id = 0;
+    int i;
+    int rc;
+    int ip;
+    short port;
+    int first_send = 1;
+    unsigned short heard_from[MAX_PLAYERS];
+    unsigned long resendat;
+    int max;
+    int remaining;
+
+    printf("peer-to-peer init. CTRL-C to abort...\n");
+
+    if (bcast)
+    {
+        if (gcom->numplayers > 1)
+        {
+            printf("ERROR: Can't do both 'broadcast' and 'allow'.\n");
+            return(0);
+        }
+
+        if (!set_socket_broadcast(1))
+            return(0);
+
+        gcom->numplayers = bcast + 1;
+    }
+
+    memset(heard_from, '\0', sizeof (heard_from));
+
+    while (my_id == 0)  /* player number is based on id, low to high. */
+        my_id = (unsigned short) rand();
+
+    printf("(This client's ID for this round is 0x%X.)\n\n", my_id);
+
+    resendat = getticks();
+    remaining = max = gcom->numplayers - 1;
+
+    printf("Waiting for %d player%s...\n", remaining, remaining==1 ? "":"s");
+    if (remaining == 0)
+    {
+        printf("Hmmm...don't have time to play with myself.\n");
+        return(0);
+    }
+
+    while ((remaining) && (!ctrlc_pressed))
+    {
+        if (resendat <= getticks())
+        {
+            if (bcast)
+            {
+                printf("%sroadcasting greeting...\n", first_send ? "B":"Reb");
+                /* !!! FIXME: This is...probably not right. */
+                send_peer_greeting(0xFFFFFFFF, udpport, my_id);
+            }
+            else
+            {
+                for (i = 0; (i < max) || g_bStun ; i++)
+                {
+				
+					//only checking one player at a time works
+					//this is where special formatting of allow lines comes in    
+					if(g_bStun)
+					{
+						i = itmp; //addfaz router fix
+					}
+	
+					if (!heard_from[i])
+					{
+	                   printf("%sending greeting to %s:%d...\n",
+                                first_send ? "S" : "Res",
+                                static_ipstring(allowed_addresses[i].host),
+                                allowed_addresses[i].port);
+    
+					   send_peer_greeting(allowed_addresses[i].host,
+                                           allowed_addresses[i].port,
+                                           my_id);
+                    }
+
+					// If this is stun-enabled then don't loop.
+					if(g_bStun)
+					{
+						break;
+					}
+                }
+            }
+            first_send = 0;
+            resendat += CLIENT_POLL_DELAY;
+        }
+
+        _idle();
+        process_udp_send_queue();
+
+        rc = get_udp_packet(&ip, &port, &packet, sizeof (packet));
+
+		//this is so we don't get unexpected packet errors from players already heard from
+		
+		if(g_bStun)	
+		{
+			//addfaz router/stun addition *Start*
+			//this is so we are not dealing with players already heard from.
+			for (i = 0; i < max; i++)
+			{
+				if (ip == allowed_addresses[i].host &&
+				port == allowed_addresses[i].port)
+				{
+					if (heard_from[i] != 0) //if we've heard from player already.
+					{
+						rc = 0;
+					}	     
+				}
+			}
+			//addfaz router/stun addition *End*
+		}
+
+
+        if ( (rc > 0) && (ip) && ((ip != myip) || (port != udpport)) )
+        {
+            char *ipstr = static_ipstring(ip);
+
+            for (i = 0; i < max; i++)
+            {
+
+                ////addfaz NAT addition *START*////
+				if(!g_bStun)
+				{
+					if(tmpmax[i] != 1)
+					{					
+						if (allowed_addresses[i].host == ip)
+						{
+							if(allowed_addresses[i].port != port)
+							{	
+								printf("Different player Port Number detected. %s:%i\n",ipstr,
+								allowed_addresses[i].port);
+								printf("Changed to %s:%i, player may be behind a firewall.\n", ipstr, port); //addfaz NAT addition
+								allowed_addresses[i].port = port;
+							}					
+						}
+					}
+				}
+				////addfaz NAT addition *END*////
+
+				if ((ip == allowed_addresses[i].host) &&
+                    (port == allowed_addresses[i].port))  //addfaz NAT line addition
+                {
+                    break;
+                }
+
+                if ((bcast) && (allowed_addresses[i].host == 0))
+                    break;  /* select this slot. */
+				
+            }
+
+            if (i == max)
+                printf("%s:%d is not an allowed player.\n", ipstr, port);
+
+            else if (rc != sizeof (packet))
+                printf("Missized packet/packet fragment from %s:%i\n", ipstr, port);
+
+            else if (packet.header != HEADER_PEER_GREETING)
+                printf("Unexpected packet type from %s:%i\n", ipstr, port);
+			
+            else if (heard_from[i] == 0)
+            {
+                packet.id = BUILDSWAP_INTEL16(packet.id);
+                heard_from[i] = packet.id;
+                allowed_addresses[i].host = ip;   /* bcast needs this. */
+                allowed_addresses[i].port = port;
+                remaining--;
+
+                printf("Heard from %s:%i (id 0x%X). %d player%s to go.\n",
+                        ipstr, port ,(int) packet.id,
+                        remaining, remaining == 1 ? "" : "s");
+
+                /* make sure they've heard from us at all... */
+                /* !!! FIXME: Could be fatal if packet is dropped... */
+                send_peer_greeting(allowed_addresses[i].host,
+                                   allowed_addresses[i].port,
+                                   my_id);
+		
+				if(g_bStun)
+				{
+					itmp++; //addfaz router/stun addition (goto next player)
+				}
+				else
+				{
+					tmpmax[i] = 1; //addfaz line addition
+				}
+            }
+        }
+    }
+	
+    if (ctrlc_pressed)
+    {
+        printf("Connection attempt aborted.\n");
+        return(0);
+    }
+
+    /* ok, now everyone is talking to you. Sort them into player numbers... */
+
+    heard_from[max] = my_id; /* so we sort, too... */
+    allowed_addresses[max].host = myip;
+    allowed_addresses[max].port = udpport;
+
+    do
+    {
+        remaining = 0;
+        for (i = 0; i < max; i++)
+        {
+            if (heard_from[i] == heard_from[i+1])  /* blah. */
+            {
+                printf("ERROR: Two players have the same random ID!\n");
+                printf("ERROR: Please restart the game to generate new IDs.\n");
+                return(0);
+            }
+
+            else if (heard_from[i] > heard_from[i+1])
+            {
+                int tmpi;
+                short tmps;
+
+                tmps = heard_from[i];
+                heard_from[i] = heard_from[i+1];
+                heard_from[i+1] = tmps;
+
+                tmpi = allowed_addresses[i].host;
+                allowed_addresses[i].host = allowed_addresses[i+1].host;
+                allowed_addresses[i+1].host = tmpi;
+
+                tmps = allowed_addresses[i].port;
+                allowed_addresses[i].port = allowed_addresses[i+1].port;
+                allowed_addresses[i+1].port = tmps;
+
+                remaining = 1;  /* yay for bubble sorting! */
+            }
+        }
+    } while (remaining);
+
+    /*
+     * Now we're sorted. But, the local player is referred to by both his
+     *  player number and player index ZERO, so bump everyone up one to
+     *  their actual index and fill in local player as item zero.
+     */
+
+    memmove(&allowed_addresses[1], &allowed_addresses[0],
+            sizeof (allowed_addresses) - sizeof (allowed_addresses[0]));
+    allowed_addresses[0].host = myip;
+
+    gcom->myconnectindex = 0;
+    for (i = 1; i <= gcom->numplayers; i++)
+    {
+        ip = (allowed_addresses[i].host);
+
+
+        if (ip == myip)
+		{
+            if (udpport == allowed_addresses[i].port)
+				gcom->myconnectindex = i;
+		}
+
+        printf("%s:%i is player #%i.\n", static_ipstring(ip),allowed_addresses[i].port,i);
+    }
+    assert(gcom->myconnectindex);
+
+    printf("Everyone ready! We are player #%i\n", gcom->myconnectindex);
+
+    /*
+     * Ok, we should have specific IPs and ports for all players, and
+     *  therefore shouldn't broadcast anymore. Disable permission to do so,
+     *  just in case, so we aren't flooding the LAN with broadcasted packets.
+     */
+    set_socket_broadcast(0);
+
+    return(1);
+}
+
+static int parse_ip(const char *str, int *ip)
+{
+    int ip1, ip2, ip3, ip4;
+
+    if (stricmp(str, "any") == 0)
+    {
+        *ip = 0;
+        return(1);
+    }
+
+    if (sscanf(str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4)
+    {
+        printf("\"%s\" is not a valid IP address.\n", str);
+        return(0);
+    }
+
+    /* we _should_ check that 0 <= ip? <= 255, but it'll fail later anyhow. */
+
+    *ip = ( ((ip1 & 0xFF) << 24) |
+            ((ip2 & 0xFF) << 16) |
+            ((ip3 & 0xFF) <<  8) |
+            ((ip4 & 0xFF)      ) );
+
+    return(1);
+}
+
+static int parse_interface(char *str, int *ip, short *udpport)
+{
+    char *ptr = strchr(str, ':');
+    if (ptr) /* portnum specified? */
+        *ptr = '\0';
+
+    if (!parse_ip(str, ip))
+        return(0);
+
+    *udpport = BUILD_DEFAULT_UDP_PORT;
+    if (ptr != NULL)  /* port specified? */
+    {
+        ptr++;
+        if (stricmp(ptr, "any") == 0)
+            *udpport = 0;
+        else
+            *udpport = (short) atoi(ptr);
+    }
+
+    return(1);
+}
+
+static int initialize_sockets(void)
+{
+#if PLATFORM_WIN32
+    int rc;
+    WSADATA data;
+    printf("initializing WinSock...\n");
+    rc = WSAStartup(0x0101, &data);
+    if (rc != 0)
+    {
+        printf("WinSock failed to initialize! [err==%d].\n", rc);
+        return(0);
+    }
+    else
+    {
+        printf("WinSock initialized.\n");
+        printf("  - Caller uses version %d.%d, highest supported is %d.%d.\n",
+                data.wVersion >> 8, data.wVersion & 0xFF,
+                data.wHighVersion >> 8, data.wHighVersion & 0xFF);
+        printf("  - Implementation description: [%s].\n", data.szDescription);
+        printf("  - System status: [%s].\n", data.szSystemStatus);
+        printf("  - Max sockets: %d.\n", data.iMaxSockets);
+        printf("  - Max UDP datagram size: %d.\n", data.iMaxUdpDg);
+    }
+#endif
+
+    return(1);
+}
+
+static void deinitialize_sockets(void)
+{
+#if PLATFORM_WIN32
+    WSACleanup();
+#endif
+}
+
+static int parse_udp_config(const char *cfgfile, gcomtype *gcom)
+{
+    char *buf;
+    char *tok;
+    char *ptr;
+    int ip = 0;  /* interface */
+    int bcast = 0;
+
+    buf = read_whole_file(cfgfile);  /* we must free this. */
+    if (buf == NULL)
+        return(0);
+
+    ptr = buf;
+    while ((tok = get_token(&ptr)) != NULL)
+    {
+        int bogus = 1;
+
+        if (stricmp(tok, "interface") == 0)
+        {
+            if ( (tok = get_token(&ptr)) &&
+                 (parse_interface(tok, &ip, &udpport)) )
+            {
+                bogus = 0;
+            }
+            printf("Interface %s:%d chosen.\n",
+                    static_ipstring(ip), (int) udpport);
+        }
+
+        else if (stricmp(tok, "mode") == 0)
+        {
+            if ((tok = get_token(&ptr)) != NULL)
+            {
+                bogus = 0;
+                if (stricmp(tok, "server") == 0)
+                    udpmode = udpmode_server;
+                else if (stricmp(tok, "client") == 0)
+                    udpmode = udpmode_client;
+                else if (stricmp(tok, "peer") == 0)
+                    udpmode = udpmode_peer;
+                else
+                    bogus = 1;
+
+                if (!bogus)
+                    printf("You want to be in [%s] mode\n", tok);
+            }
+        }
+
+        else if (stricmp(tok, "broadcast") == 0)
+        {
+            if ((tok = get_token(&ptr)) != NULL)
+            {
+                bcast = atoi(tok);
+                if (bcast > MAX_PLAYERS - 1)
+                {
+                    printf("WARNING: Too many broadcast players.\n");
+                    bcast = MAX_PLAYERS - 1;
+                }
+
+                bogus = 0;
+            }
+        }
+
+        else if (stricmp(tok, "allow") == 0)
+        {
+            int host;
+            short port=BUILD_DEFAULT_UDP_PORT;
+            if ((tok = get_token(&ptr)) != NULL)
+            {
+                if (gcom->numplayers >= MAX_PLAYERS - 1)
+                    printf("WARNING: Too many allowed IP addresses.\n");
+
+                else if (parse_interface(tok, &host, &port))
+                {
+                    allowed_addresses[gcom->numplayers].host = host;
+                    allowed_addresses[gcom->numplayers].port = port;
+                    gcom->numplayers++;
+                    bogus = 0;
+                }
+            }
+        }
+
+        if (bogus)
+            printf("bogus token! [%s]\n", tok);
+    }
+
+    free(buf);
+
+    if (open_udp_socket(ip, udpport))
+    {
+        gcom->numplayers++;  /* that's you. */
+        if (udpmode == udpmode_server)
+            return(wait_for_other_players(gcom, ip));
+        else if (udpmode == udpmode_client)
+            return(connect_to_server(gcom, ip));
+        else if (udpmode == udpmode_peer)
+            return(connect_to_everyone(gcom, ip, bcast));
+
+        printf("wtf?!");  /* Should be handled by a udpmode above... */
+        assert(0);
+    }
+
+    return(0);
+}
+
+
+gcomtype *init_network_transport(char **ARGV, int argpos)
+{
+    gcomtype *retval;
+
+    printf("UDP NETWORK TRANSPORT INITIALIZING...\n");
+
+    ctrlc_pressed = 0;
+
+    if (!initialize_sockets())
+        return(NULL);
+
+    retval = (gcomtype *)malloc(sizeof (gcomtype));
+    if (retval != NULL)
+    {
+        int rc;
+        char *cfgfile = ARGV[argpos];
+        void (*oldsigint)(int);
+
+        memset(retval, '\0', sizeof (gcomtype));
+        memset(allowed_addresses, '\0', sizeof (allowed_addresses));
+        udpsocket = -1;
+        udpport = BUILD_DEFAULT_UDP_PORT;
+        udpmode = udpmode_peer;
+
+        oldsigint = signal(SIGINT, siginthandler);
+        rc = parse_udp_config(cfgfile, retval);
+        signal(SIGINT, oldsigint);
+
+        if (!rc)
+        {
+            free(retval);
+            deinit_network_transport(NULL);
+            return(NULL);
+        }
+        retval->gametype = 3;  /* gametype: 1-serial,2-modem,3-net */
+    }
+
+    return(retval);
+}
+
+
+void deinit_network_transport(gcomtype *gcom)
+{
+    printf("UDP NETWORK TRANSPORT DEINITIALIZING...\n");
+
+    if (gcom != NULL)
+    {
+        printf("  ...freeing gcom structure...\n");
+        free(gcom);
+    }
+
+    if (udpsocket != -1)
+    {
+        printf("  ...closing socket...\n");
+        set_socket_blockmode(1);  /* block while socket drains. */
+        shutdown(udpsocket, SOCKET_SHUTDOWN_BOTH);
+        socketclose(udpsocket);
+        udpsocket = -1;
+    }
+
+    deinitialize_sockets();
+
+    printf("UDP net deinitialized successfully.\n");
+}
+
+
+void unstable_callcommit(void)
+{
+    int ip, i, rc;
+    short port;
+
+    if (udpsocket == -1)
+        return;
+
+    process_udp_send_queue();
+
+    switch (gcom->command)
+    {
+        case COMMIT_CMD_GET:
+            rc = get_udp_packet(&ip, &port, gcom->buffer, sizeof(gcom->buffer));
+            if (rc > 0)
+            {
+                gcom->numbytes = rc;  /* size of new packet. */
+                for (i = 1; i <= gcom->numplayers; i++)
+                {
+                    if ( (allowed_addresses[i].host == ip) &&
+                         (allowed_addresses[i].port == port) )
+                    {
+                        gcom->other = i;
+                        return;
+                    }
+                }
+                /* if you manage to hit this, it'll report no packet avail. */
+            }
+
+            gcom->numbytes = 0;
+            gcom->other = -1;  /* no packet available. */
+            break;
+
+        case COMMIT_CMD_SEND:
+            if ((gcom->other < 0) || (gcom->other > gcom->numplayers))
+            {
+                printf("NET TRANSPORT ERROR: send to player out of range\n");
+                return;
+            }
+
+            ip = allowed_addresses[gcom->other].host;
+            if (ip == 0)  /* dropped player? */
+                return;
+
+            port = allowed_addresses[gcom->other].port;
+
+            if (!send_udp_packet(ip, port, gcom->buffer, gcom->numbytes))
+            {
+                printf("NET TRANSPORT ERROR: send failed to %s:%d\n",
+                        static_ipstring(ip), (int) port);
+            }
+            break;
+
+        case COMMIT_CMD_SENDTOALL:
+            /* skip player zero, 'cause that's a duplicate of local IP. */
+            for (i = 1; i <= gcom->numplayers; i++)
+            {
+                ip = allowed_addresses[i].host;
+                if (ip == 0)  /* dropped player? */
+                    continue;
+
+                port = allowed_addresses[i].port;
+
+                if (!send_udp_packet(ip, port, gcom->buffer, gcom->numbytes))
+                {
+                    printf("NET TRANSPORT ERROR: send failed to %s:%d\n",
+                            static_ipstring(ip), (int) port);
+                }
+            }
+            break;
+
+        case COMMIT_CMD_SENDTOALLOTHERS:
+            /* skip player zero, 'cause that's a duplicate of local IP. */
+            for (i = 1; i <= gcom->numplayers; i++)
+            {
+                if (i == gcom->myconnectindex)  /* local player. */
+                    continue;
+
+                ip = allowed_addresses[i].host;
+                if (ip == 0)  /* dropped player? */
+                    continue;
+
+                port = allowed_addresses[i].port;
+
+                if (!send_udp_packet(ip, port, gcom->buffer, gcom->numbytes))
+                {
+                    printf("NET TRANSPORT ERROR: send failed to %s:%d\n",
+                            static_ipstring(ip), (int) port);
+                }
+            }
+            break;
+
+
+        case COMMIT_CMD_SCORE:
+			// FIX_00008: minor protocol error after frags (NET TRANSPORT ERROR: Unknown command 5)
+
+			// Leave it on, since the sendscore() function will trigger that case.
+			// This will avoid to fall in the default case on each frag and flood
+			// the DOS console with NET TRANSPORT ERROR: Unknown command error messages.
+			// sendscore() seems to be an obsolete function, and was probably used
+			// in an old netcode architecture that 3drealms forgot to remove.
+			// We leave sendscore() in the code anyway since it doesnt seem to break 
+			// anything. It's not needed to fix what's not broken...
+			gcom->other = -1;
+            break;
+
+        default:
+            printf("NET TRANSPORT ERROR: Unknown command %d\n", gcom->command);
+            gcom->other = -1;  /* oh well. */
+            break;
+    }
+}
+
+#else
+#error Please define a network transport for your platform.
+#endif
+
+/* end of mmulti.c ... */
+
--- /dev/null
+++ b/Engine/src/mmulti.cpp
@@ -1,0 +1,1213 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "enet.h"
+
+//#include "buildqueue.h"
+
+#include <vector>
+
+
+//#define _DEBUG_NETWORKING_
+
+extern "C"
+{
+
+#include "platform.h"
+#include "pragmas.h"
+#include "signal.h"
+#include "mmulti_stable.h"
+
+#define MAXPLAYERS 16
+#define BAKSIZ 16384
+#define SIMULATEERRORS 0
+#define SHOWSENDPACKETS 0
+#define SHOWGETPACKETS 0
+#define PRINTERRORS 0
+
+#define MAX_PLAYERS 16
+#define BUILD_DEFAULT_UDP_PORT 1635  /* eh...why not? */
+#define CLIENT_POLL_DELAY 5000  /* ms between pings at peer-to-peer startup. */
+#define HEADER_PEER_GREETING 245
+#define HEADER_PEER_READY 244
+
+static int udpsocket = -1;
+static short udpport = BUILD_DEFAULT_UDP_PORT;
+
+#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])
+
+static long incnt[MAXPLAYERS], outcntplc[MAXPLAYERS], outcntend[MAXPLAYERS];
+static char errorgotnum[MAXPLAYERS];
+static char errorfixnum[MAXPLAYERS];
+static char errorresendnum[MAXPLAYERS];
+#if (PRINTERRORS)
+	static char lasterrorgotnum[MAXPLAYERS];
+#endif
+
+long crctable[256];
+int tmpmax[8]; //addfaz variable addition (you could probs think of something better)
+unsigned int g_bAllPlayersFound = 0;
+
+static char lastpacket[576], inlastpacket = 0;
+static short lastpacketfrom, lastpacketleng;
+
+extern long totalclock;  /* MUST EXTERN 1 ANNOYING VARIABLE FROM GAME */
+static long timeoutcount = 60, resendagaincount = 4, lastsendtime[MAXPLAYERS];
+
+static short bakpacketptr[MAXPLAYERS][256], bakpacketlen[MAXPLAYERS][256];
+static char bakpacketbuf[BAKSIZ];
+static long bakpacketplc = 0;
+
+short myconnectindex, numplayers;
+short connecthead, connectpoint2[MAXPLAYERS];
+char syncstate = 0;
+
+unsigned char g_bWaitingForAllReady = 0;
+
+extern int _argc;
+extern char **_argv;
+
+#define MAXPACKETSIZE 2048
+typedef struct
+{
+	short intnum;                /* communication between Game and the driver */
+	short command;               /* 1-send, 2-get */
+	short other;                 /* dest for send, set by get (-1 = no packet) */
+	short numbytes;
+	short myconnectindex;
+	short numplayers;
+	short gametype;              /* gametype: 1-serial,2-modem,3-net */
+	short filler;
+	char buffer[MAXPACKETSIZE];
+	long longcalladdress;
+} gcomtype;
+static gcomtype *gcom;
+
+gcomtype g_LastPersonalPacket;
+
+typedef struct 
+{
+	unsigned short other;
+	unsigned int bufferSize;
+	unsigned char buffer[MAXPACKETSIZE];
+}PACKET;
+
+typedef std::vector<PACKET> PacketQueue;
+PacketQueue incommingPacketQueue;
+
+//typedef std::vector<PACKET> PacketQueue;
+
+enum ECommitCMDs
+{
+	COMMIT_CMD_SEND				= 1,
+	COMMIT_CMD_GET              = 2,
+	COMMIT_CMD_SENDTOALL        = 3,
+	COMMIT_CMD_SENDTOALLOTHERS  = 4,
+	COMMIT_CMD_SCORE            = 5,
+};
+
+typedef enum
+{
+    udpmode_peer,
+    udpmode_server,
+    udpmode_client
+} udpmodes;
+static udpmodes udpmode = udpmode_peer;
+
+enum EConnectionMode
+{
+	CONN_MODE_CONNECTING	= 0,
+	CONN_MODE_GREETING		= 1,
+	CONN_MODE_WAITFORREADY	= 2,
+	CONN_MODE_CONNECTED		= 3,
+	CONN_MODE_DISCONNECTED	= 4,
+};
+EConnectionMode g_ConnMode = CONN_MODE_CONNECTING;
+
+typedef struct {
+  unsigned int host;
+  short port;
+  unsigned short id;
+  unsigned short peer_idx;
+  unsigned short bHeardFrom;
+  unsigned char  bReady;
+} ADDRESS_STRUCT;
+ADDRESS_STRUCT allowed_addresses[MAX_PLAYERS];  /* only respond to these IPs. */
+
+volatile int ctrlc_pressed = 0;
+static void siginthandler(int sigint)
+{
+    ctrlc_pressed = 1;
+}
+
+typedef struct
+{
+    unsigned char dummy1;   /* so these don't confuse game after load. */
+    unsigned char dummy2;   /* so these don't confuse game after load. */
+    unsigned char dummy3;   /* so these don't confuse game after load. */
+    unsigned char header;   /* always HEADER_PEER_GREETING (245). */
+    unsigned short id;
+} PacketPeerGreeting;
+
+// unique id for determining "myconnectindex"
+unsigned short my_id = 0;
+
+unsigned short g_nPlayerIDList[MAX_PLAYERS];
+
+#define CONNECTION_DELAY 3000
+#define INITIAL_CONNECTION_DELAY 50
+#define INGAME_CONNECTION_DELAY 0
+#define POLL_DELAY 1000
+
+//ENetAddress		address;
+ENetHost*		g_Server = 0;
+//ENetEvent		event;
+ENetPeer*		g_Peers[MAX_PLAYERS];
+
+short *g_other;
+char *g_bufptr;
+short g_nMessageLen;
+unsigned char g_bAllGreetingsIn = 0;
+
+// Prototypes
+int CreateServer(char* ip, int nPort, int nMaxPlayers);
+
+char *static_ipstring(int ip);
+char *read_whole_file(const char *cfgfile);
+char *get_token(char **ptr);
+int parse_ip(const char *str, int *ip);
+int parse_interface(char *str, int *ip, short *udpport);
+int parse_udp_config(const char *cfgfile, gcomtype *gcom);
+
+int connect_to_everyone();
+void HandleEvent(ENetEvent *pEvent);
+unsigned int GetPeerIndex(ENetPeer* peer);
+unsigned int GetOtherIndex(ENetPeer* peer);
+void ServiceNetwork();
+void Send_Peer_Gretting();
+void Wait_For_Ready();
+
+void cleanup(void);
+
+
+	void stable_callcommit(void)
+	{
+	}
+	
+	void stable_initcrc(void)
+	{
+	}
+
+	// Get CRC
+	long stable_getcrc(char *buffer, short bufleng)
+	{
+		long i, j;
+
+		j = 0;
+		for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
+		return(j&65535);
+	}
+
+	void stable_initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
+	{
+		long i;
+		gcomtype *retval;
+		int k;
+
+		// DO NOT DO THIS - it screws up the networking
+		//srand(enet_time_get_raw());
+
+		for (i = _argc - 1; i > 0; i--)
+		{
+			const char *arg = _argv[i];
+			char ch = *arg;
+			if ((ch == '-') || (ch == '/'))
+			{
+				if (stricmp(arg + 1, "net") == 0)
+					break;
+			}
+		}
+
+		if ((i == 0) || (i+1 == _argc))
+		{
+			numplayers = 1; myconnectindex = 0;
+			connecthead = 0; connectpoint2[0] = -1;
+			return;
+		}
+
+		// Zero out the peers buffer
+		//memset(g_Peers, 0, MAX_PLAYERS);
+				
+		/*
+		for(k = 0; k < MAX_PLAYERS-1; ++k)
+		{
+			g_Peers[k] = NULL;
+		}
+		*/
+		
+		
+
+		if(enet_initialize() == -1)
+		{
+			printf("Error initializing ENet\n");
+		}
+
+		atexit(cleanup);
+
+		retval = (gcomtype *)malloc(sizeof (gcomtype));
+		if (retval != NULL)
+		{
+			int rc;
+			char *cfgfile = _argv[i+1];
+			void (*oldsigint)(int);
+
+			memset(retval, '\0', sizeof (gcomtype));
+			memset(allowed_addresses, '\0', sizeof (allowed_addresses));
+			udpsocket = -1;
+			udpport = BUILD_DEFAULT_UDP_PORT;
+			udpmode = udpmode_peer;
+
+			oldsigint = signal(SIGINT, siginthandler);
+			rc = parse_udp_config(cfgfile, retval);
+			signal(SIGINT, oldsigint);
+
+			if(!rc)
+			{
+				printf("Network transport initialization error!\n");
+			}
+
+			gcom = retval;
+		}
+		else
+		{
+				printf("Error allocating gcomtype!\n");
+		}
+
+		numplayers = gcom->numplayers;
+//		myconnectindex = gcom->myconnectindex;//numplayers-1;
+		#if (SIMULATEERRORS != 0)
+			srand(myconnectindex*24572457+345356);
+		#endif
+
+		//g_Peers = (ENetPeer**)malloc(sizeof(ENetPeer*) * gcom->numplayers);
+		connect_to_everyone();
+
+		connecthead = 0;
+		for(i=0;i<numplayers-1;i++)
+		{ 
+			connectpoint2[i] = i+1;
+		}
+		connectpoint2[numplayers-1] = -1;
+
+		for(i=0;i<numplayers;i++) 
+		{
+			lastsendtime[i] = totalclock;
+		}
+
+
+
+		// Set our connection index
+		myconnectindex = gcom->myconnectindex;//numplayers-1;
+
+	}
+
+	void stable_sendpacket(long other, char *bufptr, long messleng)
+	{
+
+		if(other == (myconnectindex))
+		{
+			#ifdef _DEBUG_NETWORKING_
+				printf("Send Packet to myself %d : type: %d len: %d\n", other, bufptr[0], messleng);
+			#endif
+			memcpy(g_LastPersonalPacket.buffer, bufptr, messleng);
+			g_LastPersonalPacket.numbytes = (short)messleng;
+			g_LastPersonalPacket.command = 1;
+		}
+		else
+		{
+
+			#ifdef _DEBUG_NETWORKING_
+				printf("Send Packet to peer %d : type: %d len: %d\n", other, bufptr[0], messleng);
+			#endif
+
+			ENetPacket * packet = enet_packet_create (bufptr, sizeof(char) * messleng, ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+			//enet_peer_send (g_Peers[other], 0, packet);
+			enet_peer_send (g_Peers[allowed_addresses[other].peer_idx], 0, packet);
+			enet_host_flush(g_Server);
+
+		}
+	}
+
+	void stable_setpackettimeout(long datimeoutcount, long daresendagaincount)
+	{
+		//NOT USED for anything other than '/f4'
+	}
+
+	void stable_uninitmultiplayers(void)
+	{
+		//kill networking
+
+		/*
+		if(g_Peers)
+		{
+			free(g_Peers);
+		}
+		*/
+		incommingPacketQueue.clear();
+
+		enet_deinitialize();
+	}
+
+	void cleanup(void)
+	{
+		stable_uninitmultiplayers();
+	}
+
+	void stable_sendlogon(void)
+	{
+	}
+
+	void stable_sendlogoff(void)
+	{
+		long i;
+		char tempbuf[2];
+
+		tempbuf[0] = 255;
+		tempbuf[1] = myconnectindex;
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if (i != myconnectindex)
+				stable_sendpacket(i,tempbuf,2L);
+	}
+
+	int stable_getoutputcirclesize(void)
+	{
+		return 0;
+	}
+
+	void stable_setsocket(short newsocket)
+	{
+	}
+
+
+//-------------------------------------------------
+//
+//	GetPacket
+//
+//-------------------------------------------------
+	short stable_getpacket(short *other, char *bufptr)
+	{	
+		ENetEvent event;
+		g_nMessageLen = 0;
+
+		
+		//clear out the early packet buffer first
+		if(incommingPacketQueue.size() > 0)
+		{
+			PacketQueue::iterator iter = incommingPacketQueue.begin();
+
+			if(iter != incommingPacketQueue.end() )
+			{
+				g_nMessageLen = (*iter).bufferSize;
+				*other = (*iter).other;
+				memcpy(bufptr , (*iter).buffer, g_nMessageLen);
+
+				// Delete this entry now that we're done with it.
+				incommingPacketQueue.erase(iter);
+			}
+		}
+		else			
+		if (enet_host_service (g_Server, & event, INGAME_CONNECTION_DELAY) > 0) 
+		{
+			// setup the pointers.
+			g_other = other;
+
+			HandleEvent(&event);
+
+			if(event.type == ENET_EVENT_TYPE_RECEIVE)
+			{
+				memcpy(bufptr, &lastpacket[0], g_nMessageLen);
+			}
+
+		}else // check to see if we have a packet of our own to deliver to ourselves.
+		{
+			if(g_LastPersonalPacket.command == 1)
+			{
+				*other = gcom->numplayers -1;//myconnectindex;
+				memcpy(bufptr, &g_LastPersonalPacket.buffer[0], g_nMessageLen);
+				
+				//reset it
+				g_LastPersonalPacket.command = 0;
+
+				return g_LastPersonalPacket.numbytes;
+			}
+		}
+
+		return g_nMessageLen;
+	}
+
+	void stable_flushpackets(void)
+	{
+		//STUB
+	}
+
+	void stable_genericmultifunction(long other, char *bufptr, long messleng, long command)
+	{
+
+	}
+
+//
+//
+//
+int connect_to_everyone()
+{
+	ENetAddress		address;
+    ENetEvent		event;
+	int i;
+	int bWaiting = 1;
+	int bCreatedPeers = 0;
+
+	while(bWaiting)
+	{
+		printf( (g_bAllPlayersFound) ? "." : "Waiting for connections...\n");
+
+		//wait for conencts to/from them
+		if (enet_host_service (g_Server, & event, (bCreatedPeers == 1) ? CONNECTION_DELAY : INITIAL_CONNECTION_DELAY) > 0) 
+		{
+			HandleEvent(&event);
+		}
+
+		//Create peer and connect to it
+		//enet_address_set_host (& address, m_szAddr);
+
+		if(bCreatedPeers == 0)
+		{
+			for(i = 0; i < gcom->numplayers-1; ++i)
+			{
+				ENetPeer *peer;
+				char szHostName[64];
+	
+	
+				address.host = allowed_addresses[i].host; //ip;
+				address.port = allowed_addresses[i].port; //m_nPort;
+
+	
+	
+				enet_address_get_host(&address, szHostName, 64);
+				printf("Creating peer: %s:%d\n", szHostName, address.port);
+	
+				g_Peers[i] = enet_host_connect (g_Server, & address, 2); 
+	
+				if(g_Peers[i] == NULL)
+				{
+					printf("Error creating peer! \n");
+					//return 1;
+				}else
+				{
+					allowed_addresses[i].peer_idx = i;
+				}
+
+			}
+				bCreatedPeers = 1;
+		}
+
+		if(g_bAllPlayersFound == 1)
+		{
+			bWaiting = 0;
+		}
+
+	}
+
+	printf("Negotiating connection order...\n");
+	Send_Peer_Gretting();
+	Wait_For_Ready();
+
+	return 0;
+}
+
+void Send_Peer_Gretting()
+{
+	int i;
+
+	g_ConnMode = CONN_MODE_GREETING;
+
+    while (my_id == 0)  /* player number is based on id, low to high. */
+	{
+		my_id = (unsigned short)enet_time_get_raw();//(unsigned short) rand();
+	}
+
+	printf("My client id is %d\n", my_id);
+
+	for(i = 0; i < MAX_PLAYERS; ++i)
+	{
+		allowed_addresses[i].id = 0;
+	}
+
+    PacketPeerGreeting greetpacket;
+	memset(&greetpacket, '\0', sizeof (greetpacket));
+    greetpacket.header = HEADER_PEER_GREETING;
+    greetpacket.id = BUILDSWAP_INTEL16(my_id);
+
+	// Create the greeting packet
+	ENetPacket * packet = enet_packet_create (&greetpacket, sizeof(PacketPeerGreeting), ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+
+	printf("Broadcasting Greating...\n");
+
+	// Broadcast it to all the peers
+	enet_host_broadcast(g_Server, 0, packet);
+
+	// Flush the send buffer
+	enet_host_flush(g_Server);
+
+	while(CONN_MODE_GREETING == g_ConnMode)
+	{
+		ENetEvent event;
+		if (enet_host_service (g_Server, & event, CONNECTION_DELAY) > 0) 
+		{
+			int nAllIDsIn = 1;
+
+			
+			HandleEvent(&event);	
+			
+			// are all the id's in yet?
+			for(i = 0; i < (gcom->numplayers-1); ++i)
+			{
+				if(allowed_addresses[i].id == 0)
+				{
+					nAllIDsIn = 0;
+					break;
+				}
+			}
+
+			// add our ID to the list for sorting
+			allowed_addresses[gcom->numplayers-1].id = my_id;
+
+			//check the validity of the ID and sort them.
+			if(nAllIDsIn == 1)
+			{
+				int iteration = 0;
+				int k = 0;
+				unsigned short nCurrentHigh = 0;
+
+				printf("Sorting player IDs...\n");
+
+				for(iteration = 0; iteration < gcom->numplayers; ++iteration)
+				{					
+					//g_nPlayerIDList[i]
+					for(k = iteration+1; k < gcom->numplayers; ++k)
+					{
+						if(allowed_addresses[iteration].id == allowed_addresses[k].id)
+						{
+							printf("ERROR!!!!! Two players with the same Unique ID found, please restart...\n");
+						}
+						else
+						{
+							// if it's valid, then goto sort it
+							if(allowed_addresses[k].id > allowed_addresses[iteration].id)
+							{
+								//swap'm
+								//unsigned short nTemp = allowed_addresses[iteration];
+								ADDRESS_STRUCT tempAddress;
+
+								// Swap the positions
+								memcpy(&tempAddress, &allowed_addresses[iteration], sizeof(ADDRESS_STRUCT));								
+								memcpy(&allowed_addresses[iteration], &allowed_addresses[k], sizeof(ADDRESS_STRUCT));
+								memcpy(&allowed_addresses[k], &tempAddress, sizeof(ADDRESS_STRUCT));
+							}
+						}
+					}
+					
+				}
+
+				// Find our slot	
+				printf("Finding our player index...\n");
+
+				for(i = 0; i < (gcom->numplayers); ++i)
+				{	
+					printf("Index[%d] = %d\n", i, allowed_addresses[i].id);
+
+
+					if(allowed_addresses[i].id == my_id)
+					{
+						gcom->myconnectindex = i;
+						printf("You are player #%d\n", i);
+
+						// We're all greated, switch to waiting for all ready
+						g_ConnMode = CONN_MODE_WAITFORREADY;
+						break;
+					}
+					
+				}		
+			}
+		}
+	}
+	
+}
+
+void Wait_For_Ready()
+{
+
+	g_bWaitingForAllReady = gcom->numplayers-1;
+
+	// Create the greeting packet
+	unsigned char message = HEADER_PEER_READY;
+
+	ENetPacket * packet = enet_packet_create (&message, sizeof(unsigned char), ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+
+	printf("Broadcasting Ready Packet...\n");
+
+	// Broadcast it to all the peers
+	enet_host_broadcast(g_Server, 0, packet);
+
+	// Flush the send buffer
+	enet_host_flush(g_Server);
+
+	//g_ConnMode = CONN_MODE_CONNECTED;//CONN_MODE_WAITFORREADY;
+
+	while(g_bWaitingForAllReady > 0)
+	{
+		ENetEvent event;
+		if (enet_host_service (g_Server, & event, CONNECTION_DELAY) > 0) 
+		{
+			int i;
+			
+			HandleEvent(&event);
+
+			g_bWaitingForAllReady = gcom->numplayers-1;
+			for(i = 0; i < gcom->numplayers; ++i)
+			{
+				if(allowed_addresses[i].bReady == 1)
+				{
+					--g_bWaitingForAllReady;
+				}
+			}
+
+			// Check to make sure we didn't subtract 1 from 0 to make 255. (unsigned char)
+			if(g_bWaitingForAllReady > gcom->numplayers)
+			{
+				printf("Error: we have a problem with the waiting for ready packets...\n");
+			}
+
+		}
+	}
+
+	printf("All players are ready. Start sending game data...\n");
+
+	g_ConnMode = CONN_MODE_CONNECTED;
+}
+
+void HandleEvent(ENetEvent *pEvent)
+{
+	switch(pEvent->type)
+			{
+			case ENET_EVENT_TYPE_CONNECT:
+				{
+					//bServerConnected = true;
+					ENetAddress address;
+					int i;
+
+					address.host = pEvent->peer->address.host; //ip;
+					address.port = pEvent->peer->address.port; //m_nPort;
+					char szHostName[64];
+					enet_address_get_host(&address, szHostName, 64);
+					
+					printf("Connection Established with: (%s)\n", szHostName);
+
+					for(i = 0; i < gcom->numplayers-1; ++i)
+					{
+						if(allowed_addresses[i].host == address.host)
+						{
+							allowed_addresses[i].bHeardFrom = 1;
+						}
+					}
+
+					for(i = 0; i < gcom->numplayers-1; ++i)
+					{
+						if(allowed_addresses[i].bHeardFrom == 0)
+						{
+							return;
+						}
+					}
+
+					// All players have been found... YAY!
+					g_bAllPlayersFound = 1;
+					printf("All Players Connected...\n");
+
+				}
+				break;
+			case ENET_EVENT_TYPE_RECEIVE:
+				{
+						g_nMessageLen = (short)pEvent->packet->dataLength;
+
+
+						
+						switch(g_ConnMode)
+						{
+
+						case CONN_MODE_GREETING:
+							{
+								PacketPeerGreeting packet;
+								unsigned int nPeerIndex;
+								int i;
+
+								if(pEvent->packet->data[0] != HEADER_PEER_GREETING)
+								{
+									printf("Invalid greeting!!!!\n");
+								}
+
+								printf("Received greeting from (%x)...\n", pEvent->peer->address.host);
+
+								memcpy(&packet, pEvent->packet->data, g_nMessageLen);
+								if(packet.header == HEADER_PEER_GREETING)
+								{
+									// Find the peer's index
+									nPeerIndex = GetPeerIndex(pEvent->peer);
+		
+									// Set the id for the peer
+									//g_nPlayerIDList[nPeerIndex] = packet.id;
+									allowed_addresses[nPeerIndex].id = packet.id;
+
+								}
+							}
+							break;
+						case CONN_MODE_WAITFORREADY:
+							{
+								if(pEvent->packet->data[0] == HEADER_PEER_READY)
+								{
+									allowed_addresses[GetOtherIndex(pEvent->peer)].bReady = 1;
+								}
+								else
+								{
+									printf("Invalid READY packet!!!\n");
+								}
+									
+
+							}
+							break;
+						case CONN_MODE_CONNECTED:
+						default:
+							{
+								if(g_ConnMode != CONN_MODE_CONNECTED)
+								{
+									PACKET packet;
+									packet.other = GetOtherIndex(pEvent->peer);
+									packet.bufferSize = g_nMessageLen;
+									memcpy(packet.buffer, pEvent->packet->data, g_nMessageLen);
+									incommingPacketQueue.push_back(packet);
+									printf("Saving early packet...\n");
+									break;
+								}
+
+								if(pEvent->packet->data[0] == 16)
+								{
+									printf("PACKET 16: len:[%d]\n", g_nMessageLen);
+								}
+
+								#ifdef _DEBUG_NETWORKING_LEVEL2_
+									printf("RECEIVE: type[%d] len:[%d]\n", pEvent->packet->data[0], g_nMessageLen);
+								#endif						
+								memcpy(&lastpacket[0], pEvent->packet->data, g_nMessageLen);
+								*g_other = GetOtherIndex(pEvent->peer);
+							}
+							break;
+						}
+						
+						/*if(g_nMessageLen > 0)
+						{
+							switch(pEvent->packet->data[0])
+							{
+								case HEADER_PEER_GREETING:
+								{
+									PacketPeerGreeting packet;
+									unsigned int nPeerIndex;
+									int i;
+
+									printf("Received greeting from (%x)...\n", pEvent->peer->address.host);
+
+									memcpy(&packet, pEvent->packet->data, g_nMessageLen);
+									if(packet.header == HEADER_PEER_GREETING)
+									{
+										// Find the peer's index in the g_Peers[] array
+										nPeerIndex = GetPeerIndex(pEvent->peer);
+			
+										// Set the id for the peer
+										//g_nPlayerIDList[nPeerIndex] = packet.id;
+										allowed_addresses[nPeerIndex].id = packet.id;
+
+									}
+								}
+								break;
+								case HEADER_PEER_READY:
+								{
+									unsigned int nOtherIndex;
+
+									printf("Received ready from (%x)...\n", pEvent->peer->address.host);
+									nOtherIndex = GetOtherIndex(pEvent->peer);
+
+									allowed_addresses[nOtherIndex].bReady = 1;
+
+								}
+								break;
+
+
+								default:
+									{
+										
+										if((g_bWaitingForAllReady) || (g_ConnMode == CONN_MODE_GREETING))
+										{
+											PACKET packet;
+											packet.other = GetOtherIndex(pEvent->peer);
+											packet.bufferSize = g_nMessageLen;
+											memcpy(packet.buffer, pEvent->packet->data, g_nMessageLen);
+											incommingPacketQueue.push_back(packet);
+											printf("Saving early packet...\n");
+											break;
+										}
+										
+
+										#ifdef _DEBUG_NETWORKING_LEVEL2_
+											printf("RECEIVE: type[%d] len:[%d]\n", pEvent->packet->data[0], g_nMessageLen);
+										#endif						
+										memcpy(&lastpacket[0], pEvent->packet->data, g_nMessageLen);
+
+										// find the correct index in the allowed_addresses[] array.
+										*g_other = GetOtherIndex(pEvent->peer);
+									}
+									break;
+							}
+						}
+						else
+						{
+							printf("Error: we received a Zero length packet!\n");
+						}*/
+
+						// Destroy the packet now that we're done with it.
+						enet_packet_destroy (pEvent->packet);
+				}
+				break;
+			case ENET_EVENT_TYPE_DISCONNECT:
+				{
+					printf("DISCONNECT: someone left!\n");
+				}
+				break;
+			default:
+				{
+					printf("Error: unknown event! : %d\n", pEvent->type);
+				}
+				break;
+			}
+}
+
+unsigned int GetPeerIndex(ENetPeer* peer)
+{
+	int i;
+	for(i = 0; i < gcom->numplayers; ++i)
+	{
+		if(peer->address.host == allowed_addresses[i].host)
+		{
+			return allowed_addresses[i].peer_idx;
+			//return i;
+		}
+	}
+
+	printf("Error: GetPeerIndex failed to find the corrent index!\n");
+	return 0;
+}
+
+unsigned int GetOtherIndex(ENetPeer* peer)
+{
+	int i;
+	for(i = 0; i < gcom->numplayers; ++i)
+	{
+		if(peer->address.host == allowed_addresses[i].host)
+		{
+			return i;//allowed_addresses[i].peer_idx;
+			//return i;
+		}
+	}
+
+	printf("Error: GetOtherIndex failed to find the corrent index!\n");
+	return 0;
+}
+
+void ServiceNetwork()
+{
+	ENetEvent event;
+	if (enet_host_service (g_Server, & event, INGAME_CONNECTION_DELAY) > 0) 
+	{
+		HandleEvent(&event);
+	}
+}
+
+//**************************************************************
+//* Network Transport Functions
+//**************************************************************
+int CreateServer(char* ip, int nPort, int nMaxPlayers)
+{
+
+	ENetAddress address;
+
+	printf("Creating server of %d players on port %d.\n", nMaxPlayers, nPort);
+
+    /* Bind the server to the default localhost.
+     * A specific host address can be specified by
+     * enet_address_set_host (& address, "x.x.x.x");
+     */
+    address.host = enet_address_set_host (& address, ip);//nIp;//ENET_HOST_ANY;
+    /* Bind the server to port 1234. */
+    address.port = nPort;
+
+    g_Server = enet_host_create (& address /* the address to bind the server host to */, 
+                nMaxPlayers /* allow up to 32 clients and/or outgoing connections */,
+                0 /* assume any amount of incoming bandwidth */,
+                0 /* assume any amount of outgoing bandwidth */);
+   
+	if (g_Server == NULL)
+    {
+		printf("Error creating server!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+//**************************************************************
+//* Network Config File Functions
+//**************************************************************
+
+#include "cache1d.h"  /* kopen4load for cfg file. */
+#include "display.h"  /* getticks */
+
+#define IPSEG1(ip) ((((unsigned int) ip) & 0xFF000000) >> 24)
+#define IPSEG2(ip) ((((unsigned int) ip) & 0x00FF0000) >> 16)
+#define IPSEG3(ip) ((((unsigned int) ip) & 0x0000FF00) >>  8)
+#define IPSEG4(ip) ((((unsigned int) ip) & 0x000000FF)      )
+
+	char *static_ipstring(int ip)
+	{
+		static char s[16];
+		sprintf(s, "%u.%u.%u.%u", IPSEG1(ip), IPSEG2(ip), IPSEG3(ip), IPSEG4(ip));
+		return(s);
+	}
+
+	char *read_whole_file(const char *cfgfile)
+	{
+		char *buf;
+		long len, rc;
+		long handle;
+
+		if (cfgfile == NULL)
+			return(NULL);
+
+		handle = kopen4load(cfgfile, 0);
+		if (handle == -1)
+		{
+			printf("ERROR: Failed to open config file [%s].\n", cfgfile);
+			return(NULL);
+		}
+
+		len = kfilelength(handle);
+		buf = (char *) malloc(len + 2);
+		if (!buf)
+		{
+			kclose(handle);
+			return(NULL);
+		}
+
+		rc = kread(handle, buf, len);
+		kclose(handle);
+		if (rc != len)
+		{
+			free(buf);
+			return(NULL);
+		}
+
+		buf[len] = '\0';
+		buf[len+1] = '\0';
+		return(buf);
+	}
+
+	char *get_token(char **ptr)
+	{
+		char *retval;
+		char *p = *ptr;
+		if (*p == '\0')
+			return(NULL);
+
+		while ((*p != '\0') && (isspace(*p)))
+			p++;
+
+		if (*p == '\0')  /* nothing but whitespace. */
+			return(NULL);
+
+		retval = p;
+		while ((*p != '\0') && (!isspace(*p)))
+			p++;
+
+		*p = '\0';
+		*ptr = p + 1;
+
+		/*printf("Got token [%s].\n", retval);*/
+		return(retval);
+	}
+
+	int parse_ip(const char *str, int *ip)
+	{
+		int ip1, ip2, ip3, ip4;
+
+		if (stricmp(str, "any") == 0)
+		{
+			*ip = 0;
+			return(1);
+		}
+
+		if (sscanf(str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4)
+		{
+			printf("\"%s\" is not a valid IP address.\n", str);
+			return(0);
+		}
+
+		/* we _should_ check that 0 <= ip? <= 255, but it'll fail later anyhow. */
+
+		*ip = ( ((ip1 & 0xFF) << 24) |
+				((ip2 & 0xFF) << 16) |
+				((ip3 & 0xFF) <<  8) |
+				((ip4 & 0xFF)      ) );
+
+		return(1);
+	}
+
+	int parse_interface(char *str, int *ip, short *udpport)
+	{
+		char *ptr = strchr(str, ':');
+		if (ptr) /* portnum specified? */
+			*ptr = '\0';
+
+		if (!parse_ip(str, ip))
+			return(0);
+
+		*udpport = BUILD_DEFAULT_UDP_PORT;
+		if (ptr != NULL)  /* port specified? */
+		{
+			ptr++;
+			if (stricmp(ptr, "any") == 0)
+				*udpport = 0;
+			else
+				*udpport = (short) atoi(ptr);
+		}
+
+		return(1);
+	}
+
+	int parse_udp_config(const char *cfgfile, gcomtype *gcom)
+	{
+		char *buf;
+		char *tok;
+		char *ptr;
+		int ip = 0;  /* interface */
+		int bcast = 0;
+
+		buf = read_whole_file(cfgfile);  /* we must free this. */
+		if (buf == NULL)
+			return(0);
+
+		ptr = buf;
+		while ((tok = get_token(&ptr)) != NULL)
+		{
+			int bogus = 1;
+
+			if (stricmp(tok, "interface") == 0)
+			{
+				if ( (tok = get_token(&ptr)) &&
+					 (parse_interface(tok, &ip, &udpport)) )
+				{
+					bogus = 0;
+				}
+				printf("Interface %s:%d chosen.\n",
+						static_ipstring(ip), (int) udpport);
+			}
+
+			else if (stricmp(tok, "mode") == 0)
+			{
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					bogus = 0;
+					if (stricmp(tok, "server") == 0)
+						udpmode = udpmode_server;
+					else if (stricmp(tok, "client") == 0)
+						udpmode = udpmode_client;
+					else if (stricmp(tok, "peer") == 0)
+						udpmode = udpmode_peer;
+					else
+						bogus = 1;
+
+					if (!bogus)
+						printf("You want to be in [%s] mode\n", tok);
+				}
+			}
+
+			else if (stricmp(tok, "broadcast") == 0)
+			{
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					bcast = atoi(tok);
+					if (bcast > MAX_PLAYERS - 1)
+					{
+						printf("WARNING: Too many broadcast players.\n");
+						bcast = MAX_PLAYERS - 1;
+					}
+
+					bogus = 0;
+				}
+			}
+
+			else if (stricmp(tok, "allow") == 0)
+			{
+				int host;
+				short port=BUILD_DEFAULT_UDP_PORT;
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					if (gcom->numplayers >= MAX_PLAYERS - 1)
+						printf("WARNING: Too many allowed IP addresses.\n");
+
+					else if (parse_interface(tok, &host, &port))
+					{
+						ENetAddress address;
+						enet_address_set_host(&address, static_ipstring(host));
+						printf("Adding: %s:%d to the list of allowed addresses.\n", static_ipstring(host), port);
+						allowed_addresses[gcom->numplayers].host = address.host;
+						allowed_addresses[gcom->numplayers].port = port;
+						gcom->numplayers++;
+						bogus = 0;
+					}
+				}
+			}
+
+			if (bogus)
+				printf("bogus token! [%s]\n", tok);
+		}
+
+		free(buf);
+
+		// Create the server
+		int ret = CreateServer(static_ipstring(ip), udpport, gcom->numplayers);
+		gcom->numplayers++; //that's you
+
+		if(ret == 0)
+		{
+			return 1;
+		}
+
+		return(0);
+	}
+
+
+
+
+} // end extern "C"
+
+/* end of mmulti.cpp ... */
+
+
--- /dev/null
+++ b/Engine/src/mmulti_stable.cpp
@@ -1,0 +1,1213 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "enet.h"
+
+//#include "buildqueue.h"
+
+#include <vector>
+
+
+//#define _DEBUG_NETWORKING_
+extern "C"
+{
+
+#include "platform.h"
+#include "pragmas.h"
+#include "signal.h"
+#include "mmulti_stable.h"
+
+#define MAXPLAYERS 16
+#define BAKSIZ 16384
+#define SIMULATEERRORS 0
+#define SHOWSENDPACKETS 0
+#define SHOWGETPACKETS 0
+#define PRINTERRORS 0
+
+#define MAX_PLAYERS 16
+#define BUILD_DEFAULT_UDP_PORT 1635  /* eh...why not? */
+#define CLIENT_POLL_DELAY 5000  /* ms between pings at peer-to-peer startup. */
+#define HEADER_PEER_GREETING 245
+#define HEADER_PEER_READY 244
+
+static int udpsocket = -1;
+static short udpport = BUILD_DEFAULT_UDP_PORT;
+
+#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])
+
+static long incnt[MAXPLAYERS], outcntplc[MAXPLAYERS], outcntend[MAXPLAYERS];
+static char errorgotnum[MAXPLAYERS];
+static char errorfixnum[MAXPLAYERS];
+static char errorresendnum[MAXPLAYERS];
+#if (PRINTERRORS)
+	static char lasterrorgotnum[MAXPLAYERS];
+#endif
+
+long crctable[256];
+int tmpmax[8]; //addfaz variable addition (you could probs think of something better)
+unsigned int g_bAllPlayersFound = 0;
+
+static char lastpacket[576], inlastpacket = 0;
+static short lastpacketfrom, lastpacketleng;
+
+extern long totalclock;  /* MUST EXTERN 1 ANNOYING VARIABLE FROM GAME */
+static long timeoutcount = 60, resendagaincount = 4, lastsendtime[MAXPLAYERS];
+
+static short bakpacketptr[MAXPLAYERS][256], bakpacketlen[MAXPLAYERS][256];
+static char bakpacketbuf[BAKSIZ];
+static long bakpacketplc = 0;
+
+short myconnectindex, numplayers;
+short connecthead, connectpoint2[MAXPLAYERS];
+//char syncstate = 0;
+
+unsigned char g_bWaitingForAllReady = 0;
+
+extern int _argc;
+extern char **_argv;
+
+#define MAXPACKETSIZE 2048
+typedef struct
+{
+	short intnum;                /* communication between Game and the driver */
+	short command;               /* 1-send, 2-get */
+	short other;                 /* dest for send, set by get (-1 = no packet) */
+	short numbytes;
+	short myconnectindex;
+	short numplayers;
+	short gametype;              /* gametype: 1-serial,2-modem,3-net */
+	short filler;
+	char buffer[MAXPACKETSIZE];
+	long longcalladdress;
+} gcomtype;
+static gcomtype *gcom;
+
+gcomtype g_LastPersonalPacket;
+
+typedef struct 
+{
+	unsigned short other;
+	unsigned int bufferSize;
+	unsigned char buffer[MAXPACKETSIZE];
+}PACKET;
+
+typedef std::vector<PACKET> PacketQueue;
+PacketQueue incommingPacketQueue;
+
+//typedef std::vector<PACKET> PacketQueue;
+
+enum ECommitCMDs
+{
+	COMMIT_CMD_SEND				= 1,
+	COMMIT_CMD_GET              = 2,
+	COMMIT_CMD_SENDTOALL        = 3,
+	COMMIT_CMD_SENDTOALLOTHERS  = 4,
+	COMMIT_CMD_SCORE            = 5,
+};
+
+typedef enum
+{
+    udpmode_peer,
+    udpmode_server,
+    udpmode_client
+} udpmodes;
+static udpmodes udpmode = udpmode_peer;
+
+enum EConnectionMode
+{
+	CONN_MODE_CONNECTING	= 0,
+	CONN_MODE_GREETING		= 1,
+	CONN_MODE_WAITFORREADY	= 2,
+	CONN_MODE_CONNECTED		= 3,
+	CONN_MODE_DISCONNECTED	= 4,
+};
+EConnectionMode g_ConnMode = CONN_MODE_CONNECTING;
+
+typedef struct {
+  unsigned int host;
+  short port;
+  unsigned short id;
+  unsigned short peer_idx;
+  unsigned short bHeardFrom;
+  unsigned char  bReady;
+} ADDRESS_STRUCT;
+ADDRESS_STRUCT	allowed_addresses[MAX_PLAYERS];  /* only respond to these IPs. */
+
+
+volatile int sb_ctrlc_pressed = 0;
+static void sb_siginthandler(int sigint)
+{
+    sb_ctrlc_pressed = 1;
+}
+
+typedef struct
+{
+    unsigned char dummy1;   /* so these don't confuse game after load. */
+    unsigned char dummy2;   /* so these don't confuse game after load. */
+    unsigned char dummy3;   /* so these don't confuse game after load. */
+    unsigned char header;   /* always HEADER_PEER_GREETING (245). */
+    unsigned short id;
+} PacketPeerGreeting;
+
+// unique id for determining "myconnectindex"
+unsigned short my_id = 0;
+
+unsigned short g_nPlayerIDList[MAX_PLAYERS];
+
+#define CONNECTION_DELAY 3000
+#define INITIAL_CONNECTION_DELAY 50
+#define INGAME_CONNECTION_DELAY 0
+#define POLL_DELAY 1000
+
+//ENetAddress		address;
+ENetHost*		g_Server = 0;
+//ENetEvent		event;
+ENetPeer*		g_Peers[MAX_PLAYERS];
+
+short *g_other;
+char *g_bufptr;
+short g_nMessageLen;
+unsigned char g_bAllGreetingsIn = 0;
+
+// Prototypes
+int CreateServer(char* ip, int nPort, int nMaxPlayers);
+
+char *static_ipstring(int ip);
+char *read_whole_file(const char *cfgfile);
+char *get_token(char **ptr);
+int parse_ip(const char *str, int *ip);
+int parse_interface(char *str, int *ip, short *udpport);
+int parse_udp_config(const char *cfgfile, gcomtype *gcom);
+
+int connect_to_everyone();
+void HandleEvent(ENetEvent *pEvent);
+unsigned int GetPeerIndex(ENetPeer* peer);
+unsigned int GetOtherIndex(ENetPeer* peer);
+void ServiceNetwork();
+void Send_Peer_Gretting();
+void Wait_For_Ready();
+
+void cleanup(void);
+
+
+	void stable_callcommit(void)
+	{
+	}
+	
+	void stable_initcrc(void)
+	{
+	}
+
+	// Get CRC
+	long stable_getcrc(char *buffer, short bufleng)
+	{
+		long i, j;
+
+		j = 0;
+		for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
+		return(j&65535);
+	}
+
+	void stable_initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
+	{
+		long i;
+		gcomtype *retval;
+		int k;
+
+		// DO NOT DO THIS - it screws up the networking
+		//srand(enet_time_get_raw());
+
+		for (i = _argc - 1; i > 0; i--)
+		{
+			const char *arg = _argv[i];
+			char ch = *arg;
+			if ((ch == '-') || (ch == '/'))
+			{
+				if (stricmp(arg + 1, "net") == 0)
+					break;
+			}
+		}
+
+		if ((i == 0) || (i+1 == _argc))
+		{
+			numplayers = 1; myconnectindex = 0;
+			connecthead = 0; connectpoint2[0] = -1;
+			return;
+		}
+
+		// Zero out the peers buffer
+		//memset(g_Peers, 0, MAX_PLAYERS);
+				
+		/*
+		for(k = 0; k < MAX_PLAYERS-1; ++k)
+		{
+			g_Peers[k] = NULL;
+		}
+		*/
+		
+		
+
+		if(enet_initialize() == -1)
+		{
+			printf("Error initializing ENet\n");
+		}
+
+		atexit(cleanup);
+
+		retval = (gcomtype *)malloc(sizeof (gcomtype));
+		if (retval != NULL)
+		{
+			int rc;
+			char *cfgfile = _argv[i+1];
+			void (*oldsigint)(int);
+
+			memset(retval, '\0', sizeof (gcomtype));
+			memset(allowed_addresses, '\0', sizeof (allowed_addresses));
+			udpsocket = -1;
+			udpport = BUILD_DEFAULT_UDP_PORT;
+			udpmode = udpmode_peer;
+
+			oldsigint = signal(SIGINT, sb_siginthandler);
+			rc = parse_udp_config(cfgfile, retval);
+			signal(SIGINT, oldsigint);
+
+			if(!rc)
+			{
+				printf("Network transport initialization error!\n");
+			}
+
+			gcom = retval;
+		}
+		else
+		{
+				printf("Error allocating gcomtype!\n");
+		}
+
+		numplayers = gcom->numplayers;
+//		myconnectindex = gcom->myconnectindex;//numplayers-1;
+		#if (SIMULATEERRORS != 0)
+			srand(myconnectindex*24572457+345356);
+		#endif
+
+		//g_Peers = (ENetPeer**)malloc(sizeof(ENetPeer*) * gcom->numplayers);
+		connect_to_everyone();
+
+		connecthead = 0;
+		for(i=0;i<numplayers-1;i++)
+		{ 
+			connectpoint2[i] = i+1;
+		}
+		connectpoint2[numplayers-1] = -1;
+
+		for(i=0;i<numplayers;i++) 
+		{
+			lastsendtime[i] = totalclock;
+		}
+
+
+
+		// Set our connection index
+		myconnectindex = gcom->myconnectindex;//numplayers-1;
+
+	}
+
+	void stable_sendpacket(long other, char *bufptr, long messleng)
+	{
+
+		if(other == (myconnectindex))
+		{
+			#ifdef _DEBUG_NETWORKING_
+				printf("Send Packet to myself %d : type: %d len: %d\n", other, bufptr[0], messleng);
+			#endif
+			memcpy(g_LastPersonalPacket.buffer, bufptr, messleng);
+			g_LastPersonalPacket.numbytes = (short)messleng;
+			g_LastPersonalPacket.command = 1;
+		}
+		else
+		{
+
+			#ifdef _DEBUG_NETWORKING_
+				printf("Send Packet to peer %d : type: %d len: %d\n", other, bufptr[0], messleng);
+			#endif
+
+			ENetPacket * packet = enet_packet_create (bufptr, sizeof(char) * messleng, ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+			//enet_peer_send (g_Peers[other], 0, packet);
+			enet_peer_send (g_Peers[allowed_addresses[other].peer_idx], 0, packet);
+			enet_host_flush(g_Server);
+
+		}
+	}
+
+	void stable_setpackettimeout(long datimeoutcount, long daresendagaincount)
+	{
+		//NOT USED for anything other than '/f4'
+	}
+
+	void stable_uninitmultiplayers(void)
+	{
+		//kill networking
+
+		/*
+		if(g_Peers)
+		{
+			free(g_Peers);
+		}
+		*/
+		incommingPacketQueue.clear();
+
+		enet_deinitialize();
+	}
+
+	void cleanup(void)
+	{
+		stable_uninitmultiplayers();
+	}
+
+	void stable_sendlogon(void)
+	{
+	}
+
+	void stable_sendlogoff(void)
+	{
+		long i;
+		char tempbuf[2];
+
+		tempbuf[0] = 255;
+		tempbuf[1] = myconnectindex;
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if (i != myconnectindex)
+				stable_sendpacket(i,tempbuf,2L);
+	}
+
+	int stable_getoutputcirclesize(void)
+	{
+		return 0;
+	}
+
+	void stable_setsocket(short newsocket)
+	{
+	}
+
+
+//-------------------------------------------------
+//
+//	GetPacket
+//
+//-------------------------------------------------
+	short stable_getpacket(short *other, char *bufptr)
+	{	
+		ENetEvent event;
+		g_nMessageLen = 0;
+
+		
+		//clear out the early packet buffer first
+		if(incommingPacketQueue.size() > 0)
+		{
+			PacketQueue::iterator iter = incommingPacketQueue.begin();
+
+			if(iter != incommingPacketQueue.end() )
+			{
+				g_nMessageLen = (*iter).bufferSize;
+				*other = (*iter).other;
+				memcpy(bufptr , (*iter).buffer, g_nMessageLen);
+
+				// Delete this entry now that we're done with it.
+				incommingPacketQueue.erase(iter);
+			}
+		}
+		else			
+		if (enet_host_service (g_Server, & event, INGAME_CONNECTION_DELAY) > 0) 
+		{
+			// setup the pointers.
+			g_other = other;
+
+			HandleEvent(&event);
+
+			if(event.type == ENET_EVENT_TYPE_RECEIVE)
+			{
+				memcpy(bufptr, &lastpacket[0], g_nMessageLen);
+			}
+
+		}else // check to see if we have a packet of our own to deliver to ourselves.
+		{
+			if(g_LastPersonalPacket.command == 1)
+			{
+				*other = gcom->numplayers -1;//myconnectindex;
+				memcpy(bufptr, &g_LastPersonalPacket.buffer[0], g_nMessageLen);
+				
+				//reset it
+				g_LastPersonalPacket.command = 0;
+
+				return g_LastPersonalPacket.numbytes;
+			}
+		}
+
+		return g_nMessageLen;
+	}
+
+	void stable_flushpackets(void)
+	{
+		//STUB
+	}
+
+	void stable_genericmultifunction(long other, char *bufptr, long messleng, long command)
+	{
+
+	}
+
+//
+//
+//
+int connect_to_everyone()
+{
+	ENetAddress		address;
+    ENetEvent		event;
+	int i;
+	int bWaiting = 1;
+	int bCreatedPeers = 0;
+
+	while(bWaiting)
+	{
+		printf( (g_bAllPlayersFound) ? "." : "Waiting for connections...\n");
+
+		//wait for conencts to/from them
+		if (enet_host_service (g_Server, & event, (bCreatedPeers == 1) ? CONNECTION_DELAY : INITIAL_CONNECTION_DELAY) > 0) 
+		{
+			HandleEvent(&event);
+		}
+
+		//Create peer and connect to it
+		//enet_address_set_host (& address, m_szAddr);
+
+		if(bCreatedPeers == 0)
+		{
+			for(i = 0; i < gcom->numplayers-1; ++i)
+			{
+				ENetPeer *peer;
+				char szHostName[64];
+	
+	
+				address.host = allowed_addresses[i].host; //ip;
+				address.port = allowed_addresses[i].port; //m_nPort;
+
+	
+	
+				enet_address_get_host(&address, szHostName, 64);
+				printf("Creating peer: %s:%d\n", szHostName, address.port);
+	
+				g_Peers[i] = enet_host_connect (g_Server, & address, 2); 
+	
+				if(g_Peers[i] == NULL)
+				{
+					printf("Error creating peer! \n");
+					//return 1;
+				}else
+				{
+					allowed_addresses[i].peer_idx = i;
+				}
+
+			}
+				bCreatedPeers = 1;
+		}
+
+		if(g_bAllPlayersFound == 1)
+		{
+			bWaiting = 0;
+		}
+
+	}
+
+	printf("Negotiating connection order...\n");
+	Send_Peer_Gretting();
+	Wait_For_Ready();
+
+	return 0;
+}
+
+void Send_Peer_Gretting()
+{
+	int i;
+
+	g_ConnMode = CONN_MODE_GREETING;
+
+    while (my_id == 0)  /* player number is based on id, low to high. */
+	{
+		my_id = (unsigned short)enet_time_get_raw();//(unsigned short) rand();
+	}
+
+	printf("My client id is %d\n", my_id);
+
+	for(i = 0; i < MAX_PLAYERS; ++i)
+	{
+		allowed_addresses[i].id = 0;
+	}
+
+    PacketPeerGreeting greetpacket;
+	memset(&greetpacket, '\0', sizeof (greetpacket));
+    greetpacket.header = HEADER_PEER_GREETING;
+    greetpacket.id = BUILDSWAP_INTEL16(my_id);
+
+	// Create the greeting packet
+	ENetPacket * packet = enet_packet_create (&greetpacket, sizeof(PacketPeerGreeting), ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+
+	printf("Broadcasting Greating...\n");
+
+	// Broadcast it to all the peers
+	enet_host_broadcast(g_Server, 0, packet);
+
+	// Flush the send buffer
+	enet_host_flush(g_Server);
+
+	while(CONN_MODE_GREETING == g_ConnMode)
+	{
+		ENetEvent event;
+		if (enet_host_service (g_Server, & event, CONNECTION_DELAY) > 0) 
+		{
+			int nAllIDsIn = 1;
+
+			
+			HandleEvent(&event);	
+			
+			// are all the id's in yet?
+			for(i = 0; i < (gcom->numplayers-1); ++i)
+			{
+				if(allowed_addresses[i].id == 0)
+				{
+					nAllIDsIn = 0;
+					break;
+				}
+			}
+
+			// add our ID to the list for sorting
+			allowed_addresses[gcom->numplayers-1].id = my_id;
+
+			//check the validity of the ID and sort them.
+			if(nAllIDsIn == 1)
+			{
+				int iteration = 0;
+				int k = 0;
+				unsigned short nCurrentHigh = 0;
+
+				printf("Sorting player IDs...\n");
+
+				for(iteration = 0; iteration < gcom->numplayers; ++iteration)
+				{					
+					//g_nPlayerIDList[i]
+					for(k = iteration+1; k < gcom->numplayers; ++k)
+					{
+						if(allowed_addresses[iteration].id == allowed_addresses[k].id)
+						{
+							printf("ERROR!!!!! Two players with the same Unique ID found, please restart...\n");
+						}
+						else
+						{
+							// if it's valid, then goto sort it
+							if(allowed_addresses[k].id > allowed_addresses[iteration].id)
+							{
+								//swap'm
+								//unsigned short nTemp = allowed_addresses[iteration];
+								ADDRESS_STRUCT tempAddress;
+
+								// Swap the positions
+								memcpy(&tempAddress, &allowed_addresses[iteration], sizeof(ADDRESS_STRUCT));								
+								memcpy(&allowed_addresses[iteration], &allowed_addresses[k], sizeof(ADDRESS_STRUCT));
+								memcpy(&allowed_addresses[k], &tempAddress, sizeof(ADDRESS_STRUCT));
+							}
+						}
+					}
+					
+				}
+
+				// Find our slot	
+				printf("Finding our player index...\n");
+
+				for(i = 0; i < (gcom->numplayers); ++i)
+				{	
+					printf("Index[%d] = %d\n", i, allowed_addresses[i].id);
+
+
+					if(allowed_addresses[i].id == my_id)
+					{
+						gcom->myconnectindex = i;
+						printf("You are player #%d\n", i);
+
+						// We're all greated, switch to waiting for all ready
+						g_ConnMode = CONN_MODE_WAITFORREADY;
+						break;
+					}
+					
+				}		
+			}
+		}
+	}
+	
+}
+
+void Wait_For_Ready()
+{
+
+	g_bWaitingForAllReady = gcom->numplayers-1;
+
+	// Create the greeting packet
+	unsigned char message = HEADER_PEER_READY;
+
+	ENetPacket * packet = enet_packet_create (&message, sizeof(unsigned char), ENET_PACKET_FLAG_RELIABLE);//ENET_PACKET_FLAG_RELIABLE
+
+	printf("Broadcasting Ready Packet...\n");
+
+	// Broadcast it to all the peers
+	enet_host_broadcast(g_Server, 0, packet);
+
+	// Flush the send buffer
+	enet_host_flush(g_Server);
+
+	//g_ConnMode = CONN_MODE_CONNECTED;//CONN_MODE_WAITFORREADY;
+
+	while(g_bWaitingForAllReady > 0)
+	{
+		ENetEvent event;
+		if (enet_host_service (g_Server, & event, CONNECTION_DELAY) > 0) 
+		{
+			int i;
+			
+			HandleEvent(&event);
+
+			g_bWaitingForAllReady = gcom->numplayers-1;
+			for(i = 0; i < gcom->numplayers; ++i)
+			{
+				if(allowed_addresses[i].bReady == 1)
+				{
+					--g_bWaitingForAllReady;
+				}
+			}
+
+			// Check to make sure we didn't subtract 1 from 0 to make 255. (unsigned char)
+			if(g_bWaitingForAllReady > gcom->numplayers)
+			{
+				printf("Error: we have a problem with the waiting for ready packets...\n");
+			}
+
+		}
+	}
+
+	printf("All players are ready. Start sending game data...\n");
+
+	g_ConnMode = CONN_MODE_CONNECTED;
+}
+
+void HandleEvent(ENetEvent *pEvent)
+{
+	switch(pEvent->type)
+			{
+			case ENET_EVENT_TYPE_CONNECT:
+				{
+					//bServerConnected = true;
+					ENetAddress address;
+					int i;
+
+					address.host = pEvent->peer->address.host; //ip;
+					address.port = pEvent->peer->address.port; //m_nPort;
+					char szHostName[64];
+					enet_address_get_host(&address, szHostName, 64);
+					
+					printf("Connection Established with: (%s)\n", szHostName);
+
+					for(i = 0; i < gcom->numplayers-1; ++i)
+					{
+						if(allowed_addresses[i].host == address.host)
+						{
+							allowed_addresses[i].bHeardFrom = 1;
+						}
+					}
+
+					for(i = 0; i < gcom->numplayers-1; ++i)
+					{
+						if(allowed_addresses[i].bHeardFrom == 0)
+						{
+							return;
+						}
+					}
+
+					// All players have been found... YAY!
+					g_bAllPlayersFound = 1;
+					printf("All Players Connected...\n");
+
+				}
+				break;
+			case ENET_EVENT_TYPE_RECEIVE:
+				{
+						g_nMessageLen = (short)pEvent->packet->dataLength;
+
+
+						
+						switch(g_ConnMode)
+						{
+
+						case CONN_MODE_GREETING:
+							{
+								PacketPeerGreeting packet;
+								unsigned int nPeerIndex;
+								int i;
+
+								if(pEvent->packet->data[0] != HEADER_PEER_GREETING)
+								{
+									printf("Invalid greeting!!!!\n");
+								}
+
+								printf("Received greeting from (%x)...\n", pEvent->peer->address.host);
+
+								memcpy(&packet, pEvent->packet->data, g_nMessageLen);
+								if(packet.header == HEADER_PEER_GREETING)
+								{
+									// Find the peer's index
+									nPeerIndex = GetPeerIndex(pEvent->peer);
+		
+									// Set the id for the peer
+									//g_nPlayerIDList[nPeerIndex] = packet.id;
+									allowed_addresses[nPeerIndex].id = packet.id;
+
+								}
+							}
+							break;
+						case CONN_MODE_WAITFORREADY:
+							{
+								if(pEvent->packet->data[0] == HEADER_PEER_READY)
+								{
+									allowed_addresses[GetOtherIndex(pEvent->peer)].bReady = 1;
+								}
+								else
+								{
+									printf("Invalid READY packet!!!\n");
+								}
+									
+
+							}
+							break;
+						case CONN_MODE_CONNECTED:
+						default:
+							{
+								if(g_ConnMode != CONN_MODE_CONNECTED)
+								{
+									PACKET packet;
+									packet.other = GetOtherIndex(pEvent->peer);
+									packet.bufferSize = g_nMessageLen;
+									memcpy(packet.buffer, pEvent->packet->data, g_nMessageLen);
+									incommingPacketQueue.push_back(packet);
+									printf("Saving early packet...\n");
+									break;
+								}
+
+								if(pEvent->packet->data[0] == 16)
+								{
+									printf("PACKET 16: len:[%d]\n", g_nMessageLen);
+								}
+
+								#ifdef _DEBUG_NETWORKING_LEVEL2_
+									printf("RECEIVE: type[%d] len:[%d]\n", pEvent->packet->data[0], g_nMessageLen);
+								#endif						
+								memcpy(&lastpacket[0], pEvent->packet->data, g_nMessageLen);
+								*g_other = GetOtherIndex(pEvent->peer);
+							}
+							break;
+						}
+						
+						/*if(g_nMessageLen > 0)
+						{
+							switch(pEvent->packet->data[0])
+							{
+								case HEADER_PEER_GREETING:
+								{
+									PacketPeerGreeting packet;
+									unsigned int nPeerIndex;
+									int i;
+
+									printf("Received greeting from (%x)...\n", pEvent->peer->address.host);
+
+									memcpy(&packet, pEvent->packet->data, g_nMessageLen);
+									if(packet.header == HEADER_PEER_GREETING)
+									{
+										// Find the peer's index in the g_Peers[] array
+										nPeerIndex = GetPeerIndex(pEvent->peer);
+			
+										// Set the id for the peer
+										//g_nPlayerIDList[nPeerIndex] = packet.id;
+										allowed_addresses[nPeerIndex].id = packet.id;
+
+									}
+								}
+								break;
+								case HEADER_PEER_READY:
+								{
+									unsigned int nOtherIndex;
+
+									printf("Received ready from (%x)...\n", pEvent->peer->address.host);
+									nOtherIndex = GetOtherIndex(pEvent->peer);
+
+									allowed_addresses[nOtherIndex].bReady = 1;
+
+								}
+								break;
+
+
+								default:
+									{
+										
+										if((g_bWaitingForAllReady) || (g_ConnMode == CONN_MODE_GREETING))
+										{
+											PACKET packet;
+											packet.other = GetOtherIndex(pEvent->peer);
+											packet.bufferSize = g_nMessageLen;
+											memcpy(packet.buffer, pEvent->packet->data, g_nMessageLen);
+											incommingPacketQueue.push_back(packet);
+											printf("Saving early packet...\n");
+											break;
+										}
+										
+
+										#ifdef _DEBUG_NETWORKING_LEVEL2_
+											printf("RECEIVE: type[%d] len:[%d]\n", pEvent->packet->data[0], g_nMessageLen);
+										#endif						
+										memcpy(&lastpacket[0], pEvent->packet->data, g_nMessageLen);
+
+										// find the correct index in the allowed_addresses[] array.
+										*g_other = GetOtherIndex(pEvent->peer);
+									}
+									break;
+							}
+						}
+						else
+						{
+							printf("Error: we received a Zero length packet!\n");
+						}*/
+
+						// Destroy the packet now that we're done with it.
+						enet_packet_destroy (pEvent->packet);
+				}
+				break;
+			case ENET_EVENT_TYPE_DISCONNECT:
+				{
+					printf("DISCONNECT: someone left!\n");
+				}
+				break;
+			default:
+				{
+					printf("Error: unknown event! : %d\n", pEvent->type);
+				}
+				break;
+			}
+}
+
+unsigned int GetPeerIndex(ENetPeer* peer)
+{
+	int i;
+	for(i = 0; i < gcom->numplayers; ++i)
+	{
+		if(peer->address.host == allowed_addresses[i].host)
+		{
+			return allowed_addresses[i].peer_idx;
+			//return i;
+		}
+	}
+
+	printf("Error: GetPeerIndex failed to find the corrent index!\n");
+	return 0;
+}
+
+unsigned int GetOtherIndex(ENetPeer* peer)
+{
+	int i;
+	for(i = 0; i < gcom->numplayers; ++i)
+	{
+		if(peer->address.host == allowed_addresses[i].host)
+		{
+			return i;//allowed_addresses[i].peer_idx;
+			//return i;
+		}
+	}
+
+	printf("Error: GetOtherIndex failed to find the corrent index!\n");
+	return 0;
+}
+
+void ServiceNetwork()
+{
+	ENetEvent event;
+	if (enet_host_service (g_Server, & event, INGAME_CONNECTION_DELAY) > 0) 
+	{
+		HandleEvent(&event);
+	}
+}
+
+//**************************************************************
+//* Network Transport Functions
+//**************************************************************
+int CreateServer(char* ip, int nPort, int nMaxPlayers)
+{
+
+	ENetAddress address;
+
+	printf("Creating server of %d players on port %d.\n", nMaxPlayers, nPort);
+
+    /* Bind the server to the default localhost.
+     * A specific host address can be specified by
+     * enet_address_set_host (& address, "x.x.x.x");
+     */
+    address.host = enet_address_set_host (& address, ip);//nIp;//ENET_HOST_ANY;
+    /* Bind the server to port 1234. */
+    address.port = nPort;
+
+    g_Server = enet_host_create (& address /* the address to bind the server host to */, 
+                nMaxPlayers /* allow up to 32 clients and/or outgoing connections */,
+                0 /* assume any amount of incoming bandwidth */,
+                0 /* assume any amount of outgoing bandwidth */);
+   
+	if (g_Server == NULL)
+    {
+		printf("Error creating server!\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+//**************************************************************
+//* Network Config File Functions
+//**************************************************************
+
+#include "cache1d.h"  /* kopen4load for cfg file. */
+#include "display.h"  /* getticks */
+
+#define IPSEG1(ip) ((((unsigned int) ip) & 0xFF000000) >> 24)
+#define IPSEG2(ip) ((((unsigned int) ip) & 0x00FF0000) >> 16)
+#define IPSEG3(ip) ((((unsigned int) ip) & 0x0000FF00) >>  8)
+#define IPSEG4(ip) ((((unsigned int) ip) & 0x000000FF)      )
+
+	char *static_ipstring(int ip)
+	{
+		static char s[16];
+		sprintf(s, "%u.%u.%u.%u", IPSEG1(ip), IPSEG2(ip), IPSEG3(ip), IPSEG4(ip));
+		return(s);
+	}
+
+	char *read_whole_file(const char *cfgfile)
+	{
+		char *buf;
+		long len, rc;
+		long handle;
+
+		if (cfgfile == NULL)
+			return(NULL);
+
+		handle = kopen4load(cfgfile,0);
+		if (handle == -1)
+		{
+			printf("ERROR: Failed to open config file [%s].\n", cfgfile);
+			return(NULL);
+		}
+
+		len = kfilelength(handle);
+		buf = (char *) malloc(len + 2);
+		if (!buf)
+		{
+			kclose(handle);
+			return(NULL);
+		}
+
+		rc = kread(handle, buf, len);
+		kclose(handle);
+		if (rc != len)
+		{
+			free(buf);
+			return(NULL);
+		}
+
+		buf[len] = '\0';
+		buf[len+1] = '\0';
+		return(buf);
+	}
+
+	char *get_token(char **ptr)
+	{
+		char *retval;
+		char *p = *ptr;
+		if (*p == '\0')
+			return(NULL);
+
+		while ((*p != '\0') && (isspace(*p)))
+			p++;
+
+		if (*p == '\0')  /* nothing but whitespace. */
+			return(NULL);
+
+		retval = p;
+		while ((*p != '\0') && (!isspace(*p)))
+			p++;
+
+		*p = '\0';
+		*ptr = p + 1;
+
+		/*printf("Got token [%s].\n", retval);*/
+		return(retval);
+	}
+
+	int parse_ip(const char *str, int *ip)
+	{
+		int ip1, ip2, ip3, ip4;
+
+		if (stricmp(str, "any") == 0)
+		{
+			*ip = 0;
+			return(1);
+		}
+
+		if (sscanf(str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4)
+		{
+			printf("\"%s\" is not a valid IP address.\n", str);
+			return(0);
+		}
+
+		/* we _should_ check that 0 <= ip? <= 255, but it'll fail later anyhow. */
+
+		*ip = ( ((ip1 & 0xFF) << 24) |
+				((ip2 & 0xFF) << 16) |
+				((ip3 & 0xFF) <<  8) |
+				((ip4 & 0xFF)      ) );
+
+		return(1);
+	}
+
+	int parse_interface(char *str, int *ip, short *udpport)
+	{
+		char *ptr = strchr(str, ':');
+		if (ptr) /* portnum specified? */
+			*ptr = '\0';
+
+		if (!parse_ip(str, ip))
+			return(0);
+
+		*udpport = BUILD_DEFAULT_UDP_PORT;
+		if (ptr != NULL)  /* port specified? */
+		{
+			ptr++;
+			if (stricmp(ptr, "any") == 0)
+				*udpport = 0;
+			else
+				*udpport = (short) atoi(ptr);
+		}
+
+		return(1);
+	}
+
+	int parse_udp_config(const char *cfgfile, gcomtype *gcom)
+	{
+		char *buf;
+		char *tok;
+		char *ptr;
+		int ip = 0;  /* interface */
+		int bcast = 0;
+
+		buf = read_whole_file(cfgfile);  /* we must free this. */
+		if (buf == NULL)
+			return(0);
+
+		ptr = buf;
+		while ((tok = get_token(&ptr)) != NULL)
+		{
+			int bogus = 1;
+
+			if (stricmp(tok, "interface") == 0)
+			{
+				if ( (tok = get_token(&ptr)) &&
+					 (parse_interface(tok, &ip, &udpport)) )
+				{
+					bogus = 0;
+				}
+				printf("Interface %s:%d chosen.\n",
+						static_ipstring(ip), (int) udpport);
+			}
+
+			else if (stricmp(tok, "mode") == 0)
+			{
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					bogus = 0;
+					if (stricmp(tok, "server") == 0)
+						udpmode = udpmode_server;
+					else if (stricmp(tok, "client") == 0)
+						udpmode = udpmode_client;
+					else if (stricmp(tok, "peer") == 0)
+						udpmode = udpmode_peer;
+					else
+						bogus = 1;
+
+					if (!bogus)
+						printf("You want to be in [%s] mode\n", tok);
+				}
+			}
+
+			else if (stricmp(tok, "broadcast") == 0)
+			{
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					bcast = atoi(tok);
+					if (bcast > MAX_PLAYERS - 1)
+					{
+						printf("WARNING: Too many broadcast players.\n");
+						bcast = MAX_PLAYERS - 1;
+					}
+
+					bogus = 0;
+				}
+			}
+
+			else if (stricmp(tok, "allow") == 0)
+			{
+				int host;
+				short port=BUILD_DEFAULT_UDP_PORT;
+				if ((tok = get_token(&ptr)) != NULL)
+				{
+					if (gcom->numplayers >= MAX_PLAYERS - 1)
+						printf("WARNING: Too many allowed IP addresses.\n");
+
+					else if (parse_interface(tok, &host, &port))
+					{
+						ENetAddress address;
+						enet_address_set_host(&address, static_ipstring(host));
+						printf("Adding: %s:%d to the list of allowed addresses.\n", static_ipstring(host), port);
+						allowed_addresses[gcom->numplayers].host = address.host;
+						allowed_addresses[gcom->numplayers].port = port;
+						gcom->numplayers++;
+						bogus = 0;
+					}
+				}
+			}
+
+			if (bogus)
+				printf("bogus token! [%s]\n", tok);
+		}
+
+		free(buf);
+
+		// Create the server
+		int ret = CreateServer(static_ipstring(ip), udpport, gcom->numplayers);
+		gcom->numplayers++; //that's you
+
+		if(ret == 0)
+		{
+			return 1;
+		}
+
+		return(0);
+	}
+
+
+
+
+} // end extern "C"
+
+/* end of mmulti.cpp ... */
+
+
--- /dev/null
+++ b/Engine/src/mmulti_stable.h
@@ -1,0 +1,19 @@
+#ifndef _MMULTI_STABLE_H_
+#define _MMULTI_STABLE_H_
+
+void stable_callcommit(void);
+void stable_initcrc(void);
+long stable_getcrc(char *buffer, short bufleng);
+void stable_initmultiplayers(char damultioption, char dacomrateoption, char dapriority);
+void stable_sendpacket(long other, char *bufptr, long messleng);
+void stable_setpackettimeout(long datimeoutcount, long daresendagaincount);
+void stable_uninitmultiplayers(void);
+void stable_sendlogon(void);
+void stable_sendlogoff(void);
+int  stable_getoutputcirclesize(void);
+void stable_setsocket(short newsocket);
+short stable_getpacket(short *other, char *bufptr);
+void stable_flushpackets(void);
+void stable_genericmultifunction(long other, char *bufptr, long messleng, long command);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/Engine/src/mmulti_unstable.h
@@ -1,0 +1,19 @@
+#ifndef _MMULTI_UNSTABLE_H_
+#define _MMULTI_UNSTABLE_H_
+
+void unstable_callcommit(void);
+void unstable_initcrc(void);
+long unstable_getcrc(char *buffer, short bufleng);
+void unstable_initmultiplayers(char damultioption, char dacomrateoption, char dapriority);
+void unstable_sendpacket(long other, char *bufptr, long messleng);
+void unstable_setpackettimeout(long datimeoutcount, long daresendagaincount);
+void unstable_uninitmultiplayers(void);
+void unstable_sendlogon(void);
+void unstable_sendlogoff(void);
+int  unstable_getoutputcirclesize(void);
+void unstable_setsocket(short newsocket);
+short unstable_getpacket(short *other, char *bufptr);
+void unstable_flushpackets(void);
+void unstable_genericmultifunction(long other, char *bufptr, long messleng, long command);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/Engine/src/multi.c
@@ -1,0 +1,1176 @@
+// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+// Ken Silverman's official web site: "http://www.advsys.net/ken"
+// See the included license file "BUILDLIC.TXT" for license info.
+// This file has been modified from Ken Silverman's original release
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "platform.h"
+
+extern long getcrc(char *buffer, short bufleng);
+extern void processreservedmessage(short tempbufleng, char *datempbuf);
+extern void initcrc(void);
+extern int comon(void);
+extern void comoff(void);
+extern int neton(void);
+extern void netoff(void);
+extern void startcom(void);
+extern int netinitconnection (long newconnectnum, char *newcompaddr);
+extern void installbicomhandlers(void);
+extern void uninstallbicomhandlers(void);
+
+#define COMBUFSIZ 16384
+#define COMCODEBYTES 384
+#define COMCODEOFFS 14
+#define NETCODEBYTES 384
+#define MAXPLAYERS 16
+#define ESC1 0x83
+#define ESC2 0x8f
+#define NETBACKPACKETS 4
+#define MAXIPXSIZ 546
+
+#define updatecrc16(crc,dat) crc = (((crc<<8)&65535)^crctable[((((unsigned short)crc)>>8)&65535)^dat])
+
+char syncstate = 0, hangup = 1;
+static char multioption = 0, comrateoption = 0;
+
+	//COM & NET variables
+short numplayers = 0, myconnectindex = 0;
+short connecthead, connectpoint2[MAXPLAYERS];
+char syncbuf[MAXIPXSIZ];
+long syncbufleng, outbufindex[128], outcnt;
+long myconnectnum, otherconnectnum, mypriority;
+long crctable[256];
+
+	//COM ONLY variables
+long comnum, comvect, comspeed, comtemp, comi, comescape, comreset;
+#ifdef PLATFORM_DOS   // !!! this is a real mess. --ryan.
+static void interrupt far comhandler(void);
+static unsigned short orig_pm_sel, orig_rm_seg, orig_rm_off;
+static unsigned long orig_pm_off;
+#endif
+volatile unsigned char *inbuf, *outbuf, *comerror, *incnt, *comtype;
+volatile unsigned char *comresend;
+volatile short *inbufplc, *inbufend, *outbufplc, *outbufend, *comport;
+#ifdef PLATFORM_DOS   // !!! this is a real mess. --ryan.
+static char rmbuffer[COMCODEBYTES] =        //See realcom.asm
+{
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00,0x50,0x53,0x66,0x51,0x52,0x2e,
+	0x8b,0x16,0x08,0x00,0x83,0xc2,0x02,0xec,0x24,0x07,
+	0x8a,0xe0,0x80,0xfc,0x02,0x74,0x08,0x80,0xfc,0x04,
+	0x74,0x62,0xe9,0x89,0x00,0x2e,0x8b,0x16,0x08,0x00,
+	0x2e,0x8a,0x0e,0x0a,0x00,0x80,0xe9,0x01,0x78,0x7a,
+	0x2e,0x80,0x3e,0x0c,0x00,0x01,0x7c,0x10,0x74,0x04,
+	0xb0,0x83,0xeb,0x02,0xb0,0x8f,0xee,0x2e,0xfe,0x0e,
+	0x0c,0x00,0xeb,0xe3,0x2e,0x80,0x3e,0x0b,0x00,0x01,
+	0x7c,0x12,0x74,0x04,0xb0,0x83,0xeb,0x04,0x2e,0xa0,
+	0x0d,0x00,0xee,0x2e,0xfe,0x0e,0x0b,0x00,0xeb,0xc9,
+	0x2e,0x8b,0x1e,0x04,0x00,0x2e,0x3b,0x1e,0x06,0x00,
+	0x74,0x3c,0x2e,0x8a,0x87,0x80,0x41,0xee,0x43,0x81,
+	0xe3,0xff,0x3f,0x2e,0x89,0x1e,0x04,0x00,0xeb,0xab,
+	0x2e,0x8b,0x16,0x08,0x00,0xec,0x2e,0x8b,0x1e,0x02,
+	0x00,0x2e,0x88,0x87,0x80,0x01,0x43,0x81,0xe3,0xff,
+	0x3f,0x2e,0x89,0x1e,0x02,0x00,0x2e,0x80,0x3e,0x0a,
+	0x00,0x10,0x75,0x08,0x83,0xc2,0x05,0xec,0xa8,0x01,
+	0x75,0xd6,0xf6,0xc4,0x01,0x0f,0x84,0x56,0xff,0xb0,
+	0x20,0xe6,0x20,0x5a,0x66,0x59,0x5b,0x58,0xcf,
+};
+#endif
+
+	//NET ONLY variables
+short socket = 0x4949;
+char compaddr[MAXPLAYERS][12], mycompaddr[12];
+char netincnt[MAXPLAYERS], netoutcnt[MAXPLAYERS];
+char getmess[MAXIPXSIZ];
+char omessout[MAXPLAYERS][NETBACKPACKETS][MAXIPXSIZ];
+short omessleng[MAXPLAYERS][NETBACKPACKETS];
+short omessconnectindex[MAXPLAYERS][NETBACKPACKETS];
+short omessnum[MAXPLAYERS];
+long connectnum[MAXPLAYERS], rmoffset32, rmsegment16, neti;
+volatile char *ecbget, *ecbput, *ipxin, *ipxout, *messin, *messout;
+volatile char *tempinbuf, *tempoutbuf, *rmnethandler, *netinbuf;
+volatile short *netinbufplc, *netinbufend;
+static char rmnetbuffer[NETCODEBYTES] =
+{
+	0xfb,0x2e,0x8a,0x26,0x62,0x00,0x2e,0xa0,0x63,0x00,
+	0x83,0xe8,0x1e,0x2e,0x8b,0x1e,0xe2,0x06,0x2e,0x88,
+	0x87,0xe4,0x06,0x43,0x81,0xe3,0xff,0x3f,0x2e,0x88,
+	0xa7,0xe4,0x06,0x43,0x81,0xe3,0xff,0x3f,0x33,0xf6,
+	0x2e,0x8a,0x8c,0xa0,0x00,0x46,0x2e,0x88,0x8f,0xe4,
+	0x06,0x43,0x81,0xe3,0xff,0x3f,0x3b,0xf0,0x72,0xec,
+	0x2e,0x89,0x1e,0xe2,0x06,0xbb,0x04,0x00,0x8c,0xc8,
+	0x8e,0xc0,0xbe,0x00,0x00,0xcd,0x7a,0xcb,
+};
+static long my7a = 0;
+
+#ifdef PLATFORM_DOS
+#pragma aux koutp =\
+	"out dx, al",\
+	parm [edx][eax]\
+
+#pragma aux kinp =\
+	"in al, dx",\
+	parm [edx]
+#endif
+
+long convalloc32 (long size)
+{
+#ifdef PLATFORM_DOS	
+	union REGS r;
+
+	r.x.eax = 0x0100;           //DPMI allocate DOS memory
+	r.x.ebx = ((size+15)>>4);   //Number of paragraphs requested
+	int386(0x31,&r,&r);
+
+	if (r.x.cflag != 0) return ((long)0);   //Failed
+	return ((long)((r.x.eax&0xffff)<<4));   //Returns full 32-bit offset
+#else
+	fprintf (stderr, "%s, line %d; convalloc32() called\n", __FILE__,
+		__LINE__);
+	return 0;
+#endif	
+}
+
+#ifdef PLATFORM_DOS
+#pragma aux fixregistersaftersimulate =\
+	"cld",\
+	"push ds",\
+	"pop es",\
+
+static struct rminfo
+{
+	long EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX;
+	short flags, ES, DS, FS, GS, IP, CS, SP, SS;
+} RMI;
+#endif
+
+long simulateint(char intnum, long daeax, long daebx, long daecx, long daedx, long daesi, long daedi)
+{
+#ifdef PLATFORM_DOS	
+	union REGS regs;
+	struct SREGS sregs;
+
+	memset(&RMI,0,sizeof(RMI));    // Set up real-mode call structure
+	memset(&sregs,0,sizeof(sregs));
+
+	RMI.EAX = daeax;
+	RMI.EBX = daebx;
+	RMI.ECX = daecx;
+	RMI.EDX = daedx;
+	RMI.ESI = daesi-rmoffset32;
+	RMI.EDI = daedi-rmoffset32;
+	RMI.DS = rmsegment16;
+	RMI.ES = rmsegment16;
+
+	regs.w.ax = 0x0300;            // Use DMPI call 300h to issue the DOS interrupt
+	regs.h.bl = intnum;
+	regs.h.bh = 0;
+	regs.w.cx = 0;
+	sregs.es = FP_SEG(&RMI);
+	regs.x.edi = FP_OFF(&RMI);
+	int386x(0x31,&regs,&regs,&sregs);
+
+	fixregistersaftersimulate();
+
+	return(RMI.EAX);
+#else
+	fprintf(stderr, "%s line %d; simulateint() called\n",__FILE__,__LINE__);
+	return 0;
+#endif	
+}
+
+void initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
+{
+	long i;
+
+	multioption = damultioption;
+	comrateoption = dacomrateoption;
+
+	connecthead = 0;
+	for(i=MAXPLAYERS-1;i>=0;i--)
+		connectpoint2[i] = -1, connectnum[i] = 0x7fffffff;
+
+	mypriority = dapriority;
+
+	initcrc();
+
+	if ((multioption >= 1) && (multioption <= 4))
+	{
+		comnum = multioption;
+		switch(dacomrateoption&15)
+		{
+			case 0: comspeed = 2400; break;
+			case 1: comspeed = 4800; break;
+			case 2: comspeed = 9600; break;
+			case 3: comspeed = 14400; break;
+			case 4: comspeed = 19200; break;
+			case 5: comspeed = 28800; break;
+		}
+		comon();
+	}
+	if (multioption >= 5)
+	{
+		if ((i = neton()) != 0)
+		{
+			if (i == -1) printf("IPX driver not found\n");
+			if (i == -2) printf("Socket could not be opened\n");
+			exit(0);
+		}
+	}
+	numplayers = 1;
+}
+
+void uninitmultiplayers()
+{
+	if (numplayers > 0)
+	{
+		if ((multioption >= 1) && (multioption <= 4)) comoff();
+		if (multioption >= 5) netoff();  //Uninstall before timer
+	}
+}
+
+int neton(void)
+{
+	long i, j;
+
+	if ((simulateint(0x2f,(long)0x7a00,0L,0L,0L,0L,0L)&255) != 255) return(-1);
+	if (*(long *)(0x7a<<2) == 0)
+	{
+#ifdef PLATFORM_DOS		
+		printf("Faking int 0x7a to call IPX entry at: %4x:%4x\n",RMI.ES,RMI.EDI&65535);
+		my7a = convalloc32(16L);
+		*(short *)((0x7a<<2)+0) = (my7a&15);
+		*(short *)((0x7a<<2)+2) = (my7a>>4);
+
+		*(char *)(my7a+0) = 0x2e;               //call far ptr [L1]
+		*(char *)(my7a+1) = 0x9a;
+		*(long *)(my7a+2) = 7L;
+		*(char *)(my7a+6) = 0xcf;               //iret
+		*(short *)(my7a+7) = (RMI.EDI&65535);   //L1: ipxoff
+		*(short *)(my7a+9) = RMI.ES;            //    ipxseg
+#endif		
+	}
+
+		//Special stuff for WATCOM C
+	if ((rmoffset32 = convalloc32(1380L+NETCODEBYTES+COMBUFSIZ)) == 0)
+		{ printf("Can't allocate memory for IPX\n"); exit; }
+	rmsegment16 = (rmoffset32>>4);
+
+	i = rmoffset32;
+	ecbget = (char *)i; i += 48;
+	ecbput = (char *)i; i += 48;
+	ipxin = (char *)i; i += 32;
+	ipxout = (char *)i; i += 32;
+	messin = (char *)i; i += 560;
+	messout = (char *)i; i += 560;
+	tempinbuf = (char *)i; i += 16;
+	tempoutbuf = (char *)i; i += 80;
+	rmnethandler = (char *)i; i += NETCODEBYTES;
+	netinbufplc = (short *)i; i += 2;
+	netinbufend = (short *)i; i += 2;
+	netinbuf = (char *)i; i += COMBUFSIZ;
+	memcpy((void *)rmnethandler,(void *)rmnetbuffer,NETCODEBYTES);
+
+	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket
+	if ((simulateint(0x7a,(long)0xff,0L,0L,(long)socket,0L,0L)&255) != 0) return(-2); //Opensocket
+
+	simulateint(0x7a,0L,9L,0L,0L,(long)tempoutbuf,0L);    //Getinternetworkaddress
+	memcpy((void *)&mycompaddr[0],(void *)&tempoutbuf[0],10);
+	mycompaddr[10] = (socket&255);
+	mycompaddr[11] = (socket>>8);
+	myconnectnum = ((long)tempoutbuf[6])+(((long)tempoutbuf[7])<<8)+(((long)(tempoutbuf[8]^tempoutbuf[9]))<<16)+(((long)mypriority)<<24);
+
+	netinitconnection(myconnectnum,mycompaddr);
+
+	ecbget[8] = 1; ecbput[8] = 0;
+	*netinbufplc = 0; *netinbufend = 0;
+
+	for(i=MAXPLAYERS-1;i>=0;i--) netincnt[i] = 0, netoutcnt[i] = 0;
+
+	for(i=0;i<MAXPLAYERS;i++)
+	{
+		omessnum[i] = 0;
+		for(j=NETBACKPACKETS-1;j>=0;j--)
+		{
+			omessleng[i][j] = 0;
+			omessconnectindex[i][j] = 0;
+		}
+	}
+
+		//Netlisten
+	for(i=0;i<30;i++) ipxin[i] = 0;
+	for(i=0;i<48;i++) ecbget[i] = 0;
+	ecbget[4] = (char)(((long)rmnethandler-rmoffset32)&255), ecbget[5] = (char)(((long)rmnethandler-rmoffset32)>>8);
+	ecbget[6] = (char)(rmsegment16&255), ecbget[7] = (char)(rmsegment16>>8);
+	ecbget[10] = (socket&255), ecbget[11] = (socket>>8);
+	ecbget[34] = 2, ecbget[35] = 0;
+	ecbget[36] = (char)(((long)ipxin-rmoffset32)&255), ecbget[37] = (char)(((long)ipxin-rmoffset32)>>8);
+	ecbget[38] = (char)(rmsegment16&255), ecbget[39] = (char)(rmsegment16>>8);
+	ecbget[40] = 30, ecbget[41] = 0;
+	ecbget[42] = (char)(((long)messin-rmoffset32)&255), ecbget[43] = (char)(((long)messin-rmoffset32)>>8);
+	ecbget[44] = (char)(rmsegment16&255), ecbget[45] = (char)(rmsegment16>>8);
+	ecbget[46] = (MAXIPXSIZ&255), ecbget[47] = (MAXIPXSIZ>>8);
+	simulateint(0x7a,0L,(long)0x4,0L,0L,(long)ecbget,0L);               //Receivepacket
+
+	return(0);
+}
+
+int comon()
+{
+	long divisor, cnt;
+	short *ptr;
+
+	if ((comnum < 1) || (comnum > 4)) return(-1);
+	//comvect = 0xb+(comnum&1);
+	comvect = ((comrateoption>>4)+0x8+2);
+	installbicomhandlers();
+
+	*incnt = 0; outcnt = 0;
+	*inbufplc = 0; *inbufend = 0; *outbufplc = 0; *outbufend = 0;
+
+	ptr = (short *)(0x400L+(long)((comnum-1)<<1));
+	*comport = *ptr;
+	if (*comport == 0)
+	{
+		switch(comnum)
+		{
+			case 1: *comport = 0x3f8; break;
+			case 2: *comport = 0x2f8; break;
+			case 3: *comport = 0x3e8; break;
+			case 4: *comport = 0x2e8; break;
+		}
+		if ((inp((*comport)+5)&0x60) != 0x60) { *comport = 0; return(-1); }
+	}
+	if ((comspeed <= 0) || (comspeed > 115200)) return(-1);
+
+	  // Baud-Setting,?,?,Parity O/E,Parity Off/On, Stop-1/2,Bits-5/6/7/8
+	  // 0x0b is odd parity,1 stop bit, 8 bits
+
+#ifdef PLATFORM_DOS	
+	_disable();
+#endif	
+	koutp((*comport)+3,0x80);                  //enable latch registers
+	divisor = 115200 / comspeed;
+	koutp((*comport)+0,divisor&255);           //# = 115200 / bps
+	koutp((*comport)+1,divisor>>8);
+	koutp((*comport)+3,0x03);                  //0x03 = n,8,1
+
+	koutp((*comport)+2,0x87);   //check for a 16550   0=1,64=4,128=8,192=14
+	if ((kinp((*comport)+2)&0xf8) == 0xc0)
+	{
+		*comtype = 16;
+	}
+	else
+	{
+		*comtype = 1;
+		koutp((*comport)+2,0);
+	}
+	cnt = *comtype;                        //Clear any junk already in FIFO
+	while (((kinp((*comport)+5)&0x1) > 0) && (cnt > 0))
+		{ kinp(*comport); cnt--; }
+
+	koutp((*comport)+4,0x0b);             //setup for interrupts (modem control)
+	koutp((*comport)+1,0);                //com interrupt disable
+	koutp(0x21,kinp(0x21)&(255-(1<<(comvect&7))));          //Unmask vector
+	kinp((*comport)+6);
+	kinp((*comport)+5);
+	kinp((*comport)+0);
+	kinp((*comport)+2);
+	koutp((*comport)+1,0x03);             //com interrupt enable
+	koutp(0x20,0x20);
+
+	comescape = 0; comreset = 0;
+	*comerror = 0; *comresend = 0;
+#ifdef PLATFORM_DOS	
+	_enable();
+#endif	
+
+	syncbufleng = 0;
+
+	return(0);
+}
+
+void netoff()
+{
+	if (my7a) *(long *)(0x7a<<2) = 0L;
+	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);               //Closesocket
+}
+
+void comoff()
+{
+	long i;
+
+	i = 1048576;
+	while ((*outbufplc != *outbufend) && (i >= 0))
+	{
+		startcom();
+		i--;
+	}
+
+#ifdef PLATFORM_DOS	
+	_disable();
+#endif	
+	koutp(0x21,kinp(0x21)|(1<<(comvect&7)));          //Mask vector
+	if (hangup != 0)
+	{
+		koutp((*comport)+1,0);
+		koutp((*comport)+4,0);
+	}
+#ifdef PLATFORM_DOS	
+	_enable();
+#endif	
+	uninstallbicomhandlers();
+}
+
+void netsend (short otherconnectindex, short messleng)
+{
+	long i;
+
+	i = 32767;
+	while ((ecbput[8] != 0) && (i > 0)) i--;
+	for(i=0;i<30;i++) ipxout[i] = 0;
+	for(i=0;i<48;i++) ecbput[i] = 0;
+	ipxout[5] = 4;
+	if (otherconnectindex < 0)
+	{
+		memcpy((void *)&ipxout[6],(void *)&compaddr[0][0],4);
+		ipxout[10] = 0xff, ipxout[11] = 0xff, ipxout[12] = 0xff;
+		ipxout[13] = 0xff, ipxout[14] = 0xff, ipxout[15] = 0xff;
+		ipxout[16] = (socket&255), ipxout[17] = (socket>>8);
+	}
+	else
+	{
+		memcpy((void *)&ipxout[6],(void *)&compaddr[otherconnectindex][0],12);
+	}
+
+	ecbput[10] = (socket&255), ecbput[11] = (socket>>8);
+	if (otherconnectindex < 0)
+	{
+		ecbput[28] = 0xff, ecbput[29] = 0xff, ecbput[30] = 0xff;
+		ecbput[31] = 0xff, ecbput[32] = 0xff, ecbput[33] = 0xff;
+	}
+	else
+	{
+		memcpy((void *)&ecbput[28],(void *)&compaddr[otherconnectindex][4],6);
+	}
+
+	ecbput[34] = 2, ecbput[35] = 0;
+	ecbput[36] = (char)(((long)ipxout-rmoffset32)&255), ecbput[37] = (char)(((long)ipxout-rmoffset32)>>8);
+	ecbput[38] = (char)(rmsegment16&255), ecbput[39] = (char)(rmsegment16>>8);
+	ecbput[40] = 30, ecbput[41] = 0;
+	ecbput[42] = (char)(((long)messout-rmoffset32)&255), ecbput[43] = (char)(((long)messout-rmoffset32)>>8);
+	ecbput[44] = (char)(rmsegment16&255), ecbput[45] = (char)(rmsegment16>>8);
+	ecbput[46] = (char)(messleng&255), ecbput[47] = (char)(messleng>>8);
+	simulateint(0x7a,0L,(long)0x3,0L,0L,(long)ecbput,0L);               //Sendpacket
+}
+
+void comsend(char ch)
+{
+	if (ch == ESC1)
+	{
+		outbuf[*outbufend] = ESC1; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
+		ch = 128;
+	}
+	else if (ch == ESC2)
+	{
+		outbuf[*outbufend] = ESC1; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
+		ch = 129;
+	}
+	outbuf[*outbufend] = ch; *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));
+}
+
+void startcom()
+{
+	if ((kinp((*comport)+5)&0x40) == 0) return;
+
+	if (*comresend != 0)
+	{
+		if (*comresend == 2) koutp(*comport,ESC1);
+		if (*comresend == 1) koutp(*comport,ESC2);
+		*comresend = (*comresend) - 1;
+	}
+	else if (*comerror != 0)
+	{
+		if (*comerror == 2) koutp(*comport,ESC1);
+		if (*comerror == 1) koutp(*comport,*incnt);
+		*comerror = (*comerror) - 1;
+	}
+	else if (*outbufplc != *outbufend)
+	{
+		koutp(*comport,(long)outbuf[*outbufplc]);
+		*outbufplc = (((*outbufplc)+1)&(COMBUFSIZ-1));
+	}
+}
+
+void interrupt far comhandler(void)
+{
+	do
+	{
+		comtemp = (kinp((*comport)+2)&7);
+		if (comtemp == 2)
+		{
+			for(comi=(*comtype);comi>0;comi--)
+			{
+				if (*comresend != 0)
+				{
+					if (*comresend == 2) koutp(*comport,ESC1);
+					if (*comresend == 1) koutp(*comport,ESC2);
+					*comresend = (*comresend) - 1;
+					continue;
+				}
+				if (*comerror != 0)
+				{
+					if (*comerror == 2) koutp(*comport,ESC1);
+					if (*comerror == 1) koutp(*comport,*incnt);
+					*comerror = (*comerror) - 1;
+					continue;
+				}
+				if (*outbufplc != *outbufend)
+				{
+					koutp(*comport,(long)outbuf[*outbufplc]);
+					*outbufplc = (((*outbufplc)+1)&(COMBUFSIZ-1));
+					continue;
+				}
+				break;
+			}
+		}
+		else if (comtemp == 4)
+		{
+			do
+			{
+				//comtemp = (rand()&255);
+				//if (comtemp == 17)
+				//   inbuf[*inbufend] = 17;
+				//else
+					inbuf[*inbufend] = (char)kinp(*comport);
+
+				//if (comtemp != 11) *inbufend = (((*inbufend)+1)&(COMBUFSIZ-1));
+				//if (comtemp == 24)
+				//{
+				//   inbuf[*inbufend] = 17;
+					*inbufend = (((*inbufend)+1)&(COMBUFSIZ-1));
+				//}
+				//comtemp = 4;
+
+			} while ((*comtype == 16) && ((kinp((*comport)+5)&1) > 0));
+		}
+	}
+	while ((comtemp&1) == 0);
+	koutp(0x20,0x20);
+}
+
+int netinitconnection (long newconnectnum, char *newcompaddr)
+{
+	long i, j, k, newindex, templong;
+	char tempchar;
+
+		//Check to see if connection number already initialized
+	for(i=0;i<MAXPLAYERS;i++)
+		if (connectnum[i] == newconnectnum) return(-1);
+
+		//Find blank place to put new connection number
+	newindex = 0;
+	while (connectnum[newindex] != 0x7fffffff)
+	{
+		newindex++;
+		if (newindex >= MAXPLAYERS) return(-1);  //Out of space! (more than 16 players)
+	}
+
+		//Insert connection number on connection number list
+	numplayers++;
+	connectnum[newindex] = newconnectnum;
+
+		//Getinternetworkaddress
+	memcpy((void *)&compaddr[newindex][0],(void *)newcompaddr,10);
+	compaddr[newindex][10] = (socket&255);
+	compaddr[newindex][11] = (socket>>8);
+
+		//Sort connection numbers
+	for(i=1;i<MAXPLAYERS;i++)
+		for(j=0;j<i;j++)
+			if (connectnum[i] < connectnum[j])
+			{
+				templong = connectnum[i], connectnum[i] = connectnum[j], connectnum[j] = templong;
+				for(k=0;k<12;k++) tempchar = compaddr[i][k], compaddr[i][k] = compaddr[j][k], compaddr[j][k] = tempchar;
+			}
+
+		//Rebuild linked list, MAKING SURE that the linked list goes through
+		//   the players in the same order on all computers!
+	connecthead = 0;
+	for(i=0;i<numplayers-1;i++) connectpoint2[i] = i+1;
+	connectpoint2[numplayers-1] = -1;
+
+	for(i=0;i<numplayers;i++)
+		if (connectnum[i] == myconnectnum) myconnectindex = i;
+
+	return(1);
+}
+
+void netuninitconnection(short goneindex)
+{
+	long i, j, k=0;
+
+	connectnum[goneindex] = 0x7fffffff; numplayers--;
+
+	j = 0;
+	for(i=0;i<MAXPLAYERS;i++)
+		if (connectnum[i] != 0x7fffffff)
+		{
+			if (j == 0) connecthead = i; else connectpoint2[k] = i;
+			k = i; j++;
+		}
+	connectpoint2[k] = -1;
+}
+
+void sendpacket (short otherconnectindex, unsigned char *bufptr, short messleng)
+{
+	long i, j, k, l;
+
+	if (multioption <= 0) return;
+	if (multioption < 5)
+	{
+			//Allow initial incnt/outcnt syncing
+		if ((bufptr[0] == 253) || (bufptr[0] == 254)) outcnt = 0;
+
+		outbufindex[outcnt] = *outbufend;
+
+		comsend(((messleng&1)<<7)+outcnt);
+		for(i=0;i<messleng;i++) comsend(bufptr[i]);
+		if ((comrateoption&15) > 0)
+		{
+			i = getcrc(bufptr,messleng);
+			updatecrc16(i,(((messleng&1)<<7)+outcnt));
+			comsend(i&255); comsend(i>>8);
+		}
+		outbuf[*outbufend] = ESC2, *outbufend = (((*outbufend)+1)&(COMBUFSIZ-1));  //Not raw
+
+		startcom();
+		outcnt = ((outcnt+1)&127);
+	}
+	else
+	{
+		i = 262144;       //Wait for last packet to be sent
+		while ((i > 0) && (ecbput[8] != 0)) i--;
+
+		messout[0] = myconnectindex; j = 1;
+
+		if ((unsigned char) bufptr[0] >= 200)
+		{
+				//Allow initial incnt/outcnt syncing
+			for(i=0;i<MAXPLAYERS;i++) netoutcnt[i] = 0;
+			messout[j++] = 0xfe;
+			for(i=0;i<messleng;i++) messout[j++] = bufptr[i];
+			netsend(otherconnectindex,j);
+			return;
+		}
+
+			//Copy new packet into omess fifo
+		if (otherconnectindex < 0)
+		{
+			for(k=connecthead;k>=0;k=connectpoint2[k])
+				if (k != myconnectindex)
+				{
+					omessconnectindex[k][omessnum[k]] = -1;
+					omessleng[k][omessnum[k]] = messleng;
+					for(i=0;i<messleng;i++) omessout[k][omessnum[k]][i] = bufptr[i];
+				}
+		}
+		else
+		{
+			omessconnectindex[otherconnectindex][omessnum[otherconnectindex]] = otherconnectindex;
+			omessleng[otherconnectindex][omessnum[otherconnectindex]] = messleng;
+			for(i=0;i<messleng;i++) omessout[otherconnectindex][omessnum[otherconnectindex]][i] = bufptr[i];
+		}
+
+			//Put last 4 packets into 1 big packet
+		for(l=0;l<NETBACKPACKETS;l++)
+		{
+				//k = omess index
+			k = ((omessnum[otherconnectindex]-l)&(NETBACKPACKETS-1));
+
+			if (omessconnectindex[otherconnectindex][k] < 0)
+			{
+				messout[j++] = 255;
+				for(i=connecthead;i>=0;i=connectpoint2[i])
+					if (i != myconnectindex)
+						messout[j++] = ((netoutcnt[i]-l)&255);
+			}
+			else
+			{
+				messout[j++] = otherconnectindex;
+				messout[j++] = ((netoutcnt[otherconnectindex]-l)&255);
+			}
+			messout[j++] = (omessleng[otherconnectindex][k]&255);
+			messout[j++] = ((omessleng[otherconnectindex][k]>>8)&255);
+			for(i=0;i<omessleng[otherconnectindex][k];i++)
+				messout[j++] = omessout[otherconnectindex][k][i];
+		}
+
+			//SEND!!!
+		netsend(otherconnectindex,j);
+
+
+			//Increment outcnt and omessnum counters
+		if (otherconnectindex < 0)
+		{
+			for(i=connecthead;i>=0;i=connectpoint2[i])
+				if (i != myconnectindex)
+				{
+					netoutcnt[i]++;
+					omessnum[i] = ((omessnum[i]+1)&(NETBACKPACKETS-1));
+				}
+		}
+		else
+		{
+			netoutcnt[otherconnectindex]++;
+			omessnum[otherconnectindex] = ((omessnum[otherconnectindex]+1)&(NETBACKPACKETS-1));
+		}
+	}
+}
+
+short getpacket (short *otherconnectindex, char *bufptr)
+{
+	char toindex, bad, totbad;
+	short i, j=0, k, messleng, submessleng;
+
+	if (multioption <= 0) return(0);
+	if (multioption < 5)
+	{
+		*otherconnectindex = (myconnectindex^1);
+
+		bad = 0;
+		while (*inbufplc != *inbufend)
+		{
+			i = (short)inbuf[*inbufplc], *inbufplc = (((*inbufplc)+1)&(COMBUFSIZ-1));
+
+			if (i != ESC2)
+			{
+				if (i == ESC1) { comescape++; continue; }
+				if (comescape != 0)
+				{
+					comescape--;
+					if ((i < 128) && (*comresend == 0) && (((i-outcnt)&127) > 4))
+					{
+						*comresend = 2;
+						*outbufplc = outbufindex[i];
+						startcom();
+						continue;
+					}
+					if (syncbufleng < MAXIPXSIZ)
+					{
+						if (i == 128) { syncbuf[syncbufleng++] = ESC1; continue; }
+						if (i == 129) { syncbuf[syncbufleng++] = ESC2; continue; }
+					}
+				}
+				if (syncbufleng < MAXIPXSIZ) syncbuf[syncbufleng++] = i;
+				continue;
+			}
+
+			if (comescape != 0)
+			{
+				comescape = 0; comreset = 0; *comerror = 0;
+				syncbufleng = 0;
+				continue;
+			}
+
+			messleng = syncbufleng-3+(((comrateoption&15)==0)<<1);
+			if ((syncbuf[0]&127) != *incnt)
+			{
+				bad |= 1;      //Packetcnt error
+				if ((*incnt == 1) && (syncbuf[1] == 254))  //Prevent 2 Masters!
+					myconnectindex = (myconnectnum<otherconnectnum);
+			}
+			if (((syncbuf[0]&128)>>7) != (messleng&1)) bad |= 2;   //messleng error
+			for(i=0;i<messleng;i++) bufptr[i] = syncbuf[i+1];
+			if ((comrateoption&15) > 0)
+			{
+				i = getcrc(bufptr,messleng);
+				updatecrc16(i,syncbuf[0]);
+				if (((unsigned short)i) != ((long)syncbuf[syncbufleng-2])+((long)syncbuf[syncbufleng-1]<<8))
+					bad |= 2;   //CRC error
+			}
+
+			syncbufleng = 0;
+			if (bad != 0)
+			{
+					//Don't send reset again if outbufplc is not before incnt!
+				if ((bad == 1) && ((((syncbuf[0]&127)-(*incnt))&127) >= 124))
+				{
+					bad = 0;
+					continue;
+				}
+
+				bad = 0;
+				if (comreset != 0) comreset--;
+				if (((*comerror)|comreset) == 0)
+				{
+					*comerror = 2; comreset = 2;
+					startcom();
+				}
+				continue;
+			}
+
+			*incnt = (((*incnt)+1)&127);
+			if ((messleng > 0) && (bufptr[0] >= 200))  //200-255 are messages for engine's use only
+				processreservedmessage(messleng,bufptr);
+
+			comescape = 0; comreset = 0; *comerror = 0;
+			return(messleng);
+		}
+		return(0);
+	}
+	else
+	{
+		if (*netinbufplc == *netinbufend) return(0);
+
+		messleng = (short)netinbuf[*netinbufplc] + (((short)netinbuf[((*netinbufplc)+1)&(COMBUFSIZ-1)])<<8);
+		for(i=0;i<messleng;i++)
+			getmess[i] = netinbuf[((*netinbufplc)+i+2)&(COMBUFSIZ-1)];
+
+		k = 0; *otherconnectindex = getmess[k++];
+
+		for(totbad=0;totbad<NETBACKPACKETS;totbad++)   //Number of sub-packets per packet
+		{
+			toindex = getmess[k++];
+			if (toindex == 0xfe)
+			{
+				netincnt[*otherconnectindex] = 0;  // (>= 200) && ( <= 254)
+				submessleng = messleng-2;          // Submessleng not necessary
+			}
+			else
+			{
+				if (toindex == 0xff)
+				{
+					for(i=connecthead;i>=0;i=connectpoint2[i])
+						if (i != *otherconnectindex)
+						{
+							if (i == myconnectindex) j = getmess[k];
+							k++;
+						}
+				}
+				else
+					j = getmess[k++];
+
+				if (j != netincnt[*otherconnectindex])
+				{
+					submessleng = (short)getmess[k]+(((short)getmess[k+1])<<8);
+					k += submessleng+2;
+					continue;
+				}
+				netincnt[*otherconnectindex]++;
+
+				submessleng = (short)getmess[k]+(((short)getmess[k+1])<<8); k += 2;
+			}
+			for(i=0;i<submessleng;i++) bufptr[i] = getmess[k++];
+
+			if (totbad == 0)
+			{
+					//Increment inbufplc only if first sub-message is read
+				*netinbufplc = (((*netinbufplc)+messleng+2)&(COMBUFSIZ-1));
+				if ((submessleng > 0) && (bufptr[0] >= 200)) //200-255 are messages for engine's use only
+				{
+					if (bufptr[0] >= 253)
+					{
+						processreservedmessage(submessleng,bufptr);
+						if ((bufptr[0] == 253) || (bufptr[0] == 254)) return(0);
+					}
+					return(submessleng);
+				}
+			}
+			if (*otherconnectindex == myconnectindex) return(0);
+			return(submessleng);   //Got good packet
+		}
+
+		syncstate++;   //DON'T WANT TO GET HERE!!!
+			//Increment inbufplc to make it not continuously screw up!
+		*netinbufplc = (((*netinbufplc)+messleng+2)&(COMBUFSIZ-1));
+	}
+	return(0);
+}
+
+void initcrc(void)
+{
+	long i, j, k, a;
+
+	for(j=0;j<256;j++)      //Calculate CRC table
+	{
+		k = (j<<8); a = 0;
+		for(i=7;i>=0;i--)
+		{
+			if (((k^a)&0x8000) > 0)
+				a = ((a<<1)&65535) ^ 0x1021;   //0x1021 = genpoly
+			else
+				a = ((a<<1)&65535);
+			k = ((k<<1)&65535);
+		}
+		crctable[j] = (a&65535);
+	}
+}
+
+long getcrc(char *buffer, short bufleng)
+{
+	long i, j;
+
+	j = 0;
+	for(i=bufleng-1;i>=0;i--) updatecrc16(j,buffer[i]);
+	return(j&65535);
+}
+
+void installbicomhandlers(void)
+{
+#ifdef PLATFORM_DOS	
+	union REGS r;
+	struct SREGS sr;
+	long lowp;
+	void far *fh;
+
+		//Get old protected mode handler
+	r.x.eax = 0x3500+comvect;   /* DOS get vector (INT 0Ch) */
+	sr.ds = sr.es = 0;
+	int386x(0x21,&r,&r,&sr);
+	orig_pm_sel = (unsigned short)sr.es;
+	orig_pm_off = r.x.ebx;
+
+		//Get old real mode handler
+	r.x.eax = 0x0200;   /* DPMI get real mode vector */
+	r.h.bl = comvect;
+	int386(0x31,&r,&r);
+	orig_rm_seg = (unsigned short)r.x.ecx;
+	orig_rm_off = (unsigned short)r.x.edx;
+
+		//Allocate memory in low memory to store real mode handler
+	if ((lowp = convalloc32(COMCODEBYTES+(COMBUFSIZ<<1))) == 0)
+		{ printf("Can't allocate conventional memory.\n"); exit; }
+
+	inbufplc = (short *)(lowp+0);
+	inbufend = (short *)(lowp+2);
+	outbufplc = (short *)(lowp+4);
+	outbufend = (short *)(lowp+6);
+	comport = (short *)(lowp+8);
+	comtype = (char *)(lowp+10);
+	comerror = (char *)(lowp+11);
+	comresend = (char *)(lowp+12);
+	incnt = (char *)(lowp+13);
+	inbuf = (char *)(lowp+COMCODEBYTES);
+	outbuf = (char *)(lowp+COMCODEBYTES+COMBUFSIZ);
+
+	memcpy((void *)lowp,(void *)rmbuffer,COMCODEBYTES);
+
+		//Set new protected mode handler
+	r.x.eax = 0x2500+comvect;   /* DOS set vector (INT 0Ch) */
+	fh = (void far *)comhandler;
+	r.x.edx = FP_OFF(fh);
+	sr.ds = FP_SEG(fh);      //DS:EDX == &handler
+	sr.es = 0;
+	int386x(0x21,&r,&r,&sr);
+
+		//Set new real mode handler (must be after setting protected mode)
+	r.x.eax = 0x0201;
+	r.h.bl = comvect;              //CX:DX == real mode &handler
+	r.x.ecx = ((lowp>>4)&0xffff);  //D32realseg
+	r.x.edx = COMCODEOFFS;         //D32realoff
+	int386(0x31,&r,&r);
+#else
+	fprintf (stderr,"%s, line %d; installbicomhandlers() called\n",
+		__FILE__, __LINE__);
+#endif	
+}
+
+void uninstallbicomhandlers(void)
+{
+#ifdef PLATFORM_DOS	
+	union REGS r;
+	struct SREGS sr;
+
+		//restore old protected mode handler
+	r.x.eax = 0x2500+comvect;   /* DOS set vector (INT 0Ch) */
+	r.x.edx = orig_pm_off;
+	sr.ds = orig_pm_sel;    /* DS:EDX == &handler */
+	sr.es = 0;
+	int386x(0x21,&r,&r,&sr);
+
+		//restore old real mode handler
+	r.x.eax = 0x0201;   /* DPMI set real mode vector */
+	r.h.bl = comvect;
+	r.x.ecx = (unsigned long)orig_rm_seg;     //CX:DX == real mode &handler
+	r.x.edx = (unsigned long)orig_rm_off;
+	int386(0x31,&r,&r);
+#else
+	fprintf (stderr, "%s line %d; uninstallbicomhandlers() called\n",
+		__FILE__, __LINE__);	
+#endif	
+}
+
+void processreservedmessage(short tempbufleng, char *datempbuf)
+{
+	long i, j, k, daotherconnectnum, templong;
+
+	switch(datempbuf[0])
+	{
+		//[253] (login, if myconnectnum's lowest, then respond with packet type 254)
+		case 253:
+			if (multioption < 5)
+			{
+				otherconnectnum = ((long)datempbuf[1])+(((long)datempbuf[2])<<8)+(((long)datempbuf[3])<<16)+(((long)datempbuf[4])<<24);
+
+				datempbuf[0] = 254;
+				sendpacket(-1,datempbuf,1);
+
+				myconnectindex = 0;
+				connecthead = 0; connectpoint2[0] = 1; connectpoint2[1] = -1;
+				numplayers = 2;
+			}
+			else if (multioption >= 5)
+			{
+				daotherconnectnum = ((long)datempbuf[1])+((long)(datempbuf[2]<<8))+((long)(datempbuf[3]<<16))+((long)(datempbuf[4]<<24));
+				if (daotherconnectnum != myconnectnum)
+				{
+					netinitconnection(daotherconnectnum,&datempbuf[5]);
+
+					if ((myconnectindex == connecthead) || ((connectnum[connecthead] == daotherconnectnum) && (myconnectindex == connectpoint2[connecthead])))
+					{
+						datempbuf[0] = 254;
+						j = 1;
+						for(i=0;i<MAXPLAYERS;i++)
+							if ((connectnum[i] != 0x7fffffff) && (connectnum[i] != daotherconnectnum))
+							{
+								datempbuf[j++] = (connectnum[i]&255);
+								datempbuf[j++] = ((connectnum[i]>>8)&255);
+								datempbuf[j++] = ((connectnum[i]>>16)&255);
+								datempbuf[j++] = ((connectnum[i]>>24)&255);
+
+								for(k=0;k<10;k++)
+									datempbuf[j++] = compaddr[i][k];
+							}
+
+							//While this doesn't have to be a broadcast, sending
+							//this info again makes good error correction
+						sendpacket(-1,datempbuf,j);
+
+						for(i=0;i<MAXPLAYERS;i++)
+							if (connectnum[i] == daotherconnectnum)
+							{
+								sendpacket((short)i,datempbuf,j);
+								break;
+							}
+					}
+				}
+			}
+			break;
+		case 254:  //[254][connectnum][connectnum]...(Packet type 253 response)
+			if (multioption < 5)
+			{
+				myconnectindex = 1;
+				connecthead = 0; connectpoint2[0] = 1; connectpoint2[1] = -1;
+				numplayers = 2;
+			}
+			else if (multioption >= 5)
+			{
+				j = 1;
+				while (j < tempbufleng)
+				{
+					templong = ((long)datempbuf[j])+((long)(datempbuf[j+1]<<8))+((long)(datempbuf[j+2]<<16))+((long)(datempbuf[j+3]<<24));
+					netinitconnection(templong,&datempbuf[j+4]);
+					j += 14;
+				}
+			}
+			break;
+		case 255:
+			if (multioption >= 5)
+				netuninitconnection(datempbuf[1]);
+			break;
+	}
+}
+
+void sendlogon(void)
+{
+	long i;
+	char tempbuf[16];
+
+	if (multioption <= 0)
+		return;
+
+	tempbuf[0] = 253;
+	if (multioption < 5)
+	{
+		tempbuf[1] = kinp(0x40);
+		tempbuf[2] = kinp(0x40);
+		tempbuf[3] = kinp(0x40);
+		tempbuf[4] = mypriority;
+		myconnectnum = ((long)tempbuf[1])+(((long)tempbuf[2])<<8)+(((long)tempbuf[3])<<16)+(((long)mypriority)<<24);
+		sendpacket(-1,tempbuf,5);
+	}
+	else
+	{
+		tempbuf[1] = (myconnectnum&255);
+		tempbuf[2] = ((myconnectnum>>8)&255);
+		tempbuf[3] = ((myconnectnum>>16)&255);
+		tempbuf[4] = ((myconnectnum>>24)&255);
+		for(i=0;i<10;i++)
+			tempbuf[i+5] = mycompaddr[i];
+
+		sendpacket(-1,tempbuf,15);
+	}
+}
+
+void sendlogoff(void)
+{
+	char tempbuf[16];
+	long i;
+
+	if ((numplayers <= 1) || (multioption <= 0)) return;
+
+	tempbuf[0] = 255;
+	if (multioption < 5)
+	{
+		sendpacket(-1,tempbuf,1);
+	}
+	else
+	{
+		tempbuf[1] = myconnectindex;
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if (i != myconnectindex)
+				sendpacket(i,tempbuf,2);
+	}
+}
+
+int getoutputcirclesize(void)
+{
+	if ((multioption >= 1) && (multioption <= 4))
+	{
+		startcom();
+		return(((*outbufend)-(*outbufplc)+COMBUFSIZ)&(COMBUFSIZ-1));
+	}
+	return(0);
+}
+
+int setsocket(short newsocket)
+{
+	long i;
+
+	if (multioption < 5)
+	{
+		socket = newsocket;
+		return(0);
+	}
+
+	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket
+
+	socket = newsocket;
+
+	simulateint(0x7a,0L,(long)0x1,0L,(long)socket,0L,0L);                             //Closesocket
+	if ((simulateint(0x7a,(long)0xff,0L,0L,(long)socket,0L,0L)&255) != 0) return(-2); //Opensocket
+	mycompaddr[10] = (socket&255);
+	mycompaddr[11] = (socket>>8);
+	ecbget[10] = (socket&255);
+	ecbget[11] = (socket>>8);
+	for(i=0;i<MAXPLAYERS;i++)
+	{
+		compaddr[i][10] = (socket&255);
+		compaddr[i][11] = (socket>>8);
+	}
+	return(0);
+}
--- /dev/null
+++ b/Engine/src/platform.h
@@ -1,0 +1,46 @@
+#ifndef _INCLUDE_PLATFORM_H_
+#define _INCLUDE_PLATFORM_H_
+
+#if (defined PLATFORM_WIN32)
+#include "win32_compat.h"
+#elif (defined PLATFORM_UNIX)
+#include "unix_compat.h"
+#elif (defined PLATFORM_DOS)
+#include "doscmpat.h"
+#else
+#error Define your platform!
+#endif
+
+#if (!defined __EXPORT__)
+#define __EXPORT__
+#endif
+
+#if ((defined PLATFORM_SUPPORTS_SDL) && (!defined PLATFORM_TIMER_HZ))
+#define PLATFORM_TIMER_HZ 100
+#endif
+
+#if (!defined PLATFORM_TIMER_HZ)
+#error You need to define PLATFORM_TIMER_HZ for your platform.
+#endif
+
+#if (defined __WATCOMC__)
+#define snprintf _snprintf
+#endif
+
+unsigned short _swap16(unsigned short D);
+unsigned int _swap32(unsigned int D);
+#if PLATFORM_MACOSX
+#define PLATFORM_BIGENDIAN 1
+#define BUILDSWAP_INTEL16(x) _swap16(x)
+#define BUILDSWAP_INTEL32(x) _swap32(x)
+#else
+#define PLATFORM_LITTLEENDIAN 1
+#define BUILDSWAP_INTEL16(x) (x)
+#define BUILDSWAP_INTEL32(x) (x)
+#endif
+
+#endif  /* !defined _INCLUDE_PLATFORM_H_ */
+
+/* end of platform.h ... */
+
+
--- /dev/null
+++ b/Engine/src/pragmas.c
@@ -1,0 +1,59 @@
+// converted from asm to c by Jonof
+
+#include <stdio.h>
+#include "platform.h"
+#include "pragmas.h"
+
+unsigned long getkensmessagecrc(long param) {
+    return(0x56c764d4);
+}
+
+
+void clearbuf(void *d, long c, long a)
+{
+	long *p = (long*)d;
+	while ((c--) > 0) *(p++) = a;
+}
+
+void clearbufbyte(void *D, long c, long a)
+{ // Cringe City
+	char *p = (char*)D;
+	long m[4] = { 0xffl,0xff00l,0xff0000l,0xff000000l };
+	long n[4] = { 0,8,16,24 };
+	long z=0;
+	while ((c--) > 0) {
+		*(p++) = (char)((a & m[z])>>n[z]);
+		z=(z+1)&3;
+	}
+}
+
+void copybuf(void *s, void *d, long c)
+{
+	long *p = (long*)s, *q = (long*)d;
+	while ((c--) > 0) *(q++) = *(p++);
+}
+
+void copybufbyte(void *S, void *D, long c)
+{
+	char *p = (char*)S, *q = (char*)D;
+	while((c--) > 0) *(q++) = *(p++);
+}
+
+void copybufreverse(void *S, void *D, long c)
+{
+	char *p = (char*)S, *q = (char*)D;
+	while((c--) > 0) *(q++) = *(p--);
+}
+
+void qinterpolatedown16(long bufptr, long num, long val, long add)
+{ // gee, I wonder who could have provided this...
+    long i, *lptr = (long *)bufptr;
+    for(i=0;i<num;i++) { lptr[i] = (val>>16); val += add; }
+}
+
+void qinterpolatedown16short(long bufptr, long num, long val, long add)
+{ // ...maybe the same person who provided this too?
+    long i; short *sptr = (short *)bufptr;
+    for(i=0;i<num;i++) { sptr[i] = (short)(val>>16); val += add; }
+}
+
--- /dev/null
+++ b/Engine/src/pragmas.h
@@ -1,0 +1,165 @@
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file has been modified from Ken Silverman's original release
+ */
+
+#ifndef __PRAGMAS_H__
+#define __PRAGMAS_H__
+
+#include "platform.h" //for __int64
+
+static __inline void swapchar(unsigned char *p1, unsigned char *p2)
+{ unsigned char tmp = *p1; *p1 = *p2; *p2 = tmp; }
+static __inline void swapshort(short *p1, short *p2)
+{ short tmp = *p1; *p1 = *p2; *p2 = tmp; }
+static __inline void swaplong(long *p1, long *p2)
+{ long tmp = *p1; *p1 = *p2; *p2 = tmp; }
+static __inline void swapchar2(unsigned char *p1, unsigned char *p2, int xsiz)
+{
+    swapchar(p1, p2);
+    swapchar(p1 + 1, p2 + xsiz);
+}
+
+
+unsigned long getkensmessagecrc(long param);
+
+static __inline long msqrtasm(unsigned long c)
+{
+	unsigned long a,b;
+
+	a = 0x40000000l;		// mov eax, 0x40000000
+	b = 0x20000000l;		// mov ebx, 0x20000000
+	do {				// begit:
+		if (c >= a) {		// cmp ecx, eax	 /  jl skip
+			c -= a;		// sub ecx, eax
+			a += b*4;	// lea eax, [eax+ebx*4]
+		}			// skip:
+		a -= b;			// sub eax, ebx
+		a >>= 1;		// shr eax, 1
+		b >>= 2;		// shr ebx, 2
+	} while (b);			// jnz begit
+	if (c >= a)			// cmp ecx, eax
+		a++;			// sbb eax, -1
+	a >>= 1;			// shr eax, 1
+	return a;
+}
+
+void vlin16first (long i1, long i2);
+
+static __inline int sqr (int input1) { return input1*input1; }
+
+/* internal use:32x32 = 64bit */
+static __inline __int64 mul32_64(int i1,int i2)
+{
+	return (__int64)i1*i2;
+}
+static __inline int scale (int input1, int input2, int input3)
+{
+	return mul32_64(input1,input2)/input3;
+}
+static __inline int mulscale (int input1, int input2, int input3)
+{
+	return mul32_64(input1,input2)>>input3;
+}
+static __inline int dmulscale  (int input1, int input2, int input3,int input4,int input5)
+{
+	return (mul32_64(input1,input2) + mul32_64(input3,input4))>>input5;
+}
+static __inline int tmulscale(int i1, int i2, int i3, int i4, int i5, int i6,int shift)
+{
+	return (mul32_64(i1,i2) + mul32_64(i3,i4) + mul32_64(i5,i6))>>shift;
+}
+static __inline int divscale(int i1, int i2, int i3)
+{
+	return ((__int64)i1<<i3)/i2;
+}
+
+#define DEFFUNCS \
+DEFFUN(1)\
+DEFFUN(2)\
+DEFFUN(3)\
+DEFFUN(4)\
+DEFFUN(5)\
+DEFFUN(6)\
+DEFFUN(7)\
+DEFFUN(8)\
+DEFFUN(9)\
+DEFFUN(10)\
+DEFFUN(11)\
+DEFFUN(12)\
+DEFFUN(13)\
+DEFFUN(14)\
+DEFFUN(15)\
+DEFFUN(16)\
+DEFFUN(17)\
+DEFFUN(18)\
+DEFFUN(19)\
+DEFFUN(20)\
+DEFFUN(21)\
+DEFFUN(22)\
+DEFFUN(23)\
+DEFFUN(24)\
+DEFFUN(25)\
+DEFFUN(26)\
+DEFFUN(27)\
+DEFFUN(28)\
+DEFFUN(29)\
+DEFFUN(30)\
+DEFFUN(31)\
+DEFFUN(32)
+
+#define DEFFUN(N) \
+static __inline int mulscale##N(int input1, int input2) \
+{ return mulscale(input1,input2,N); }
+DEFFUNCS
+#undef DEFFUN
+
+#define DEFFUN(N) \
+static __inline int dmulscale##N(int input1, int input2,int input3,int input4) \
+{ return dmulscale(input1,input2,input3,input4,N); }
+DEFFUNCS
+#undef DEFFUN
+
+#define DEFFUN(N) \
+static __inline int tmulscale##N(int i1, int i2,int i3,int i4,int i5,int i6) \
+{ return tmulscale(i1,i2,i3,i4,i5,i6,N); }
+DEFFUNCS
+#undef DEFFUN
+
+#define DEFFUN(N) \
+static __inline int divscale##N(int input1, int input2) \
+{ return divscale(input1,input2,N); }
+DEFFUNCS
+#undef DEFFUN
+
+static __inline int ksgn(int i1)
+{
+  if (i1 < 0) return -1;
+  else if (i1 > 0) return 1;
+  else return 0;
+}
+
+static __inline int sgn(int i1) { return ksgn(i1); }
+static __inline int klabs (int i1)
+{
+  if (i1 < 0) i1 = -i1;
+  return i1;
+}
+static __inline int mul3 (int i1) { return i1*3; }
+static __inline int mul5 (int i1) { return i1*5; }
+static __inline int mul9 (int i1) { return i1*9; }
+
+void copybufreverse(void *S, void *D, long c);
+void copybuf(void *s, void *d, long c);
+void clearbuf(void *d, long c, long a);
+void clearbufbyte(void *D, long c, long a);
+void copybufbyte(void *S, void *D, long c);
+
+void qinterpolatedown16 (long bufptr, long num, long val, long add);
+void qinterpolatedown16short (long bufptr, long num, long val, long add);
+
+#endif /* !defined _INCLUDE_PRAGMAS_H_ */
+
+
--- /dev/null
+++ b/Engine/src/sdl_driver.c
@@ -1,0 +1,2429 @@
+/*
+ * An SDL replacement for BUILD's VESA code.
+ *
+ *  Written by Ryan C. Gordon. (icculus@clutteredmind.org)
+ *
+ * Please do NOT harrass Ken Silverman about any code modifications
+ *  (including this file) to BUILD.
+ */
+
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file IS NOT A PART OF Ken Silverman's original release
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include "platform.h"
+
+#if (!defined PLATFORM_SUPPORTS_SDL)
+#error This platform apparently does not use SDL. Do not compile this.
+#endif
+
+#include "SDL.h"
+#include "build.h"
+#include "display.h"
+#include "pragmas.h"
+#include "engine.h"
+#include "engine_protos.h"
+
+#include "mmulti_unstable.h"
+#include "mmulti_stable.h"
+
+#include "icon.h"
+
+/**/
+/*
+void (*callcommit)(void);
+void (*initcrc)(void);
+long (*getcrc)(char *buffer, short bufleng);
+void (*initmultiplayers)(char damultioption, char dacomrateoption, char dapriority);
+void (*sendpacket)(long other, char *bufptr, long messleng);
+void (*setpackettimeout)(long datimeoutcount, long daresendagaincount);
+void (*uninitmultiplayers)(void);
+void (*sendlogon)(void);
+void (*sendlogoff)(void);
+int  (*getoutputcirclesize)(void);
+void (*setsocket)(short newsocket);
+short (*getpacket)(short *other, char *bufptr);
+void (*flushpackets)(void);
+void (*genericmultifunction)(long other, char *bufptr, long messleng, long command);
+*/
+/**/
+
+void Setup_UnstableNetworking();
+void Setup_StableNetworking();
+
+int nNetMode = 0;
+
+#pragma message ( "[Fix this horrible networking mess. Function pointers not happy]" )
+// I do not like this one bit.
+// Figure out what was causing the problems with the function pointers.
+// This mess is a direct result of my lack of time.. bleh
+// This mess shouldn't even be in this file. /slap /slap
+void callcommit(void)
+{
+	switch(nNetMode)
+	{
+	case 0:		
+		unstable_callcommit();
+		break;
+	case 1:
+		stable_callcommit();
+		break;		
+	}
+}
+void initcrc(void)
+{
+	switch(nNetMode)
+	{
+	case 0:	
+		unstable_initcrc();
+		break;
+	case 1:	
+		stable_initcrc();
+		break;
+	}
+}
+long getcrc(char *buffer, short bufleng)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		return unstable_getcrc(buffer, bufleng);
+	case 1:
+		return stable_getcrc(buffer, bufleng);
+	}
+
+	return 0;
+}
+void initmultiplayers(char damultioption, char dacomrateoption, char dapriority)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_initmultiplayers(damultioption, dacomrateoption, dapriority);
+		break;
+	case 1:
+		stable_initmultiplayers(damultioption, dacomrateoption, dapriority);
+		break;
+	}
+}
+void sendpacket(long other, char *bufptr, long messleng)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_sendpacket(other, bufptr, messleng);
+		break;
+	case 1:
+		stable_sendpacket(other, bufptr, messleng);
+		break;
+	}
+}
+void setpackettimeout(long datimeoutcount, long daresendagaincount)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_setpackettimeout(datimeoutcount, daresendagaincount);
+		break;
+	case 1:
+		stable_setpackettimeout(datimeoutcount, daresendagaincount);
+		break;
+	}
+
+}
+void uninitmultiplayers(void)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_uninitmultiplayers();
+		break;
+	case 1:
+		stable_uninitmultiplayers();
+		break;
+	}
+}
+void sendlogon(void)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_sendlogon();
+		break;
+	case 1:
+		unstable_sendlogon();
+		break;
+	}
+}
+void sendlogoff(void)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_sendlogoff();
+		break;
+	case 1:
+		stable_sendlogoff();
+		break;
+	}
+}
+int  getoutputcirclesize(void)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		return unstable_getoutputcirclesize();
+	case 1:
+		return stable_getoutputcirclesize();		
+	}
+
+	return 0;
+}
+void setsocket(short newsocket)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_setsocket(newsocket);
+		break;
+	case 1:
+		stable_setsocket(newsocket);
+		break;
+	}
+}
+short getpacket(short *other, char *bufptr)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		return unstable_getpacket(other, bufptr); // default
+	case 1:
+		return stable_getpacket(other, bufptr);
+	}
+
+	return 0;
+}
+void flushpackets(void)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_flushpackets();
+		break;
+	case 1:
+		stable_flushpackets();
+		break;
+	}
+}
+void genericmultifunction(long other, char *bufptr, long messleng, long command)
+{
+	switch(nNetMode)
+	{
+	case 0:
+		unstable_genericmultifunction(other, bufptr, messleng, command);
+		break;
+	case 1:
+		stable_genericmultifunction(other, bufptr, messleng, command);
+		break;
+	}
+}
+
+#if (defined USE_OPENGL)
+#include "buildgl.h"
+#endif
+
+#if ((defined PLATFORM_WIN32))
+#include <windows.h>
+#endif
+
+typedef enum
+{
+    RENDERER_SOFTWARE,
+    RENDERER_OPENGL3D,
+    RENDERER_TOTAL
+} sdl_renderer_type;
+
+const char *renderer_name[RENDERER_TOTAL];
+
+#define ENVRSTR_RENDERER_SOFTWARE  "software"
+#define ENVRSTR_RENDERER_OPENGL3D  "opengl3d"
+
+static sdl_renderer_type renderer = RENDERER_SOFTWARE;
+
+/* !!! ugh. Clean this up. */
+#if (!defined __WATCOMC__)
+#include "a.h"
+#else
+extern long setvlinebpl(long);
+#pragma aux setvlinebpl parm [eax];
+#endif  /* __WATCOMC__ */
+
+#include "cache1d.h"
+
+
+/*
+ * !!! remove the surface_end checks, for speed's sake. They are a
+ * !!!  needed safety right now. --ryan.
+ */
+
+
+#define DEFAULT_MAXRESWIDTH  1600
+#define DEFAULT_MAXRESHEIGHT 1200
+
+
+#define UNLOCK_SURFACE_AND_RETURN  if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface); return;
+
+
+int _argc = 0;
+char **_argv = NULL;
+
+    /* !!! move these elsewhere? */
+long xres, yres, bytesperline, frameplace, frameoffset, imageSize, maxpages;
+char *screen, vesachecked;
+long buffermode, origbuffermode, linearmode;
+char permanentupdate = 0, vgacompatible;
+
+SDL_Surface *surface = NULL; /* This isn't static so that we can use it elsewhere AH */
+static int debug_hall_of_mirrors = 0;
+static Uint32 sdl_flags = SDL_HWPALETTE;
+static long mouse_relative_x = 0;
+static long mouse_relative_y = 0;
+static short mouse_buttons = 0;
+static unsigned int lastkey = 0;
+static SDL_TimerID primary_timer = NULL;
+/* so we can make use of setcolor16()... - DDOI */
+static unsigned char drawpixel_color=0;
+
+static unsigned int scancodes[SDLK_LAST];
+
+static long last_render_ticks = 0;
+long total_render_time = 1;
+long total_rendered_frames = 0;
+
+static char *titlelong = NULL;
+static char *titleshort = NULL;
+
+void restore256_palette (void);
+void set16color_palette (void);
+
+static FILE *_sdl_debug_file = NULL;
+
+void set_sdl_renderer(void);
+
+static __inline void __out_sdldebug(const char *subsystem,
+                                  const char *fmt, va_list ap)
+{
+    fprintf(_sdl_debug_file, "%s: ", subsystem);
+    vfprintf(_sdl_debug_file, fmt, ap);
+    fprintf(_sdl_debug_file, "\n");
+    fflush(_sdl_debug_file);
+} /* __out_sdldebug */
+
+
+static void sdldebug(const char *fmt, ...)
+{
+    va_list ap;
+
+    if (_sdl_debug_file)
+    {
+        va_start(ap, fmt);
+        __out_sdldebug("BUILDSDL", fmt, ap);
+        va_end(ap);
+    } /* if */
+} /* sdldebug */
+
+
+#if (defined USE_OPENGL)
+void sgldebug(const char *fmt, ...)
+{
+    va_list ap;
+
+    if (_sdl_debug_file)
+    {
+        va_start(ap, fmt);
+        __out_sdldebug("BUILDSDL/GL", fmt, ap);
+        va_end(ap);
+    } /* if */
+} /* sgldebug */
+#endif
+
+
+static void __append_sdl_surface_flag(SDL_Surface *_surface, char *str,
+                                      size_t strsize, Uint32 flag,
+                                      const char *flagstr)
+{
+    if (_surface->flags & flag)
+    {
+        if ( (strlen(str) + strlen(flagstr)) >= (strsize - 1) )
+            strcpy(str + (strsize - 5), " ...");
+        else
+            strcat(str, flagstr);
+    } /* if */
+} /* append_sdl_surface_flag */
+
+
+#define append_sdl_surface_flag(a, b, c, fl) __append_sdl_surface_flag(a, b, c, fl, " " #fl)
+#define print_tf_state(str, val) sdldebug("%s: {%s}", str, (val) ? "true" : "false" )
+
+static void output_surface_info(SDL_Surface *_surface)
+{
+    const SDL_VideoInfo *info;
+    char f[256];
+
+    if (!_sdl_debug_file)
+        return;
+
+    if (_surface == NULL)
+    {
+        sdldebug("-WARNING- You've got a NULL screen surface!");
+    }
+    else
+    {
+        f[0] = '\0';
+        sdldebug("screen surface is (%dx%dx%dbpp).",
+                _surface->w, _surface->h, _surface->format->BitsPerPixel);
+
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_SWSURFACE);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_HWSURFACE);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_ASYNCBLIT);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_ANYFORMAT);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_HWPALETTE);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_DOUBLEBUF);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_FULLSCREEN);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_OPENGL);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_OPENGLBLIT);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_RESIZABLE);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_NOFRAME);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_HWACCEL);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_SRCCOLORKEY);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_RLEACCELOK);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_RLEACCEL);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_SRCALPHA);
+        append_sdl_surface_flag(_surface, f, sizeof (f), SDL_PREALLOC);
+
+        if (f[0] == '\0')
+            strcpy(f, " (none)");
+
+        sdldebug("New vidmode flags:%s", f);
+
+        info = SDL_GetVideoInfo();
+        assert(info != NULL);
+
+        print_tf_state("hardware surface available", info->hw_available);
+        print_tf_state("window manager available", info->wm_available);
+        print_tf_state("accelerated hardware->hardware blits", info->blit_hw);
+        print_tf_state("accelerated hardware->hardware colorkey blits", info->blit_hw_CC);
+        print_tf_state("accelerated hardware->hardware alpha blits", info->blit_hw_A);
+        print_tf_state("accelerated software->hardware blits", info->blit_sw);
+        print_tf_state("accelerated software->hardware colorkey blits", info->blit_sw_CC);
+        print_tf_state("accelerated software->hardware alpha blits", info->blit_sw_A);
+        print_tf_state("accelerated color fills", info->blit_fill);
+
+        sdldebug("video memory: (%d)", info->video_mem);
+    } /* else */
+} /* output_surface_info */
+
+
+static void output_driver_info(void)
+{
+    char buffer[256];
+
+    if (!_sdl_debug_file)
+        return;
+
+    sdldebug("Using BUILD renderer \"%s\".", renderer_name[renderer]);
+
+    if (SDL_VideoDriverName(buffer, sizeof (buffer)) == NULL)
+    {
+        sdldebug("-WARNING- SDL_VideoDriverName() returned NULL!");
+    } /* if */
+    else
+    {
+        sdldebug("Using SDL video driver \"%s\".", buffer);
+    } /* else */
+} /* output_driver_info */
+
+
+static Uint8 *get_framebuffer(void)
+{
+    assert(renderer != RENDERER_OPENGL3D);
+
+    if (renderer == RENDERER_SOFTWARE)
+        return((Uint8 *) surface->pixels);
+    else if (renderer == RENDERER_OPENGL3D)
+        return((Uint8 *) frameplace);
+
+    return(NULL);
+} /* get_framebuffer */
+
+
+int using_opengl(void)
+{
+    return(renderer == RENDERER_OPENGL3D);
+} /* using_opengl */
+
+
+/*
+ * !!! This is almost an entire copy of the original setgamemode().
+ * !!!  Figure out what is needed for just 2D mode, and separate that
+ * !!!  out. Then, place the original setgamemode() back into engine.c,
+ * !!!  and remove our simple implementation (and this function.)
+ * !!!  Just be sure to keep the non-DOS things, like the window's
+ * !!!  titlebar caption.   --ryan.
+ */
+static char screenalloctype = 255;
+static void init_new_res_vars(int davidoption)
+{
+    int i = 0;
+    int j = 0;
+
+    setupmouse();
+
+    SDL_WM_SetCaption(titlelong, titleshort);
+
+    xdim = xres = surface->w;
+    ydim = yres = surface->h;
+
+	printf("init_new_res_vars %d %d\n",xdim,ydim);
+
+    bytesperline = surface->w;
+    vesachecked = 1;
+    vgacompatible = 1;
+    linearmode = 1;
+	qsetmode = surface->h;
+	activepage = visualpage = 0;
+
+    if (renderer == RENDERER_OPENGL3D)
+        frameplace = (long) NULL;
+    else
+		// FIX_00083: Sporadic crash on fullscreen/window mode toggle
+		// frameoffset wasn't always updated fast enought. Build were using the old 
+		// pointer of frameoffset.  
+        frameoffset = frameplace = (long) ( ((Uint8 *) surface->pixels) );
+
+  	if (screen != NULL)
+   	{
+       	if (screenalloctype == 0) kkfree((void *)screen);
+   	    if (screenalloctype == 1) suckcache((long *)screen);
+   		screen = NULL;
+   	} /* if */
+
+	// FIX_00085: Optimized Video driver. FPS increases by +20%.
+//    if (davidoption != -1)
+//    {
+    	switch(vidoption)
+    	{
+    		case 1:i = xdim*ydim; break;
+    		case 2: xdim = 320; ydim = 200; i = xdim*ydim; break;
+    		case 6: xdim = 320; ydim = 200; i = 131072; break;
+    		default: assert(0);
+    	}
+    	j = ydim*4*sizeof(long);  /* Leave room for horizlookup&horizlookup2 */
+
+  //  	screenalloctype = 0;
+//	    if ((screen = (char *)kkmalloc(i+(j<<1))) == NULL)
+//    	{
+//	    	 allocache((long *)&screen,i+(j<<1),&permanentlock);
+//    		 screenalloctype = 1;
+//    	}
+
+        /* !!! FIXME: Should screen get allocated above if in opengl3d mode? */
+
+//        if (renderer == RENDERER_OPENGL3D)
+//            frameplace = (long) NULL;
+//        else
+//        {
+//            frameplace = FP_OFF(screen);
+//          	horizlookup = (long *)(frameplace+i);
+//           	horizlookup2 = (long *)(frameplace+i+j);
+//        } /* else */
+//    } /* if */
+		if(horizlookup)
+			free(horizlookup);
+
+		if(horizlookup2)
+			free(horizlookup2);
+		
+		horizlookup = (long*)malloc(j);
+		horizlookup2 = (long*)malloc(j);
+
+    j = 0;
+  	for(i = 0; i <= ydim; i++)
+    {
+        ylookup[i] = j;
+        j += bytesperline;
+    } /* for */
+
+   	horizycent = ((ydim*4)>>1);
+
+		/* Force drawrooms to call dosetaspect & recalculate stuff */
+	oxyaspect = oxdimen = oviewingrange = -1;
+
+	setvlinebpl(bytesperline);
+
+    //if (davidoption != -1)
+    //{
+    	setview(0L,0L,xdim-1,ydim-1);
+    //	clearallviews(0L);
+    //} /* if */
+
+	setbrightness((char) curbrightness, (unsigned char *) &palette[0]);
+
+	if (searchx < 0) { searchx = halfxdimen; searchy = (ydimen>>1); }
+} /* init_new_res_vars */
+
+
+static void go_to_new_vid_mode(int davidoption, int w, int h)
+{
+    getvalidvesamodes();
+    SDL_ClearError();
+    // don't do SDL_SetVideoMode if SDL_WM_SetIcon not called. See sdl doc for SDL_WM_SetIcon
+	surface = SDL_SetVideoMode(w, h, 8, sdl_flags);
+    if (surface == NULL)
+    {
+		Error(EXIT_FAILURE,	"BUILDSDL: Failed to set %dx%d video mode!\n"
+							"BUILDSDL: SDL_Error() says [%s].\n",
+							w, h, SDL_GetError());
+	} /* if */
+
+    output_surface_info(surface);
+    init_new_res_vars(davidoption); // dont be confused between vidoption (global) and davidoption
+}
+
+static __inline int sdl_mouse_button_filter(SDL_MouseButtonEvent const *event)
+{
+        /*
+         * What bits BUILD expects:
+         *  0 left button pressed if 1
+         *  1 right button pressed if 1
+         *  2 middle button pressed if 1
+         *
+         *   (That is, this is what Int 33h (AX=0x05) returns...)
+         *
+         *  additionally bits 3&4 are set for the mouse wheel
+         */
+    Uint8 button = event->button;
+    if (button >= sizeof (mouse_buttons) * 8)
+        return(0);
+
+    if (button == SDL_BUTTON_RIGHT)
+        button = SDL_BUTTON_MIDDLE;
+    else if (button == SDL_BUTTON_MIDDLE)
+        button = SDL_BUTTON_RIGHT;
+
+    if (((const SDL_MouseButtonEvent*)event)->state)
+        mouse_buttons |= 1<<(button-1);
+    else if (button != 4 && button != 5)
+        mouse_buttons ^= 1<<(button-1);
+#if 0
+    Uint8 bmask = SDL_GetMouseState(NULL, NULL);
+    mouse_buttons = 0;
+    if (bmask & SDL_BUTTON_LMASK) mouse_buttons |= 1;
+    if (bmask & SDL_BUTTON_RMASK) mouse_buttons |= 2;
+    if (bmask & SDL_BUTTON_MMASK) mouse_buttons |= 4;
+#endif
+
+    return(0);
+} /* sdl_mouse_up_filter */
+
+
+static int sdl_mouse_motion_filter(SDL_Event const *event)
+{
+	static int i = 0;
+
+    if (surface == NULL)
+		return(0);
+
+    if (event->type == SDL_JOYBALLMOTION)
+    {
+        mouse_relative_x = event->jball.xrel/100;
+        mouse_relative_y = event->jball.yrel/100;
+    }
+    else
+    {
+			if (SDL_WM_GrabInput(SDL_GRAB_QUERY)==SDL_GRAB_ON) 
+			{
+				mouse_relative_x += event->motion.xrel;
+	       	    mouse_relative_y += event->motion.yrel;
+				//printf("sdl_mouse_motion_filter: mrx=%d, mry=%d, mx=%d, my=%d\n",
+				//	mouse_relative_x, mouse_relative_y, event->motion.xrel, event->motion.yrel);
+				
+				// mouse_relative_* is already reset in 
+				// readmousexy(). It must not be
+				// reset here because calling this function does not mean
+				// we always handle the mouse. 
+				// FIX_00001: Mouse speed is uneven and slower in windowed mode vs fullscreen mode.
+			}
+			else
+				mouse_relative_x = mouse_relative_y = 0;
+	}
+
+    return(0);
+} /* sdl_mouse_motion_filter */
+
+
+    /*
+     * The windib driver can't alert us to the keypad enter key, which
+     *  Ken's code depends on heavily. It sends it as the same key as the
+     *  regular return key. These users will have to hit SHIFT-ENTER,
+     *  which we check for explicitly, and give the engine a keypad enter
+     *  enter event.
+     */
+static __inline int handle_keypad_enter_hack(const SDL_Event *event)
+{
+    static int kp_enter_hack = 0;
+    int retval = 0;
+
+    if (event->key.keysym.sym == SDLK_RETURN)
+    {
+        if (event->key.state == SDL_PRESSED)
+        {
+            if (event->key.keysym.mod & KMOD_SHIFT)
+            {
+                kp_enter_hack = 1;
+                lastkey = scancodes[SDLK_KP_ENTER];
+                retval = 1;
+            } /* if */
+        } /* if */
+
+        else  /* key released */
+        {
+            if (kp_enter_hack)
+            {
+                kp_enter_hack = 0;
+                lastkey = scancodes[SDLK_KP_ENTER];
+                retval = 1;
+            } /* if */
+        } /* if */
+    } /* if */
+
+    return(retval);
+} /* handle_keypad_enter_hack */
+
+void fullscreen_toggle_and_change_driver(void)
+{
+	
+//  FIX_00002: New Toggle Windowed/FullScreen system now simpler and will 
+//  dynamically change for Windib or Directx driver. Windowed/Fullscreen 
+//  toggle also made available from menu.
+//  Replace attempt_fullscreen_toggle(SDL_Surface **surface, Uint32 *flags)
+  	
+	long int x,y;
+	x = surface->w;
+	y = surface->h;
+
+	BFullScreen =!BFullScreen;
+	SDL_QuitSubSystem(SDL_INIT_VIDEO);
+	_platform_init(0, NULL, "Duke Nukem 3D", "Duke3D");
+	_setgamemode(ScreenMode,x,y);
+	vscrn();
+
+	return;
+}
+
+static int sdl_key_filter(const SDL_Event *event)
+{
+    int extended;
+
+    if ( (event->key.keysym.sym == SDLK_m) &&
+         (event->key.state == SDL_PRESSED) &&
+         (event->key.keysym.mod & KMOD_CTRL) )
+    {
+
+
+		// FIX_00005: Mouse pointer can be toggled on/off (see mouse menu or use CTRL-M)
+		// This is usefull to move the duke window when playing in window mode.
+  
+        if (SDL_WM_GrabInput(SDL_GRAB_QUERY)==SDL_GRAB_ON) 
+		{
+            SDL_WM_GrabInput(SDL_GRAB_OFF);
+			SDL_ShowCursor(1);
+		}
+		else
+		{
+            SDL_WM_GrabInput(SDL_GRAB_ON);
+			SDL_ShowCursor(0);
+		}
+
+        return(0);
+    } /* if */
+
+    else if ( ( (event->key.keysym.sym == SDLK_RETURN) ||
+                (event->key.keysym.sym == SDLK_KP_ENTER) ) &&
+              (event->key.state == SDL_PRESSED) &&
+              (event->key.keysym.mod & KMOD_ALT) )
+    {	fullscreen_toggle_and_change_driver();
+
+		// hack to discard the ALT key...
+		lastkey=scancodes[SDLK_RALT]>>8; // extended
+		keyhandler();
+		lastkey=(scancodes[SDLK_RALT]&0xff)+0x80; // Simulating Key up
+		keyhandler();
+		lastkey=(scancodes[SDLK_LALT]&0xff)+0x80; // Simulating Key up (not extended)
+		keyhandler();
+		SDL_SetModState(KMOD_NONE); // SDL doesnt see we are releasing the ALT-ENTER keys
+        
+		return(0);					
+    }								
+
+    if (!handle_keypad_enter_hack(event))
+        lastkey = scancodes[event->key.keysym.sym];
+
+//	printf("key.keysym.sym=%d\n", event->key.keysym.sym);
+
+    if (lastkey == 0x0000)   /* No DOS equivalent defined. */
+        return(0);
+
+    extended = ((lastkey & 0xFF00) >> 8);
+    if (extended != 0)
+    {
+        lastkey = extended;
+        keyhandler();
+        lastkey = (scancodes[event->key.keysym.sym] & 0xFF);
+    } /* if */
+
+    if (event->key.state == SDL_RELEASED)
+        lastkey += 128;  /* +128 signifies that the key is released in DOS. */
+
+    keyhandler();
+    return(0);
+} /* sdl_key_filter */
+
+
+static int root_sdl_event_filter(const SDL_Event *event)
+{
+    switch (event->type)
+    {
+        case SDL_KEYUP:
+            // FIX_00003: Pause mode is now fully responsive - (Thx to Jonathon Fowler tips)
+			if(event->key.keysym.sym == SDLK_PAUSE)
+				break;
+        case SDL_KEYDOWN:
+            return(sdl_key_filter(event));
+        case SDL_JOYBUTTONDOWN:
+        case SDL_JOYBUTTONUP:
+            {
+                //Do Nothing
+
+                //printf("Joybutton UP/DOWN\n");
+	            //return(sdl_joystick_button_filter((const SDL_MouseButtonEvent*)event));
+                return 0;
+            }
+        case SDL_JOYBALLMOTION:
+        case SDL_MOUSEMOTION:
+            return(sdl_mouse_motion_filter(event));
+        case SDL_MOUSEBUTTONUP:
+        case SDL_MOUSEBUTTONDOWN:
+			return(sdl_mouse_button_filter((const SDL_MouseButtonEvent*)event));
+        case SDL_QUIT:
+            /* !!! rcg TEMP */
+            Error(EXIT_SUCCESS, "Exit through SDL\n"); 
+		default:
+			//printf("This event is not handled: %d\n",event->type);
+			break;
+    } /* switch */
+
+    return(1);
+} /* root_sdl_event_filter */
+
+
+static void handle_events(void)
+{
+    SDL_Event event;
+
+	while(SDL_PollEvent(&event))
+        root_sdl_event_filter(&event);
+} /* handle_events */
+
+
+/* bleh...public version... */
+void _handle_events(void)
+{
+    handle_events();
+} /* _handle_events */
+
+
+static SDL_Joystick *joystick = NULL;
+void _joystick_init(void)
+{
+    const char *envr = getenv(BUILD_SDLJOYSTICK);
+    int favored = 0;
+    int numsticks;
+    int i;
+
+    if (joystick != NULL)
+    {
+        sdldebug("Joystick appears to be already initialized.");
+        sdldebug("...deinitializing for stick redetection...");
+        _joystick_deinit();
+    } /* if */
+
+    if ((envr != NULL) && (strcmp(envr, "none") == 0))
+    {
+        sdldebug("Skipping joystick detection/initialization at user request");
+        return;
+    } /* if */
+
+    sdldebug("Initializing SDL joystick subsystem...");
+    sdldebug(" (export environment variable BUILD_SDLJOYSTICK=none to skip)");
+
+    if (SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_NOPARACHUTE) != 0)
+    {
+        sdldebug("SDL_Init(SDL_INIT_JOYSTICK) failed: [%s].", SDL_GetError());
+        return;
+    } /* if */
+
+    numsticks = SDL_NumJoysticks();
+    sdldebug("SDL sees %d joystick%s.", numsticks, numsticks == 1 ? "" : "s");
+    if (numsticks == 0)
+        return;
+
+    for (i = 0; i < numsticks; i++)
+    {
+        const char *stickname = SDL_JoystickName(i);
+        if ((envr != NULL) && (strcmp(envr, stickname) == 0))
+            favored = i;
+
+        sdldebug("Stick #%d: [%s]", i, stickname);
+    } /* for */
+
+    sdldebug("Using Stick #%d.", favored);
+    if ((envr == NULL) && (numsticks > 1))
+        sdldebug("Set BUILD_SDLJOYSTICK to one of the above names to change.");
+
+    joystick = SDL_JoystickOpen(favored);
+    if (joystick == NULL)
+    {
+        sdldebug("Joystick #%d failed to init: %s", favored, SDL_GetError());
+        return;
+    } /* if */
+
+    sdldebug("Joystick initialized. %d axes, %d buttons, %d hats, %d balls.",
+              SDL_JoystickNumAxes(joystick), SDL_JoystickNumButtons(joystick),
+              SDL_JoystickNumHats(joystick), SDL_JoystickNumBalls(joystick));
+
+    SDL_JoystickEventState(SDL_QUERY);
+} /* _joystick_init */
+
+
+void _joystick_deinit(void)
+{
+    if (joystick != NULL)
+    {
+        sdldebug("Closing joystick device...");
+        SDL_JoystickClose(joystick);
+        sdldebug("Joystick device closed. Deinitializing SDL subsystem...");
+        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+        sdldebug("SDL joystick subsystem deinitialized.");
+        joystick = NULL;
+    } /* if */
+} /* _joystick_deinit */
+
+
+int _joystick_update(void)
+{
+    if (joystick == NULL)
+        return(0);
+
+    SDL_JoystickUpdate();
+    return(1);
+} /* _joystick_update */
+
+
+int _joystick_axis(int axis)
+{
+    if (joystick == NULL)
+    {   
+        return(0);
+    }
+
+    return(SDL_JoystickGetAxis(joystick, axis));
+} /* _joystick_axis */
+
+int _joystick_hat(int hat)
+{
+    if (joystick == NULL)
+    {   
+        return(-1);
+    }
+
+    return(SDL_JoystickGetHat(joystick, hat));
+} /* _joystick_axis */
+
+int _joystick_button(int button)
+{
+    if (joystick == NULL)
+        return(0);
+
+    return(SDL_JoystickGetButton(joystick, button) != 0);
+} /* _joystick_button */
+
+
+unsigned char _readlastkeyhit(void)
+{
+    return(lastkey);
+} /* _readlastkeyhit */
+
+
+static __inline void init_debugging(void)
+{
+    const char *envr = getenv(BUILD_SDLDEBUG); 
+
+    debug_hall_of_mirrors = (getenv(BUILD_HALLOFMIRRORS) != NULL);
+
+    if (_sdl_debug_file != NULL)
+    {
+        fclose(_sdl_debug_file);
+        _sdl_debug_file = NULL;
+    } /* if */
+
+    if (envr != NULL)
+    {
+        if (strcmp(envr, "-") == 0)
+	    _sdl_debug_file = stdout;
+        else
+            _sdl_debug_file = fopen(envr, "w");
+
+        if (_sdl_debug_file == NULL)
+            printf("BUILDSDL: -WARNING- Could not open debug file!\n");
+        else
+            setbuf(_sdl_debug_file, NULL);
+    } /* if */
+} /* init_debugging */
+
+
+#if (!defined __DATE__)
+#define __DATE__ "a long, long time ago"
+#endif
+
+static __inline void output_sdl_versions(void)
+{
+    const SDL_version *linked_ver = SDL_Linked_Version();
+    SDL_version compiled_ver;
+
+    SDL_VERSION(&compiled_ver);
+
+    sdldebug("SDL display driver for the BUILD engine initializing.");
+    sdldebug("  sdl_driver.c by Ryan C. Gordon (icculus@clutteredmind.org).");
+    sdldebug("Compiled %s against SDL version %d.%d.%d ...", __DATE__,
+                compiled_ver.major, compiled_ver.minor, compiled_ver.patch);
+    sdldebug("Linked SDL version is %d.%d.%d ...",
+                linked_ver->major, linked_ver->minor, linked_ver->patch);
+} /* output_sdl_versions */
+
+
+static int in_vmware = 0;
+static __inline void detect_vmware(void)
+{
+#if 1
+    in_vmware = 0;  /* oh well. */
+#else
+    /* !!! need root access to touch i/o ports on Linux. */
+    #if (!defined __linux__)
+        in_vmware = (int) is_vmware_running();
+    #endif
+    sdldebug("vmWare %s running.", (in_vmware) ? "is" : "is not");
+#endif
+} /* detect_vmware */
+
+
+/* lousy -ansi flag.  :) */
+static char *string_dupe(const char *str)
+{
+    char *retval = malloc(strlen(str) + 1);
+    if (retval != NULL)
+        strcpy(retval, str);
+    return(retval);
+} /* string_dupe */
+
+
+void set_sdl_renderer(void)
+{
+    const char *envr = getenv(BUILD_RENDERER);
+
+#ifdef USE_OPENGL
+    int need_opengl_lib = 0;
+#endif
+
+    if ((envr == NULL) || (strcmp(envr, ENVRSTR_RENDERER_SOFTWARE) == 0))
+        renderer = RENDERER_SOFTWARE;
+
+#ifdef USE_OPENGL
+#if 0
+    else if (strcmp(envr, ENVRSTR_RENDERER_OPENGL3D) == 0)
+    {
+        renderer = RENDERER_OPENGL3D;
+        need_opengl_lib = 1;
+    } /* else if */
+#endif
+
+#endif
+
+    else
+    {
+        fprintf(stderr,
+                "BUILDSDL: \"%s\" in the %s environment var is not available.\n",
+                envr, BUILD_RENDERER);
+        _exit(1);
+    } /* else */
+
+    if (SDL_Init(SDL_INIT_VIDEO |SDL_INIT_NOPARACHUTE) == -1)
+    {
+		Error(EXIT_FAILURE, "BUILDSDL: SDL_Init() failed!\n"
+							"BUILDSDL: SDL_GetError() says \"%s\".\n", SDL_GetError());
+    } /* if */
+
+#ifdef USE_OPENGL
+    if (need_opengl_lib)
+    {
+        if (opengl_load_library() == -1)
+        {
+            SDL_Quit();
+            fprintf(stderr, "BUILDSDL/GL: Failed to load OpenGL library!\n");
+            if (getenv(BUILD_SDLDEBUG) == NULL)
+            {
+                fprintf(stderr, "BUILDSDL/GL: Try setting environment variable"
+                                " %s for more info.\n", BUILD_SDLDEBUG);
+            } /* if */
+            _exit(42);
+        } /* if */
+    } /* if */
+#endif
+
+} /* set_sdl_renderer */
+
+
+static void init_renderer_names(void)
+{
+    memset((void *) renderer_name, '\0', sizeof (renderer_name));
+    renderer_name[RENDERER_SOFTWARE] = "RENDERER_SOFTWARE";
+    renderer_name[RENDERER_OPENGL3D] = "RENDERER_OPENGL3D";
+} /* init_renderer_names */
+
+
+
+//#include "mmulti_stable.h"
+void Setup_UnstableNetworking()
+{
+	/*
+	callcommit = unstable_callcommit;
+	initcrc = unstable_initcrc;
+	getcrc = unstable_getcrc;
+	initmultiplayers = unstable_initmultiplayers;
+	sendpacket = unstable_sendpacket;
+	setpackettimeout = unstable_setpackettimeout;
+	uninitmultiplayers = unstable_uninitmultiplayers;
+	sendlogon = unstable_sendlogon;
+	sendlogoff = unstable_sendlogoff;
+	getoutputcirclesize = unstable_getoutputcirclesize;
+	setsocket = unstable_setsocket;
+	getpacket = unstable_getpacket;
+	flushpackets = unstable_flushpackets;
+	genericmultifunction = unstable_genericmultifunction;
+	*/
+	nNetMode = 0;
+}
+
+void Setup_StableNetworking()
+{
+	/*
+	callcommit = stable_callcommit;
+	initcrc = stable_initcrc;
+	getcrc = stable_getcrc;
+	initmultiplayers = stable_initmultiplayers;
+	sendpacket = stable_sendpacket;
+	setpackettimeout = stable_setpackettimeout;
+	uninitmultiplayers = stable_uninitmultiplayers;
+	sendlogon = stable_sendlogon;
+	sendlogoff = stable_sendlogoff;
+	getoutputcirclesize = stable_getoutputcirclesize;
+	setsocket = stable_setsocket;
+	getpacket = stable_getpacket;
+	flushpackets = stable_flushpackets;
+	genericmultifunction = stable_genericmultifunction;
+	*/
+
+	nNetMode = 1;
+}
+
+
+void _platform_init(int argc, char **argv, const char *title, const char *icon)
+{
+    int i;
+	long long timeElapsed;
+	char dummyString[4096];
+
+    _argc = argc;
+    _argv = argv;
+
+	// FIX_00061: "ERROR: Two players have the same random ID" too frequent cuz of internet windows times
+    QueryPerformanceCounter(&timeElapsed);
+	srand(timeElapsed&0xFFFFFFFF);
+
+	Setup_UnstableNetworking();
+
+    // Look through the command line args
+    for(i = 0; i < argc; i++)
+    {
+        if(argv[i][0] == '-' )
+        {
+            if(strcmpi(argv[i], "-netmode_stable") == 0)
+            {
+                //fullscreen = 1;
+				//TODO:
+#pragma message ( "[Todo: handle -netmode <int>]" )
+				Setup_StableNetworking();
+					
+            }
+        }
+    }
+
+	// Set up the correct renderer
+	// Becarfull setenv can't reach dll in VC++
+	// A way to proceed is to integrate the SDL libs
+	// in the exe instead.
+	
+    // FIX_00004: SDL.dll and SDL_Mixer.dll are now integrated within the exe
+    // (this also makes the Windib/Directx driver switching easier with SDL)		
+
+    // This requires to recompile the whole sdl and sdl mixer with the lib
+    // switch instead of the default dll switch.
+	if( BFullScreen )
+	{
+		putenv("SDL_VIDEODRIVER=directx");
+		printf("FullScreen Mode, trying directx\n");
+	}
+	else
+	{
+		putenv("SDL_VIDEODRIVER=windib");
+		printf("Window Mode, trying windib\n");
+	}
+
+	putenv("SDL_VIDEO_CENTERED=1");
+
+    init_renderer_names();
+
+    init_debugging();
+
+    if (title == NULL)
+        title = "BUILD";
+
+    if (icon == NULL)
+        icon = "BUILD";
+
+    titlelong = string_dupe(title);
+    titleshort = string_dupe(icon);
+
+    sdl_flags = BFullScreen ? SDL_FULLSCREEN : 0;
+
+    sdl_flags |= SDL_HWPALETTE;
+   
+//	// These flags cause duke to exit on fullscreen
+//	/*
+	if(!BFullScreen)
+	{
+	//	sdl_flags |= SDL_HWSURFACE;   //!!!
+	//	sdl_flags |= SDL_DOUBLEBUF; //
+	}
+//	*/
+	
+	
+
+    memset(scancodes, '\0', sizeof (scancodes));
+    scancodes[SDLK_ESCAPE]          = 0x01;
+    scancodes[SDLK_1]               = 0x02;
+    scancodes[SDLK_2]               = 0x03;
+    scancodes[SDLK_3]               = 0x04;
+    scancodes[SDLK_4]               = 0x05;
+    scancodes[SDLK_5]               = 0x06;
+    scancodes[SDLK_6]               = 0x07;
+    scancodes[SDLK_7]               = 0x08;
+    scancodes[SDLK_8]               = 0x09;
+    scancodes[SDLK_9]               = 0x0A;
+    scancodes[SDLK_0]               = 0x0B;
+    scancodes[SDLK_MINUS]           = 0x0C; /* was 0x4A */
+    scancodes[SDLK_EQUALS]          = 0x0D; /* was 0x4E */
+    scancodes[SDLK_BACKSPACE]       = 0x0E;
+    scancodes[SDLK_TAB]             = 0x0F;
+    scancodes[SDLK_q]               = 0x10;
+    scancodes[SDLK_w]               = 0x11;
+    scancodes[SDLK_e]               = 0x12;
+    scancodes[SDLK_r]               = 0x13;
+    scancodes[SDLK_t]               = 0x14;
+    scancodes[SDLK_y]               = 0x15;
+    scancodes[SDLK_u]               = 0x16;
+    scancodes[SDLK_i]               = 0x17;
+    scancodes[SDLK_o]               = 0x18;
+    scancodes[SDLK_p]               = 0x19;
+    scancodes[SDLK_LEFTBRACKET]     = 0x1A;
+    scancodes[SDLK_RIGHTBRACKET]    = 0x1B;
+    scancodes[SDLK_RETURN]          = 0x1C;
+    scancodes[SDLK_LCTRL]           = 0x1D;
+    scancodes[SDLK_a]               = 0x1E;
+    scancodes[SDLK_s]               = 0x1F;
+    scancodes[SDLK_d]               = 0x20;
+    scancodes[SDLK_f]               = 0x21;
+    scancodes[SDLK_g]               = 0x22;
+    scancodes[SDLK_h]               = 0x23;
+    scancodes[SDLK_j]               = 0x24;
+    scancodes[SDLK_k]               = 0x25;
+    scancodes[SDLK_l]               = 0x26;
+    scancodes[SDLK_SEMICOLON]       = 0x27;
+    scancodes[SDLK_QUOTE]           = 0x28;
+    scancodes[SDLK_BACKQUOTE]       = 0x29;
+    scancodes[SDLK_LSHIFT]          = 0x2A;
+    scancodes[SDLK_BACKSLASH]       = 0x2B;
+    scancodes[SDLK_z]               = 0x2C;
+    scancodes[SDLK_x]               = 0x2D;
+    scancodes[SDLK_c]               = 0x2E;
+    scancodes[SDLK_v]               = 0x2F;
+    scancodes[SDLK_b]               = 0x30;
+    scancodes[SDLK_n]               = 0x31;
+    scancodes[SDLK_m]               = 0x32;
+    scancodes[SDLK_COMMA]           = 0x33;
+    scancodes[SDLK_PERIOD]          = 0x34;
+    scancodes[SDLK_SLASH]           = 0x35;
+    scancodes[SDLK_RSHIFT]          = 0x36;
+    scancodes[SDLK_KP_MULTIPLY]     = 0x37;
+    scancodes[SDLK_LALT]            = 0x38;
+    scancodes[SDLK_SPACE]           = 0x39;
+    scancodes[SDLK_CAPSLOCK]        = 0x3A;
+    scancodes[SDLK_F1]              = 0x3B;
+    scancodes[SDLK_F2]              = 0x3C;
+    scancodes[SDLK_F3]              = 0x3D;
+    scancodes[SDLK_F4]              = 0x3E;
+    scancodes[SDLK_F5]              = 0x3F;
+    scancodes[SDLK_F6]              = 0x40;
+    scancodes[SDLK_F7]              = 0x41;
+    scancodes[SDLK_F8]              = 0x42;
+    scancodes[SDLK_F9]              = 0x43;
+    scancodes[SDLK_F10]             = 0x44;
+    scancodes[SDLK_NUMLOCK]         = 0x45;
+    scancodes[SDLK_SCROLLOCK]       = 0x46;
+    scancodes[SDLK_KP7]             = 0x47;
+    scancodes[SDLK_KP8]             = 0x48;
+    scancodes[SDLK_KP9]             = 0x49;
+    scancodes[SDLK_KP_MINUS]        = 0x4A;
+    scancodes[SDLK_KP4]             = 0x4B;
+    scancodes[SDLK_KP5]             = 0x4C;
+    scancodes[SDLK_KP6]             = 0x4D;
+    scancodes[SDLK_KP_PLUS]         = 0x4E;
+    scancodes[SDLK_KP1]             = 0x4F;
+    scancodes[SDLK_KP2]             = 0x50;
+    scancodes[SDLK_KP3]             = 0x51;
+    scancodes[SDLK_KP0]             = 0x52;
+    scancodes[SDLK_KP_PERIOD]       = 0x53;
+    scancodes[SDLK_F11]             = 0x57;
+    scancodes[SDLK_F12]             = 0x58;
+    scancodes[SDLK_PAUSE]           = 0x59; /* SBF - technically incorrect */
+
+    scancodes[SDLK_KP_ENTER]        = 0xE01C;
+    scancodes[SDLK_RCTRL]           = 0xE01D;
+    scancodes[SDLK_KP_DIVIDE]       = 0xE035;
+    scancodes[SDLK_PRINT]           = 0xE037; /* SBF - technically incorrect */
+    scancodes[SDLK_SYSREQ]          = 0xE037; /* SBF - for windows... */
+    scancodes[SDLK_RALT]            = 0xE038;
+    scancodes[SDLK_HOME]            = 0xE047;
+    scancodes[SDLK_UP]              = 0xE048;
+    scancodes[SDLK_PAGEUP]          = 0xE049;
+    scancodes[SDLK_LEFT]            = 0xE04B;
+    scancodes[SDLK_RIGHT]           = 0xE04D;
+    scancodes[SDLK_END]             = 0xE04F;
+    scancodes[SDLK_DOWN]            = 0xE050;
+    scancodes[SDLK_PAGEDOWN]        = 0xE051;
+    scancodes[SDLK_INSERT]          = 0xE052;
+    scancodes[SDLK_DELETE]          = 0xE053;
+    
+    set_sdl_renderer();
+
+    output_sdl_versions();
+    output_driver_info();
+    detect_vmware();
+
+	printf("Video Driver [directx or windib]? -> %s \n", SDL_VideoDriverName(dummyString, 20));
+
+} /* _platform_init */
+
+
+int setvesa(long x, long y)
+{
+	Error(EXIT_FAILURE, "setvesa() called in SDL driver\n");
+    return(0);
+} /* setvesa */
+
+
+// Capture BMP of the current frame
+int screencapture(char *filename, char inverseit)
+{
+//  FIX_00006: better naming system for screenshots + message when pic is taken. 
+//  Use ./screenshots folder. Screenshot code rerwritten. Faster and
+//  makes smaller files. Doesn't freeze or lag the game anymore.
+  
+	SDL_SaveBMP(surface, filename);  
+	return 0;
+} /* screencapture */
+
+
+void setvmode(int mode)
+{
+
+    if (mode == 0x3)  /* text mode. */
+    {
+        SDL_QuitSubSystem(SDL_INIT_VIDEO);
+        return;
+    } else
+        printf("setvmode(0x%x) is unsupported in SDL driver.\n", mode);
+
+} 
+
+int _setgamemode(char davidoption, long daxdim, long daydim)
+{
+	int validated, i;
+	SDL_Surface     *image;
+	Uint32          colorkey;
+
+#ifdef USE_OPENGL
+    static int shown_gl_strings = 0;
+    int gl = using_opengl();
+    if (gl)
+        sdl_flags |= SDL_OPENGL;
+#endif
+
+	// Install icon
+	image = SDL_LoadBMP_RW(SDL_RWFromMem(iconBMP, sizeof(iconBMP)), 1);
+	// image = SDL_LoadBMP("nuclear2.bmp");
+	// colorkey = SDL_MapRGB(image->format, 252, 254, 252); // to lookup idx in true color img
+	colorkey = 0; // index in this image to be transparent
+	SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
+	SDL_WM_SetIcon(image,NULL);
+
+    if (daxdim > MAXXDIM || daydim > MAXYDIM)
+    {
+		printf("%d x %d is too big. Changed to %d x %d\n", daxdim, daydim, MAXXDIM,MAXYDIM);
+	    daxdim = MAXXDIM;
+	    daydim = MAXYDIM;
+    } 
+
+	getvalidvesamodes();
+
+	validated = 0;
+	for(i=0; i<validmodecnt; i++)
+	{
+		if(validmodexdim[i] == daxdim && validmodeydim[i] == daydim)
+			validated = 1;
+	}
+
+	if(!validated)
+    {
+		printf("%d x %d unsupported. Changed to 640 x 480\n", daxdim, daydim);
+	    daxdim = 640;
+	    daydim = 480;
+    }
+
+    go_to_new_vid_mode((int) davidoption, daxdim, daydim);
+
+    #ifdef USE_OPENGL
+        if (gl)
+        {
+            if (!shown_gl_strings)
+            {
+                sgldebug("GL_VENDOR [%s]", (char *) dglGetString(GL_VENDOR));
+                sgldebug("GL_RENDERER [%s]", (char *) dglGetString(GL_RENDERER));
+                sgldebug("GL_VERSION [%s]", (char *) dglGetString(GL_VERSION));
+                sgldebug("GL_EXTENSIONS [%s]", (char *) dglGetString(GL_EXTENSIONS));
+                shown_gl_strings = 1;
+            } /* if */
+
+            dglViewport(0, 0, daxdim, daydim);
+            dglEnable(GL_TEXTURE_2D);
+            dglPixelTransferi(GL_MAP_COLOR, GL_TRUE);
+            dglPixelTransferi(GL_INDEX_SHIFT, 0);
+            dglPixelTransferi(GL_INDEX_OFFSET, 0);
+            dglClearDepth(1.0);
+            dglDepthFunc(GL_LESS);
+            dglEnable(GL_DEPTH_TEST);
+            dglShadeModel(GL_SMOOTH);
+            dglMatrixMode(GL_PROJECTION);
+            dglLoadIdentity();
+            dglMatrixMode(GL_MODELVIEW);
+        } /* if */
+    #endif
+
+    qsetmode = 200;
+    last_render_ticks = getticks();
+
+    return(0);
+} /* setgamemode */
+
+
+static int get_dimensions_from_str(const char *str, long *_w, long *_h)
+{
+    char *xptr = NULL;
+    char *ptr = NULL;
+    long w = -1;
+    long h = -1;
+
+    if (str == NULL)
+        return(0);
+
+    xptr = strchr(str, 'x');
+    if (xptr == NULL)
+        return(0);
+
+    w = strtol(str, &ptr, 10);
+    if (ptr != xptr)
+        return(0);
+
+    xptr++;
+    h = strtol(xptr, &ptr, 10);
+    if ( (*xptr == '\0') || (*ptr != '\0') )
+        return(0);
+
+    if ((w <= 1) || (h <= 1))
+        return(0);
+
+    if (_w != NULL)
+        *_w = w;
+
+    if (_h != NULL)
+        *_h = h;
+
+    return(1);
+} /* get_dimensions_from_str */
+
+
+static __inline void get_max_screen_res(long *max_w, long *max_h)
+{
+    long w = DEFAULT_MAXRESWIDTH;
+    long h = DEFAULT_MAXRESHEIGHT;
+    const char *envr = getenv(BUILD_MAXSCREENRES);
+
+    if (envr != NULL)
+    {
+        if (!get_dimensions_from_str(envr, &w, &h))
+        {
+            sdldebug("User's resolution ceiling [%s] is bogus!", envr);
+            w = DEFAULT_MAXRESWIDTH;
+            h = DEFAULT_MAXRESHEIGHT;
+        } /* if */
+    } /* if */
+
+    if (max_w != NULL)
+        *max_w = w;
+
+    if (max_h != NULL)
+        *max_h = h;
+} /* get_max_screen_res */
+
+
+static void add_vesa_mode(const char *typestr, int w, int h)
+{
+    sdldebug("Adding %s resolution (%dx%d).", typestr, w, h);
+    validmode[validmodecnt] = validmodecnt;
+    validmodexdim[validmodecnt] = w;
+    validmodeydim[validmodecnt] = h;
+    validmodecnt++;
+} /* add_vesa_mode */
+
+
+/* Let the user specify a specific mode via environment variable. */
+static __inline void add_user_defined_resolution(void)
+{
+    long w;
+    long h;
+    const char *envr = getenv(BUILD_USERSCREENRES);
+
+    if (envr == NULL)
+        return;
+
+    if (get_dimensions_from_str(envr, &w, &h))
+        add_vesa_mode("user defined", w, h);
+    else
+        sdldebug("User defined resolution [%s] is bogus!", envr);
+} /* add_user_defined_resolution */
+
+
+static __inline SDL_Rect **get_physical_resolutions(void)
+{
+    const SDL_VideoInfo *vidInfo = SDL_GetVideoInfo();
+    SDL_Rect **modes = SDL_ListModes(vidInfo->vfmt, sdl_flags | SDL_FULLSCREEN);
+    if (modes == NULL)
+    {
+        sdl_flags &= ~SDL_FULLSCREEN;
+        modes = SDL_ListModes(vidInfo->vfmt, sdl_flags); /* try without fullscreen. */
+        if (modes == NULL)
+            modes = (SDL_Rect **) -1;  /* fuck it. */
+    } /* if */
+
+    if (modes == (SDL_Rect **) -1)
+        sdldebug("Couldn't get any physical resolutions.");
+    else
+    {
+        sdldebug("Highest physical resolution is (%dx%d).",
+                  modes[0]->w, modes[0]->h);
+    } /* else */
+
+    return(modes);
+} /* get_physical_resolutions */
+
+
+static void remove_vesa_mode(int index, const char *reason)
+{
+    int i;
+
+    assert(index < validmodecnt);
+    sdldebug("Removing resolution #%d, %dx%d [%s].",
+              index, validmodexdim[index], validmodeydim[index], reason);
+
+    for (i = index; i < validmodecnt - 1; i++)
+    {
+        validmode[i] = validmode[i + 1];
+        validmodexdim[i] = validmodexdim[i + 1];
+        validmodeydim[i] = validmodeydim[i + 1];
+    } /* for */
+
+    validmodecnt--;
+} /* remove_vesa_mode */
+
+
+static __inline void cull_large_vesa_modes(void)
+{
+    long max_w;
+    long max_h;
+    int i;
+
+    get_max_screen_res(&max_w, &max_h);
+    sdldebug("Setting resolution ceiling to (%ldx%ld).", max_w, max_h);
+
+    for (i = 0; i < validmodecnt; i++)
+    {
+        if ((validmodexdim[i] > max_w) || (validmodeydim[i] > max_h))
+        {
+            remove_vesa_mode(i, "above resolution ceiling");
+            i--;  /* list shrinks. */
+        } /* if */
+    } /* for */
+} /* cull_large_vesa_modes */
+
+
+static __inline void cull_duplicate_vesa_modes(void)
+{
+    int i;
+    int j;
+
+    for (i = 0; i < validmodecnt; i++)
+    {
+        for (j = i + 1; j < validmodecnt; j++)
+        {
+            if ( (validmodexdim[i] == validmodexdim[j]) &&
+                 (validmodeydim[i] == validmodeydim[j]) )
+            {
+                remove_vesa_mode(j, "duplicate");
+                j--;  /* list shrinks. */
+            } /* if */
+        } /* for */
+    } /* for */
+} /* cull_duplicate_vesa_modes */
+
+
+#define swap_macro(tmp, x, y) { tmp = x; x = y; y = tmp; }
+
+/* be sure to call cull_duplicate_vesa_modes() before calling this. */
+static __inline void sort_vesa_modelist(void)
+{
+    int i;
+    int sorted;
+    long tmp;
+
+    do
+    {
+        sorted = 1;
+        for (i = 0; i < validmodecnt - 1; i++)
+        {
+            if ( (validmodexdim[i] >= validmodexdim[i+1]) &&
+                 (validmodeydim[i] >= validmodeydim[i+1]) )
+            {
+                sorted = 0;
+                swap_macro(tmp, validmode[i], validmode[i+1]);
+                swap_macro(tmp, validmodexdim[i], validmodexdim[i+1]);
+                swap_macro(tmp, validmodeydim[i], validmodeydim[i+1]);
+            } /* if */
+        } /* for */
+    } while (!sorted);
+} /* sort_vesa_modelist */
+
+
+static __inline void cleanup_vesa_modelist(void)
+{
+    cull_large_vesa_modes();
+    cull_duplicate_vesa_modes();
+    sort_vesa_modelist();
+} /* cleanup_vesa_modelist */
+
+
+static __inline void output_vesa_modelist(void)
+{
+    char buffer[256];
+    char numbuf[20];
+    int i;
+
+    if (!_sdl_debug_file)
+        return;
+
+    buffer[0] = '\0';
+
+    for (i = 0; i < validmodecnt; i++)
+    {
+        sprintf(numbuf, " (%ldx%ld)",
+                  (long) validmodexdim[i], (long) validmodeydim[i]);
+
+        if ( (strlen(buffer) + strlen(numbuf)) >= (sizeof (buffer) - 1) )
+            strcpy(buffer + (sizeof (buffer) - 5), " ...");
+        else
+            strcat(buffer, numbuf);
+    } /* for */
+
+    sdldebug("Final sorted modelist:%s", buffer);
+} /* output_vesa_modelist */
+
+
+void getvalidvesamodes(void)
+{
+    static int already_checked = 0;
+    int i;
+    SDL_Rect **modes = NULL;
+    int stdres[][2] = {
+                        {320, 200}, {640, 350}, {640, 480},
+                        {800, 600}, {1024, 768}
+                      };
+
+    if (already_checked)
+        return;
+
+    already_checked = 1;
+   	validmodecnt = 0;
+    vidoption = 1;  /* !!! tmp */
+
+        /* fill in the standard resolutions... */
+    for (i = 0; i < sizeof (stdres) / sizeof (stdres[0]); i++)
+        add_vesa_mode("standard", stdres[i][0], stdres[i][1]);
+
+         /* Anything the hardware can specifically do is added now... */
+    modes = get_physical_resolutions();
+    for (i = 0; (modes != (SDL_Rect **) -1) && (modes[i] != NULL); i++)
+        add_vesa_mode("physical", modes[i]->w, modes[i]->h);
+
+        /* Now add specific resolutions that the user wants... */
+    add_user_defined_resolution();
+
+        /* get rid of dupes and bogus resolutions... */
+    cleanup_vesa_modelist();
+
+        /* print it out for debugging purposes... */
+    output_vesa_modelist();
+} /* getvalidvesamodes */
+
+
+int VBE_setPalette(long start, long num, char *palettebuffer)
+/*
+ * (From Ken's docs:)
+ *   Set (num) palette palette entries starting at (start)
+ *   palette entries are in a 4-byte format in this order:
+ *       0: Blue (0-63)
+ *       1: Green (0-63)
+ *       2: Red (0-63)
+ *       3: Reserved
+ *
+ * Naturally, the bytes are in the reverse order that SDL wants them...
+ *  More importantly, SDL wants the color elements in a range from 0-255,
+ *  so we do a conversion.
+ */
+{
+    SDL_Color fmt_swap[256];
+    SDL_Color *sdlp = &fmt_swap[start];
+    char *p = palettebuffer;
+    int i;
+
+#if (defined USE_OPENGL)
+    int gl = using_opengl();
+    GLfloat gl_reds[256];
+    GLfloat gl_greens[256];
+    GLfloat gl_blues[256];
+    GLfloat gl_alphas[256];
+
+    if (gl)
+    {
+        dglGetPixelMapfv(GL_PIXEL_MAP_I_TO_R, gl_reds);
+        dglGetPixelMapfv(GL_PIXEL_MAP_I_TO_G, gl_greens);
+        dglGetPixelMapfv(GL_PIXEL_MAP_I_TO_B, gl_blues);
+        dglGetPixelMapfv(GL_PIXEL_MAP_I_TO_A, gl_alphas);
+    } /* if */
+#endif
+
+    assert( (start + num) <= (sizeof (fmt_swap) / sizeof (SDL_Color)) );
+
+    for (i = 0; i < num; i++)
+    {
+        sdlp->b = (Uint8) ((((float) *p++) / 63.0) * 255.0);
+        sdlp->g = (Uint8) ((((float) *p++) / 63.0) * 255.0);
+        sdlp->r = (Uint8) ((((float) *p++) / 63.0) * 255.0);
+        sdlp->unused = *p++;   /* This byte is unused in BUILD, too. */
+
+#if (defined USE_OPENGL)
+        if (gl)
+        {
+            gl_reds[i+start]   = ((GLfloat) sdlp->r) / 255.0f;
+            gl_greens[i+start] = ((GLfloat) sdlp->g) / 255.0f;
+            gl_blues[i+start]  = ((GLfloat) sdlp->b) / 255.0f;
+            gl_alphas[i+start] = 1.0f;
+        } /* if */
+#endif
+
+        sdlp++;
+    } /* for */
+
+#if (defined USE_OPENGL)
+    if (gl)
+    {
+        dglPixelMapfv(GL_PIXEL_MAP_I_TO_R, start + num, gl_reds);
+        dglPixelMapfv(GL_PIXEL_MAP_I_TO_G, start + num, gl_greens);
+        dglPixelMapfv(GL_PIXEL_MAP_I_TO_B, start + num, gl_blues);
+        dglPixelMapfv(GL_PIXEL_MAP_I_TO_A, start + num, gl_alphas);
+    } /* if */
+#endif
+    return(SDL_SetColors(surface, fmt_swap, start, num));
+} /* VBE_setPalette */
+
+
+int VBE_getPalette(long start, long num, char *palettebuffer)
+{
+    SDL_Color *sdlp = surface->format->palette->colors + start;
+    char *p = palettebuffer + (start * 4);
+    int i;
+
+    for (i = 0; i < num; i++)
+    {
+        *p++ = (Uint8) ((((float) sdlp->b) / 255.0) * 63.0);
+        *p++ = (Uint8) ((((float) sdlp->g) / 255.0) * 63.0);
+        *p++ = (Uint8) ((((float) sdlp->r) / 255.0) * 63.0);
+        *p++ = sdlp->unused;   /* This byte is unused in both SDL and BUILD. */
+        sdlp++;
+    } /* for */
+
+    return(1);
+} /* VBE_getPalette */
+
+
+void _uninitengine(void)
+{
+   SDL_QuitSubSystem(SDL_INIT_VIDEO);
+} /* _uninitengine */
+
+
+void uninitvesa(void)
+{
+   SDL_QuitSubSystem(SDL_INIT_VIDEO);
+} /* uninitvesa */
+
+
+int setupmouse(void)
+{
+
+	SDL_Event event;
+
+    if (surface == NULL)
+        return(0);
+
+    SDL_WM_GrabInput(SDL_GRAB_ON);
+    SDL_ShowCursor(0);
+
+    mouse_relative_x = mouse_relative_y = 0;
+
+        /*
+         * this global usually gets set by BUILD, but it's a one-shot
+         *  deal, and we may not have an SDL surface at that point. --ryan.
+         */
+    moustat = 1;
+
+	// FIX_00063: Duke's angle changing or incorrect when using toggle fullscreen/window mode
+	while(SDL_PollEvent(&event)); // Empying the various pending events (especially the mouse one)
+
+	//SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+
+    return(1);
+} /* setupmouse */
+
+
+void readmousexy(short *x, short *y)
+{
+    if (x) *x = mouse_relative_x << 2;
+    if (y) *y = mouse_relative_y << 2;
+
+    mouse_relative_x = mouse_relative_y = 0;
+} /* readmousexy */
+
+
+void readmousebstatus(short *bstatus)
+{
+    if (bstatus)
+        *bstatus = mouse_buttons;
+
+    // special wheel treatment: make it like a button click
+    if(mouse_buttons&8) mouse_buttons ^= 8;
+    if(mouse_buttons&16) mouse_buttons ^= 16;
+
+} /* readmousebstatus */
+
+
+static unsigned char mirrorcolor = 0;
+
+void _updateScreenRect(long x, long y, long w, long h)
+{
+    if (renderer == RENDERER_SOFTWARE)
+        SDL_UpdateRect(surface, x, y, w, h);
+} /* _updatescreenrect */
+
+
+void _nextpage(void)
+{
+    Uint32 ticks;
+
+    _handle_events();
+
+    if (renderer == RENDERER_SOFTWARE)
+    {
+		// FIX_00085: Optimized Video driver. FPS increases by +20%.
+        // SDL_Flip(surface);
+		SDL_UpdateRect(surface, 0, 0, 0, 0);
+    }
+
+#ifdef USE_OPENGL
+    else if (renderer == RENDERER_OPENGL3D)
+    {
+        opengl_swapbuffers();
+    } /* else if */
+#endif
+
+    if ((debug_hall_of_mirrors) && (qsetmode == 200) && (frameplace))
+    {
+        memset((void *) frameplace, mirrorcolor, surface->w * surface->h);
+        mirrorcolor++;
+    } /* if */
+
+    ticks = getticks();
+    total_render_time = (ticks - last_render_ticks);
+    if (total_render_time > 1000)
+    {
+        total_rendered_frames = 0;
+        total_render_time = 1;
+        last_render_ticks = ticks;
+    } /* if */
+    total_rendered_frames++;
+} /* _nextpage */
+
+
+unsigned char readpixel(long offset)
+{
+    return( *((unsigned char *) offset) );
+} /* readpixel */
+
+void drawpixel(long offset, unsigned char pixel)
+{
+    *((unsigned char *) offset) = pixel;
+} /* drawpixel */
+
+
+/* !!! These are incorrect. */
+void drawpixels(long offset, unsigned short pixels)
+{
+    Uint8 *surface_end;
+    Uint16 *pos;
+
+                Error(EXIT_FAILURE, "Blargh!\n");
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_LockSurface(surface);
+
+    surface_end = (((Uint8 *) surface->pixels) + (surface->w * surface->h)) - 2;
+    pos = (Uint16 *) (((Uint8 *) surface->pixels) + offset);
+    if ((pos >= (Uint16 *) surface->pixels) && (pos < (Uint16 *) surface_end))
+        *pos = pixels;
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_UnlockSurface(surface);
+} /* drawpixels */
+
+
+void drawpixelses(long offset, unsigned int pixelses)
+{
+    Uint8 *surface_end;
+    Uint32 *pos;
+
+                Error(EXIT_FAILURE, "Blargh!\n");
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_LockSurface(surface);
+
+    surface_end = (((Uint8 *)surface->pixels) + (surface->w * surface->h)) - 2;
+    pos = (Uint32 *) (((Uint8 *) surface->pixels) + offset);
+    if ((pos >= (Uint32 *) surface->pixels) && (pos < (Uint32 *) surface_end))
+        *pos = pixelses;
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_UnlockSurface(surface);
+} /* drawpixelses */
+
+
+/* Fix this up The Right Way (TM) - DDOI */
+void setcolor16(int col)
+{
+	drawpixel_color = col;
+}
+
+void drawpixel16(long offset)
+{
+    drawpixel(((long) surface->pixels + offset), drawpixel_color);
+} /* drawpixel16 */
+
+
+void fillscreen16(long offset, long color, long blocksize)
+{
+    Uint8 *surface_end;
+    Uint8 *wanted_end;
+    Uint8 *pixels;
+
+#if (defined USE_OPENGL)
+    if (renderer == RENDERER_OPENGL3D)
+    {
+        /* !!! dglClearColor() ... */
+        return;
+    } /* if */
+#endif
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_LockSurface(surface);
+
+    pixels = get_framebuffer();
+
+    /* Make this function pageoffset aware - DDOI */
+    if (!pageoffset) { 
+	    offset = offset << 3;
+	    offset += 640*336;
+    }
+
+    surface_end = (pixels + (surface->w * surface->h)) - 1;
+    wanted_end = (pixels + offset) + blocksize;
+
+    if (offset < 0)
+        offset = 0;
+
+    if (wanted_end > surface_end)
+        blocksize = ((unsigned long) surface_end) - ((unsigned long) pixels + offset);
+
+    memset(pixels + offset, (int) color, blocksize);
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_UnlockSurface(surface);
+
+    _nextpage();
+} /* fillscreen16 */
+
+
+/* Most of this line code is taken from Abrash's "Graphics Programming Blackbook".
+Remember, sharing code is A Good Thing. AH */
+static __inline void DrawHorizontalRun (char **ScreenPtr, int XAdvance, int RunLength, char Color)
+{
+    int i;
+    char *WorkingScreenPtr = *ScreenPtr;
+
+    for (i=0; i<RunLength; i++)
+    {
+        *WorkingScreenPtr = Color;
+        WorkingScreenPtr += XAdvance;
+    }
+    WorkingScreenPtr += surface->w;
+    *ScreenPtr = WorkingScreenPtr;
+}
+
+static __inline void DrawVerticalRun (char **ScreenPtr, int XAdvance, int RunLength, char Color)
+{
+    int i;
+    char *WorkingScreenPtr = *ScreenPtr;
+
+    for (i=0; i<RunLength; i++)
+    {
+      	*WorkingScreenPtr = Color;
+    	WorkingScreenPtr += surface->w;
+    }
+    WorkingScreenPtr += XAdvance;
+    *ScreenPtr = WorkingScreenPtr;
+}
+
+void drawline16(long XStart, long YStart, long XEnd, long YEnd, char Color)
+{
+    int Temp, AdjUp, AdjDown, ErrorTerm, XAdvance, XDelta, YDelta;
+    int WholeStep, InitialPixelCount, FinalPixelCount, i, RunLength;
+    char *ScreenPtr;
+    long dx, dy;
+
+    if (SDL_MUSTLOCK(surface))
+        SDL_LockSurface(surface);
+
+	dx = XEnd-XStart; dy = YEnd-YStart;
+	if (dx >= 0)
+	{
+		if ((XStart > 639) || (XEnd < 0)) return;
+		if (XStart < 0) { if (dy) YStart += scale(0-XStart,dy,dx); XStart = 0; }
+		if (XEnd > 639) { if (dy) YEnd += scale(639-XEnd,dy,dx); XEnd = 639; }
+	}
+	else
+	{
+		if ((XEnd > 639) || (XStart < 0)) return;
+		if (XEnd < 0) { if (dy) YEnd += scale(0-XEnd,dy,dx); XEnd = 0; }
+		if (XStart > 639) { if (dy) YStart += scale(639-XStart,dy,dx); XStart = 639; }
+	}
+	if (dy >= 0)
+	{
+		if ((YStart >= ydim16) || (YEnd < 0)) return;
+		if (YStart < 0) { if (dx) XStart += scale(0-YStart,dx,dy); YStart = 0; }
+		if (YEnd >= ydim16) { if (dx) XEnd += scale(ydim16-1-YEnd,dx,dy); YEnd = ydim16-1; }
+	}
+	else
+	{
+		if ((YEnd >= ydim16) || (YStart < 0)) return;
+		if (YEnd < 0) { if (dx) XEnd += scale(0-YEnd,dx,dy); YEnd = 0; }
+		if (YStart >= ydim16) { if (dx) XStart += scale(ydim16-1-YStart,dx,dy); YStart = ydim16-1; }
+	}
+
+	/* Make sure the status bar border draws correctly - DDOI */
+	if (!pageoffset) { YStart += 336; YEnd += 336; }
+
+    /* We'll always draw top to bottom */
+    if (YStart > YEnd) {
+        Temp = YStart;
+        YStart = YEnd;
+        YEnd = Temp;
+        Temp = XStart;
+        XStart = XEnd;
+        XEnd = Temp;
+    }
+
+    /* Point to the bitmap address first pixel to draw */
+    ScreenPtr = (char *) (get_framebuffer()) + XStart + (surface->w * YStart);
+
+    /* Figure out whether we're going left or right, and how far we're going horizontally */
+    if ((XDelta = XEnd - XStart) < 0)
+    {
+        XAdvance = (-1);
+        XDelta = -XDelta;
+    } else {
+        XAdvance = 1;
+    }
+
+    /* Figure out how far we're going vertically */
+    YDelta = YEnd - YStart;
+
+    /* Special cases: Horizontal, vertical, and diagonal lines */
+    if (XDelta == 0)
+    {
+        for (i=0; i <= YDelta; i++)
+        {
+            *ScreenPtr = Color;
+            ScreenPtr += surface->w;
+        }
+
+        UNLOCK_SURFACE_AND_RETURN;
+    }
+    if (YDelta == 0)
+    {
+    	for (i=0; i <= XDelta; i++)
+    	{
+      	    *ScreenPtr = Color;
+    	    ScreenPtr += XAdvance;
+    	}
+        UNLOCK_SURFACE_AND_RETURN;
+    }
+    if (XDelta == YDelta)
+    {
+    	for (i=0; i <= XDelta; i++)
+        {
+            *ScreenPtr = Color;
+            ScreenPtr += XAdvance + surface->w;
+        }
+        UNLOCK_SURFACE_AND_RETURN;
+    }
+
+    /* Determine whether the line is X or Y major, and handle accordingly */
+    if (XDelta >= YDelta) /* X major line */
+    {
+        WholeStep = XDelta / YDelta;
+        AdjUp = (XDelta % YDelta) * 2;
+        AdjDown = YDelta * 2;
+        ErrorTerm = (XDelta % YDelta) - (YDelta * 2);
+
+        InitialPixelCount = (WholeStep / 2) + 1;
+        FinalPixelCount = InitialPixelCount;
+
+        if ((AdjUp == 0) && ((WholeStep & 0x01) == 0)) InitialPixelCount--;
+        if ((WholeStep & 0x01) != 0) ErrorTerm += YDelta;
+
+        DrawHorizontalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color);
+
+        for (i=0; i<(YDelta-1); i++)
+        {
+            RunLength = WholeStep;
+            if ((ErrorTerm += AdjUp) > 0)
+            {
+        	RunLength ++;
+        	ErrorTerm -= AdjDown;
+            }
+
+            DrawHorizontalRun(&ScreenPtr, XAdvance, RunLength, Color);
+         }
+
+         DrawHorizontalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color);
+
+         UNLOCK_SURFACE_AND_RETURN;
+    } else {	/* Y major line */
+    	WholeStep = YDelta / XDelta;
+    	AdjUp = (YDelta % XDelta) * 2;
+    	AdjDown = XDelta * 2;
+        ErrorTerm = (YDelta % XDelta) - (XDelta * 2);
+        InitialPixelCount = (WholeStep / 2) + 1;
+        FinalPixelCount = InitialPixelCount;
+
+        if ((AdjUp == 0) && ((WholeStep & 0x01) == 0)) InitialPixelCount --;
+        if ((WholeStep & 0x01) != 0) ErrorTerm += XDelta;
+
+        DrawVerticalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color);
+
+        for (i=0; i<(XDelta-1); i++)
+        {
+            RunLength = WholeStep;
+            if ((ErrorTerm += AdjUp) > 0)
+            {
+            	RunLength ++;
+            	ErrorTerm -= AdjDown;
+            }
+
+            DrawVerticalRun(&ScreenPtr, XAdvance, RunLength, Color);
+        }
+
+        DrawVerticalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color);
+        UNLOCK_SURFACE_AND_RETURN;
+     }
+} /* drawline16 */
+
+
+void clear2dscreen(void)
+{
+    SDL_Rect rect;
+
+    rect.x = rect.y = 0;
+    rect.w = surface->w;
+
+	if (qsetmode == 350)
+        rect.h = 350;
+	else if (qsetmode == 480)
+	{
+		if (ydim16 <= 336)
+            rect.h = 336;
+        else
+            rect.h = 480;
+	} /* else if */
+
+    SDL_FillRect(surface, &rect, 0);
+} /* clear2dscreen */
+
+
+void _idle(void)
+{
+    if (surface != NULL)
+        _handle_events();
+    SDL_Delay(1);
+} /* _idle */
+
+void *_getVideoBase(void)
+{
+    return((void *) surface->pixels);
+}
+
+void setactivepage(long dapagenum)
+{
+	/* !!! Is this really still needed? - DDOI */
+    /*fprintf(stderr, "%s, line %d; setactivepage(): STUB.\n", __FILE__, __LINE__);*/
+} /* setactivepage */
+
+void limitrate(void)
+{
+    /* this is a no-op in SDL. It was for buggy VGA cards in DOS. */
+} /* limitrate */
+
+
+//-------------------------------------------------------------------------------------------------
+//  TIMER
+//=================================================================================================
+
+// FIX_00007: game speed corrected. The game speed is now as the real 
+// DOS duke3d. Unloading a full 200 bullet pistol must take 45.1 sec.
+// SDL timer was not fast/accurate enough and was slowing down the gameplay,
+// so bad
+
+typedef long long int64;
+static int64 timerfreq=0;
+static long timerlastsample=0;
+static int timerticspersec=0;
+static void (*usertimercallback)(void) = NULL;
+
+//  This timer stuff is all Ken's idea.
+
+//
+// installusertimercallback() -- set up a callback function to be called when the timer is fired
+//
+void (*installusertimercallback(void (*callback)(void)))(void)
+{
+	void (*oldtimercallback)(void);
+
+	oldtimercallback = usertimercallback;
+	usertimercallback = callback;
+
+	return oldtimercallback;
+}
+
+
+//
+// inittimer() -- initialise timer
+//
+int inittimer(int tickspersecond)
+{
+	int64 t;
+	
+	if (timerfreq) return 0;	// already installed
+
+	printf("Initialising timer\n");
+
+	// OpenWatcom seems to want us to query the value into a local variable
+	// instead of the global 'timerfreq' or else it gets pissed with an
+	// access violation
+	if (!QueryPerformanceFrequency((LARGE_INTEGER*)&t)) {
+		printf("Failed fetching timer frequency\n");
+		return -1;
+	}
+	timerfreq = t;
+	timerticspersec = tickspersecond;
+	QueryPerformanceCounter((LARGE_INTEGER*)&t);
+	timerlastsample = (long)(t*timerticspersec / timerfreq);
+
+	usertimercallback = NULL;
+
+	return 0;
+}
+
+//
+// uninittimer() -- shut down timer
+//
+void uninittimer(void)
+{
+	if (!timerfreq) return;
+
+	timerfreq=0;
+	timerticspersec = 0;
+}
+
+//
+// sampletimer() -- update totalclock
+//
+void sampletimer(void)
+{
+	int64 i;
+	long n;
+	
+	if (!timerfreq) return;
+
+	QueryPerformanceCounter((LARGE_INTEGER*)&i);
+	n = (long)(i*timerticspersec / timerfreq) - timerlastsample;
+	if (n>0) {
+		totalclock += n;
+		timerlastsample += n;
+	}
+
+	if (usertimercallback) for (; n>0; n--) usertimercallback();
+}
+
+
+//
+// getticks() -- returns the windows ticks count
+//
+unsigned long getticks(void)
+{
+	int64 i;
+	QueryPerformanceCounter((LARGE_INTEGER*)&i);
+	return (unsigned long)(i*(long long)(1000)/timerfreq);
+}
+
+
+//
+// gettimerfreq() -- returns the number of ticks per second the timer is configured to generate
+//
+int gettimerfreq(void)
+{
+	return timerticspersec;
+}
+
+// ****************************************************************************
+
+//static Uint32 _timer_catcher(Uint32 interval, void *bleh)
+//{
+//    timerhandler();
+//    return(1);
+//} /* _timer_catcher */
+//
+//
+//void inittimer(void)
+//{
+//    SDL_ClearError();
+//    primary_timer = SDL_AddTimer(1000 / PLATFORM_TIMER_HZ, _timer_catcher, NULL);
+//    if (primary_timer == NULL)
+//    {
+//        fprintf(stderr, "BUILDSDL: -ERROR- Problem initializing primary timer!\n");
+//        fprintf(stderr, "BUILDSDL:  Reason: [%s]\n", SDL_GetError());
+//        Error(EXIT_FAILURE, "");
+//    } /* if */
+//}
+//
+//
+//void uninittimer(void)
+//{
+//    if (primary_timer != NULL)
+//    {
+//        SDL_RemoveTimer(primary_timer);
+//        primary_timer = NULL;
+//    } /* if */
+//}
+
+void initkeys(void)
+{
+    /* does nothing in SDL. Key input handling is set up elsewhere. */
+    /* !!! why not here? */
+}
+
+void uninitkeys(void)
+{
+    /* does nothing in SDL. Key input handling is set up elsewhere. */
+}
+
+
+//unsigned long getticks(void)
+//{
+//    return(SDL_GetTicks());
+//} /* getticks */
+
+/* end of sdl_driver.c ... */
+
--- /dev/null
+++ b/Engine/src/win32_compat.h
@@ -1,0 +1,137 @@
+/*
+ * win32 compatibility header. Takes care of some legacy code issues
+ *  and incompatibilities at the source level.
+ *
+ *  Written by Ryan C. Gordon (icculus@clutteredmind.org)
+ *
+ * Please do NOT harrass Ken Silverman about any code modifications
+ *  (including this file) to BUILD.
+ */
+
+/*
+ * "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * See the included license file "BUILDLIC.TXT" for license info.
+ * This file IS NOT A PART OF Ken Silverman's original release
+ */
+  
+#ifndef _INCLUDE_WIN32_COMPAT_H_
+#define _INCLUDE_WIN32_COMPAT_H_
+
+#if (!defined PLATFORM_WIN32)
+#error PLATFORM_WIN32 is not defined.
+#endif
+
+#define PLATFORM_SUPPORTS_SDL
+
+#include <stdio.h>
+
+#if (!defined _MSC_VER)
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <io.h>
+#include <direct.h>
+#include <conio.h>
+#include <dos.h>
+#include <assert.h>
+
+extern const int hbits[];   /* !!! what is this, and why is it here? */
+
+/*
+  Do some bitwise magic to approximate an algebraic (sign preserving)
+  right shift.
+ */
+#define shift_algebraic_right(value,distance) \
+(((value) >> (distance))| \
+ (hbits[(distance) + (((value) & 0x80000000) >> 26)]))
+
+/* !!! remove me later! */
+/* !!! remove me later! */
+/* !!! remove me later! */
+#define outpw(x, y)   printf("outpw(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define koutpw(x, y)  printf("koutpw(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define outb(x, y)    printf("outb(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define koutb(x, y)   printf("koutb(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define outp(x, y)    printf("outp(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define koutp(x, y)   printf("koutp(0x%X, 0x%X) call in %s, line %d.\n", \
+                              (x), (y), __FILE__, __LINE__)
+
+#define kinp(x)       _kinp_handler((x), __FILE__, __LINE__)
+#define inp(x)        _inp_handler((x), __FILE__, __LINE__)
+
+int _inp_handler(int port, char *source_file, int source_line);
+int _kinp_handler(int port, char *source_file, int source_line);
+/* !!! remove me later! */
+/* !!! remove me later! */
+/* !!! remove me later! */
+
+
+#define __far
+#define __interrupt
+#define interrupt
+#define far
+#define kmalloc(x) malloc(x)
+#define kkmalloc(x) malloc(x)
+#define kfree(x) free(x)
+#define kkfree(x) free(x)
+
+#ifdef FP_OFF
+#undef FP_OFF
+#endif
+
+#define FP_OFF(x) ((long) (x))
+
+/* !!! This might be temporary. */
+#define printext16 printext256
+#define printext16_noupdate printext256_noupdate
+
+#ifndef max
+#define max(x, y)  (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef min
+#define min(x, y)  (((x) < (y)) ? (x) : (y))
+#endif
+
+#if (defined __WATCOMC__)
+#define inline
+#pragma intrinsic(min);
+#pragma intrinsic(max);
+#define __int64 long long
+#endif
+
+#if (defined _MSC_VER)
+#if ((!defined _INTEGRAL_MAX_BITS) || (_INTEGRAL_MAX_BITS < 64))
+#error __int64 type not supported
+#endif
+
+#define open _open
+#define O_BINARY _O_BINARY
+#define O_RDONLY _O_RDONLY
+#define O_WRONLY _O_WRONLY
+#define O_RDWR _O_RDWR
+#define O_TRUNC _O_TRUNC
+#define O_CREAT _O_CREAT
+#define S_IREAD _S_IREAD
+#define S_IWRITE _S_IWRITE
+#define S_IRDWR _S_IRDWR
+#endif /* defined _MSC_VER */
+
+#endif
+
+/* end of win32_compat.h ... */
+
+
+
--- /dev/null
+++ b/Game/BIN2C.C
@@ -1,0 +1,122 @@
+
+/* BIN2C V1.0 CODED BY CHRISTIAN PADOVANO ON 17-MAY-1995
+
+   this little utility translates a binary file in a useful C structure
+   that can be included in a C source.
+
+   to contact me write to EMAIL: Christian_Padovano@amp.flashnet.it
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#define BUF_LEN 1
+#define LINE     12
+
+/* Tell u the file size in bytes */
+
+long int filesize( FILE *fp )
+  {
+    long int save_pos, size_of_file;
+
+    save_pos = ftell( fp );
+    fseek( fp, 0L, SEEK_END );
+    size_of_file = ftell( fp );
+    fseek( fp, save_pos, SEEK_SET );
+    return( size_of_file );
+  }
+
+
+/* lower chars --> upper chars */
+
+void Upper_chars(char *buffer)
+{
+ unsigned int c;
+
+ for (c=0; c <= strlen(buffer)-1; c++) *(buffer+c)=toupper( *(buffer+c) );
+}
+
+
+void main( argc, argv )
+int argc;
+char *argv[];
+{
+    FILE *source,*dest;
+    unsigned char buffer[BUF_LEN], Dummy[20];
+    int c;
+
+    if ( (argc < 4) )
+    {
+
+     if (  ( argc == 2 ) && ( strcmp(argv[1],"/?")==0 )  )
+     {
+      puts(" - <<< BIN2C V1.0 >>> by Christian Padovano - \n");
+      puts("USAGE: Bin2C  <BINARY file name> <TARGET file name> <STRUCT name>");
+      puts("\n <STRUCT > = name of the C structure in the destination file name.\n");
+      puts("  <TARGET > = without extension '.h' it will be added by program.");
+      exit(0L);
+     }
+     else
+     {
+      puts("Bad arguments !!! You must give me all the parameters !!!!\n"
+           "Type 'BIN2C /?' to read the help !!!! ");
+      exit(0L);
+     }
+
+    }
+
+    if( (source=fopen( argv[1], "rb" )) == NULL )
+    {
+      printf("ERROR : I can't find source file   %s\n",argv[1]);
+      exit(20L);
+    }
+
+    strcpy(Dummy,argv[2]);
+    strcat(Dummy,".h");               /* add suffix .h to target name */
+
+    if( (dest=fopen( Dummy, "wb+" )) == NULL )
+    {
+      printf("ERROR : I can't open destination file   %s\n",Dummy);
+      (void)fcloseall();
+      exit(20L);
+    }
+
+
+    strcpy(Dummy,argv[3]);
+    Upper_chars(Dummy);  /* lower to upper chars */
+    strcat(Dummy,"_LEN");  /* add the suffix _LEN to the struct name */
+                           /* for the #define stantment              */
+
+
+    /* It writes the header information */
+    fprintf( dest, "\n#define %s %ld\n\n", Dummy, filesize(source) );
+    fprintf( dest, " static unsigned char %s[]=\n {\n  ", argv[3] );
+
+    if( ferror( dest ) )
+    {
+     printf( "ERROR writing on target file:  %s\n",argv[2] );
+     (void)fcloseall();
+     exit(20L);
+    }
+
+    do
+    {
+     for ( c=0; ((c <= LINE) && (! feof( source ) )) ; c++)
+     {
+      fread( buffer, 1, 1, source );
+      if (! feof( source ) ) fprintf(dest,"0x%02x,",*buffer);
+      else fprintf(dest,"0x%02x",*buffer);
+     }
+     fprintf(dest,"\n  ");
+    }
+    while( ! feof( source ) );
+
+    fprintf(dest,"};\n\n");
+
+    (void)fcloseall();
+
+}
+
+
+
binary files /dev/null b/Game/BIN2C.COM differ
--- /dev/null
+++ b/Game/Game.vcproj
@@ -1,0 +1,473 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="Game"
+	ProjectGUID="{6D80B050-1DC6-406A-98E3-60C65C3304F3}"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\bin"
+			IntermediateDirectory=".\ReleaseVC7"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			WholeProgramOptimization="0"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Release/Game.tlb"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="1"
+				OmitFramePointers="true"
+				AdditionalIncludeDirectories="&quot;..\..\SDL-1.2.9\include&quot;;&quot;..\..\SDL_mixer-1.2.6&quot;;..\Engine\src;.\src"
+				PreprocessorDefinitions="nDBGRECORD; CHECK_XDUKE_REV; nDEBUG; WIN32; _CONSOLE; PLATFORM_WIN32; _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE"
+				RuntimeLibrary="0"
+				BufferSecurityCheck="false"
+				DefaultCharIsUnsigned="true"
+				UsePrecompiledHeader="0"
+				SuppressStartupBanner="true"
+				CompileAs="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="0"
+				ResourceOutputFileName="$(IntDir)/$(InputName).res"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386"
+				AdditionalDependencies="dxguid.lib sdl.lib sdl_mixer.lib winmm.lib WS2_32.lib"
+				OutputFile="$(OutDir)/$(SolutionName).exe"
+				Version="19.4"
+				LinkIncremental="1"
+				SuppressStartupBanner="true"
+				AdditionalLibraryDirectories="&quot;..\..\Directx_dec_2005\&quot;;&quot;..\..\SDL-1.2.9\VisualC\SDL\Release&quot;;&quot;..\..\SDL_mixer-1.2.6\VisualC\Release&quot;"
+				IgnoreDefaultLibraryNames=""
+				SubSystem="1"
+				OptimizeReferences="2"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\bin"
+			IntermediateDirectory=".\DebugVC7"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Debug/Game.tlb"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;..\SDL-1.2.9\include&quot;;&quot;..\SDL_mixer-1.2.6&quot;;..\Engine\src;"
+				PreprocessorDefinitions="nDBGRECORD; CHECK_XDUKE_REV; nDEBUG; WIN32; _CONSOLE; PLATFORM_WIN32; _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="0"
+				DefaultCharIsUnsigned="true"
+				UsePrecompiledHeader="0"
+				BrowseInformation="1"
+				SuppressStartupBanner="true"
+				DebugInformationFormat="4"
+				CompileAs="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="0"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386"
+				AdditionalDependencies="dxguid.lib sdl.lib sdl_mixer.lib winmm.lib WS2_32.lib"
+				OutputFile="$(OutDir)/$(SolutionName)d.exe"
+				SuppressStartupBanner="true"
+				AdditionalLibraryDirectories="&quot;..\Directx_dec_2005\&quot;;&quot;..\SDL-1.2.9\VisualC\SDL\Release&quot;;&quot;..\SDL_mixer-1.2.6\VisualC\Release&quot;"
+				IgnoreDefaultLibraryNames=""
+				GenerateDebugInformation="true"
+				SubSystem="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+			>
+			<File
+				RelativePath="src\actors.c"
+				>
+			</File>
+			<File
+				RelativePath="src\animlib.c"
+				>
+			</File>
+			<File
+				RelativePath="src\config.c"
+				>
+			</File>
+			<File
+				RelativePath="src\control.c"
+				>
+			</File>
+			<File
+				RelativePath="src\game.c"
+				>
+			</File>
+			<File
+				RelativePath="src\gamedef.c"
+				>
+			</File>
+			<File
+				RelativePath="src\global.c"
+				>
+			</File>
+			<File
+				RelativePath="src\keyboard.c"
+				>
+			</File>
+			<File
+				RelativePath="src\menues.c"
+				>
+			</File>
+			<File
+				RelativePath="src\player.c"
+				>
+			</File>
+			<File
+				RelativePath="src\premap.c"
+				>
+			</File>
+			<File
+				RelativePath="src\rts.c"
+				>
+			</File>
+			<File
+				RelativePath="src\scriplib.c"
+				>
+			</File>
+			<File
+				RelativePath="src\sector.c"
+				>
+			</File>
+			<File
+				RelativePath="src\sounds.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl"
+			>
+			<File
+				RelativePath="src\_rts.h"
+				>
+			</File>
+			<File
+				RelativePath="src\animlib.h"
+				>
+			</File>
+			<File
+				RelativePath="src\config.h"
+				>
+			</File>
+			<File
+				RelativePath="src\control.h"
+				>
+			</File>
+			<File
+				RelativePath="src\develop.h"
+				>
+			</File>
+			<File
+				RelativePath="src\duke3d.h"
+				>
+			</File>
+			<File
+				RelativePath="src\dukewin.h"
+				>
+			</File>
+			<File
+				RelativePath="src\file_lib.h"
+				>
+			</File>
+			<File
+				RelativePath="src\funct.h"
+				>
+			</File>
+			<File
+				RelativePath="src\function.h"
+				>
+			</File>
+			<File
+				RelativePath="src\gamedefs.h"
+				>
+			</File>
+			<File
+				RelativePath="src\keyboard.h"
+				>
+			</File>
+			<File
+				RelativePath="src\mouse.h"
+				>
+			</File>
+			<File
+				RelativePath="src\names.h"
+				>
+			</File>
+			<File
+				RelativePath="src\rts.h"
+				>
+			</File>
+			<File
+				RelativePath="src\scriplib.h"
+				>
+			</File>
+			<File
+				RelativePath="src\soundefs.h"
+				>
+			</File>
+			<File
+				RelativePath="src\sounds.h"
+				>
+			</File>
+			<File
+				RelativePath="src\types.h"
+				>
+			</File>
+			<File
+				RelativePath=".\src\util_lib.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Midi"
+			>
+			<File
+				RelativePath="src\midi\databuf.h"
+				>
+			</File>
+			<File
+				RelativePath="src\midi\win_midiout.cpp"
+				>
+			</File>
+			<File
+				RelativePath="src\midi\win_midiout.h"
+				>
+			</File>
+			<File
+				RelativePath="src\midi\xmidi.cpp"
+				>
+			</File>
+			<File
+				RelativePath="src\midi\xmidi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Audiolib"
+			>
+			<File
+				RelativePath=".\src\audiolib\assert.h"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\dsl.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\fx_man.c"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						ObjectFile="$(IntDir)/$(InputName)1.obj"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						ObjectFile="$(IntDir)/$(InputName)1.obj"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="src\audiolib\ll_man.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\multivoc.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\mv_mix.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\mvreverb.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\nodpmi.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\pitch.c"
+				>
+			</File>
+			<File
+				RelativePath="src\audiolib\user.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Console Source"
+			>
+			<File
+				RelativePath="src\console.c"
+				>
+			</File>
+			<File
+				RelativePath="src\cvar_defs.c"
+				>
+			</File>
+			<File
+				RelativePath="src\cvars.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Console Headers"
+			>
+			<File
+				RelativePath="src\_functio.h"
+				>
+			</File>
+			<File
+				RelativePath="src\console.h"
+				>
+			</File>
+			<File
+				RelativePath="src\cvar_defs.h"
+				>
+			</File>
+			<File
+				RelativePath="src\cvars.h"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
binary files /dev/null b/Game/nuclear2.bmp differ
--- /dev/null
+++ b/Game/src/DbgHelp.h
@@ -1,0 +1,2437 @@
+/*++ BUILD Version: 0001     Increment this if a change has global effects
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    dbghelp.h
+
+Abstract:
+
+    This module defines the prototypes and constants required for the image
+    help routines.
+
+    Contains debugging support routines that are redistributable.
+
+Revision History:
+
+--*/
+
+#ifndef _DBGHELP_
+#define _DBGHELP_
+
+#if _MSC_VER > 1020
+#pragma once
+#endif
+
+
+// As a general principal always call the 64 bit version
+// of every API, if a choice exists.  The 64 bit version
+// works great on 32 bit platforms, and is forward
+// compatible to 64 bit platforms.
+
+#ifdef _WIN64
+#ifndef _IMAGEHLP64
+#define _IMAGEHLP64
+#endif
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _IMAGEHLP_SOURCE_
+#define IMAGEAPI __stdcall
+#define DBHLP_DEPRECIATED
+#else
+#define IMAGEAPI DECLSPEC_IMPORT __stdcall
+#define DBHLP_DEPRECIATED DECLSPEC_DEPRECATED
+#endif
+#define DBHLPAPI IMAGEAPI
+
+#define IMAGE_SEPARATION (64*1024)
+
+typedef struct _LOADED_IMAGE {
+    PSTR                  ModuleName;
+    HANDLE                hFile;
+    PUCHAR                MappedAddress;
+#ifdef _IMAGEHLP64
+    PIMAGE_NT_HEADERS64   FileHeader;
+#else
+    PIMAGE_NT_HEADERS32   FileHeader;
+#endif
+    PIMAGE_SECTION_HEADER LastRvaSection;
+    ULONG                 NumberOfSections;
+    PIMAGE_SECTION_HEADER Sections;
+    ULONG                 Characteristics;
+    BOOLEAN               fSystemImage;
+    BOOLEAN               fDOSImage;
+    LIST_ENTRY            Links;
+    ULONG                 SizeOfImage;
+} LOADED_IMAGE, *PLOADED_IMAGE;
+
+
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFile (
+    PSTR FileName,
+    PSTR SymbolPath,
+    PSTR DebugFilePath
+    );
+
+typedef BOOL
+(CALLBACK *PFIND_DEBUG_FILE_CALLBACK)(
+    HANDLE FileHandle,
+    PSTR FileName,
+    PVOID CallerData
+    );
+
+HANDLE
+IMAGEAPI
+FindDebugInfoFileEx (
+    PSTR FileName,
+    PSTR SymbolPath,
+    PSTR DebugFilePath,
+    PFIND_DEBUG_FILE_CALLBACK Callback,
+    PVOID CallerData
+    );
+
+typedef BOOL
+(CALLBACK *PFINDFILEINPATHCALLBACK)(
+    PSTR  filename,
+    PVOID context
+    );
+
+BOOL
+IMAGEAPI
+SymFindFileInPath(
+    HANDLE hprocess,
+    LPSTR  SearchPath,
+    LPSTR  FileName,
+    PVOID  id,
+    DWORD  two,
+    DWORD  three,
+    DWORD  flags,
+    LPSTR  FilePath,
+    PFINDFILEINPATHCALLBACK callback,
+    PVOID  context
+    );
+
+HANDLE
+IMAGEAPI
+FindExecutableImage(
+    PSTR FileName,
+    PSTR SymbolPath,
+    PSTR ImageFilePath
+    );
+
+typedef BOOL
+(CALLBACK *PFIND_EXE_FILE_CALLBACK)(
+    HANDLE FileHandle,
+    PSTR FileName,
+    PVOID CallerData
+    );
+
+HANDLE
+IMAGEAPI
+FindExecutableImageEx(
+    PSTR FileName,
+    PSTR SymbolPath,
+    PSTR ImageFilePath,
+    PFIND_EXE_FILE_CALLBACK Callback,
+    PVOID CallerData
+    );
+
+PIMAGE_NT_HEADERS
+IMAGEAPI
+ImageNtHeader (
+    IN PVOID Base
+    );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToDataEx (
+    IN PVOID Base,
+    IN BOOLEAN MappedAsImage,
+    IN USHORT DirectoryEntry,
+    OUT PULONG Size,
+    OUT PIMAGE_SECTION_HEADER *FoundHeader OPTIONAL
+    );
+
+PVOID
+IMAGEAPI
+ImageDirectoryEntryToData (
+    IN PVOID Base,
+    IN BOOLEAN MappedAsImage,
+    IN USHORT DirectoryEntry,
+    OUT PULONG Size
+    );
+
+PIMAGE_SECTION_HEADER
+IMAGEAPI
+ImageRvaToSection(
+    IN PIMAGE_NT_HEADERS NtHeaders,
+    IN PVOID Base,
+    IN ULONG Rva
+    );
+
+PVOID
+IMAGEAPI
+ImageRvaToVa(
+    IN PIMAGE_NT_HEADERS NtHeaders,
+    IN PVOID Base,
+    IN ULONG Rva,
+    IN OUT PIMAGE_SECTION_HEADER *LastRvaSection
+    );
+
+// Symbol server exports
+
+typedef BOOL (*PSYMBOLSERVERPROC)(LPCSTR, LPCSTR, PVOID, DWORD, DWORD, LPSTR);
+typedef BOOL (*PSYMBOLSERVEROPENPROC)(VOID);
+typedef BOOL (*PSYMBOLSERVERCLOSEPROC)(VOID);
+typedef BOOL (*PSYMBOLSERVERSETOPTIONSPROC)(UINT_PTR, ULONG64);
+typedef BOOL (CALLBACK *PSYMBOLSERVERCALLBACKPROC)(UINT_PTR action, ULONG64 data, ULONG64 context);
+typedef UINT_PTR (*PSYMBOLSERVERGETOPTIONSPROC)();
+
+#define SSRVOPT_CALLBACK    0x01
+#define SSRVOPT_DWORD       0x02
+#define SSRVOPT_DWORDPTR    0x04
+#define SSRVOPT_GUIDPTR     0x08
+#define SSRVOPT_OLDGUIDPTR  0x10
+#define SSRVOPT_UNATTENDED  0x20
+#define SSRVOPT_RESET    ((ULONG_PTR)-1)
+
+#define SSRVACTION_TRACE 1
+
+
+#ifndef _WIN64
+// This api won't be ported to Win64 - Fix your code.
+
+typedef struct _IMAGE_DEBUG_INFORMATION {
+    LIST_ENTRY List;
+    DWORD ReservedSize;
+    PVOID ReservedMappedBase;
+    USHORT ReservedMachine;
+    USHORT ReservedCharacteristics;
+    DWORD ReservedCheckSum;
+    DWORD ImageBase;
+    DWORD SizeOfImage;
+
+    DWORD ReservedNumberOfSections;
+    PIMAGE_SECTION_HEADER ReservedSections;
+
+    DWORD ReservedExportedNamesSize;
+    PSTR ReservedExportedNames;
+
+    DWORD ReservedNumberOfFunctionTableEntries;
+    PIMAGE_FUNCTION_ENTRY ReservedFunctionTableEntries;
+    DWORD ReservedLowestFunctionStartingAddress;
+    DWORD ReservedHighestFunctionEndingAddress;
+
+    DWORD ReservedNumberOfFpoTableEntries;
+    PFPO_DATA ReservedFpoTableEntries;
+
+    DWORD SizeOfCoffSymbols;
+    PIMAGE_COFF_SYMBOLS_HEADER CoffSymbols;
+
+    DWORD ReservedSizeOfCodeViewSymbols;
+    PVOID ReservedCodeViewSymbols;
+
+    PSTR ImageFilePath;
+    PSTR ImageFileName;
+    PSTR ReservedDebugFilePath;
+
+    DWORD ReservedTimeDateStamp;
+
+    BOOL  ReservedRomImage;
+    PIMAGE_DEBUG_DIRECTORY ReservedDebugDirectory;
+    DWORD ReservedNumberOfDebugDirectories;
+
+    DWORD ReservedOriginalFunctionTableBaseAddress;
+
+    DWORD Reserved[ 2 ];
+
+} IMAGE_DEBUG_INFORMATION, *PIMAGE_DEBUG_INFORMATION;
+
+
+PIMAGE_DEBUG_INFORMATION
+IMAGEAPI
+MapDebugInformation(
+    HANDLE FileHandle,
+    PSTR FileName,
+    PSTR SymbolPath,
+    DWORD ImageBase
+    );
+
+BOOL
+IMAGEAPI
+UnmapDebugInformation(
+    PIMAGE_DEBUG_INFORMATION DebugInfo
+    );
+
+#endif
+
+BOOL
+IMAGEAPI
+SearchTreeForFile(
+    PSTR RootPath,
+    PSTR InputPathName,
+    PSTR OutputPathBuffer
+    );
+
+BOOL
+IMAGEAPI
+MakeSureDirectoryPathExists(
+    PCSTR DirPath
+    );
+
+//
+// UnDecorateSymbolName Flags
+//
+
+#define UNDNAME_COMPLETE                 (0x0000)  // Enable full undecoration
+#define UNDNAME_NO_LEADING_UNDERSCORES   (0x0001)  // Remove leading underscores from MS extended keywords
+#define UNDNAME_NO_MS_KEYWORDS           (0x0002)  // Disable expansion of MS extended keywords
+#define UNDNAME_NO_FUNCTION_RETURNS      (0x0004)  // Disable expansion of return type for primary declaration
+#define UNDNAME_NO_ALLOCATION_MODEL      (0x0008)  // Disable expansion of the declaration model
+#define UNDNAME_NO_ALLOCATION_LANGUAGE   (0x0010)  // Disable expansion of the declaration language specifier
+#define UNDNAME_NO_MS_THISTYPE           (0x0020)  // NYI Disable expansion of MS keywords on the 'this' type for primary declaration
+#define UNDNAME_NO_CV_THISTYPE           (0x0040)  // NYI Disable expansion of CV modifiers on the 'this' type for primary declaration
+#define UNDNAME_NO_THISTYPE              (0x0060)  // Disable all modifiers on the 'this' type
+#define UNDNAME_NO_ACCESS_SPECIFIERS     (0x0080)  // Disable expansion of access specifiers for members
+#define UNDNAME_NO_THROW_SIGNATURES      (0x0100)  // Disable expansion of 'throw-signatures' for functions and pointers to functions
+#define UNDNAME_NO_MEMBER_TYPE           (0x0200)  // Disable expansion of 'static' or 'virtual'ness of members
+#define UNDNAME_NO_RETURN_UDT_MODEL      (0x0400)  // Disable expansion of MS model for UDT returns
+#define UNDNAME_32_BIT_DECODE            (0x0800)  // Undecorate 32-bit decorated names
+#define UNDNAME_NAME_ONLY                (0x1000)  // Crack only the name for primary declaration;
+                                                                                                   //  return just [scope::]name.  Does expand template params
+#define UNDNAME_NO_ARGUMENTS             (0x2000)  // Don't undecorate arguments to function
+#define UNDNAME_NO_SPECIAL_SYMS          (0x4000)  // Don't undecorate special names (v-table, vcall, vector xxx, metatype, etc)
+
+DWORD
+IMAGEAPI
+WINAPI
+UnDecorateSymbolName(
+    PCSTR   DecoratedName,         // Name to undecorate
+    PSTR    UnDecoratedName,       // If NULL, it will be allocated
+    DWORD    UndecoratedLength,     // The maximym length
+    DWORD    Flags                  // See above.
+    );
+
+
+//
+// these values are used for synthesized file types
+// that can be passed in as image headers instead of
+// the standard ones from ntimage.h
+//
+
+#define DBHHEADER_DEBUGDIRS     0x1
+
+typedef struct _DBGHELP_MODLOAD_DATA {
+    DWORD   ssize;                  // size of this struct
+    DWORD   ssig;                   // signature identifying the passed data
+    PVOID   data;                   // pointer to passed data
+    DWORD   size;                   // size of passed data
+    DWORD   flags;                  // options
+} MODLOAD_DATA, *PMODLOAD_DATA;
+
+//
+// StackWalking API
+//
+
+typedef enum {
+    AddrMode1616,
+    AddrMode1632,
+    AddrModeReal,
+    AddrModeFlat
+} ADDRESS_MODE;
+
+typedef struct _tagADDRESS64 {
+    DWORD64       Offset;
+    WORD          Segment;
+    ADDRESS_MODE  Mode;
+} ADDRESS64, *LPADDRESS64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define ADDRESS ADDRESS64
+#define LPADDRESS LPADDRESS64
+#else
+typedef struct _tagADDRESS {
+    DWORD         Offset;
+    WORD          Segment;
+    ADDRESS_MODE  Mode;
+} ADDRESS, *LPADDRESS;
+
+__inline
+void
+Address32To64(
+    LPADDRESS a32,
+    LPADDRESS64 a64
+    )
+{
+    a64->Offset = (ULONG64)(LONG64)(LONG)a32->Offset;
+    a64->Segment = a32->Segment;
+    a64->Mode = a32->Mode;
+}
+
+__inline
+void
+Address64To32(
+    LPADDRESS64 a64,
+    LPADDRESS a32
+    )
+{
+    a32->Offset = (ULONG)a64->Offset;
+    a32->Segment = a64->Segment;
+    a32->Mode = a64->Mode;
+}
+#endif
+
+//
+// This structure is included in the STACKFRAME structure,
+// and is used to trace through usermode callbacks in a thread's
+// kernel stack.  The values must be copied by the kernel debugger
+// from the DBGKD_GET_VERSION and WAIT_STATE_CHANGE packets.
+//
+
+//
+// New KDHELP structure for 64 bit system support.
+// This structure is preferred in new code.
+//
+typedef struct _KDHELP64 {
+
+    //
+    // address of kernel thread object, as provided in the
+    // WAIT_STATE_CHANGE packet.
+    //
+    DWORD64   Thread;
+
+    //
+    // offset in thread object to pointer to the current callback frame
+    // in kernel stack.
+    //
+    DWORD   ThCallbackStack;
+
+    //
+    // offset in thread object to pointer to the current callback backing
+    // store frame in kernel stack.
+    //
+    DWORD   ThCallbackBStore;
+
+    //
+    // offsets to values in frame:
+    //
+    // address of next callback frame
+    DWORD   NextCallback;
+
+    // address of saved frame pointer (if applicable)
+    DWORD   FramePointer;
+
+
+    //
+    // Address of the kernel function that calls out to user mode
+    //
+    DWORD64   KiCallUserMode;
+
+    //
+    // Address of the user mode dispatcher function
+    //
+    DWORD64   KeUserCallbackDispatcher;
+
+    //
+    // Lowest kernel mode address
+    //
+    DWORD64   SystemRangeStart;
+
+    DWORD64  Reserved[8];
+
+} KDHELP64, *PKDHELP64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define KDHELP KDHELP64
+#define PKDHELP PKDHELP64
+#else
+typedef struct _KDHELP {
+
+    //
+    // address of kernel thread object, as provided in the
+    // WAIT_STATE_CHANGE packet.
+    //
+    DWORD   Thread;
+
+    //
+    // offset in thread object to pointer to the current callback frame
+    // in kernel stack.
+    //
+    DWORD   ThCallbackStack;
+
+    //
+    // offsets to values in frame:
+    //
+    // address of next callback frame
+    DWORD   NextCallback;
+
+    // address of saved frame pointer (if applicable)
+    DWORD   FramePointer;
+
+    //
+    // Address of the kernel function that calls out to user mode
+    //
+    DWORD   KiCallUserMode;
+
+    //
+    // Address of the user mode dispatcher function
+    //
+    DWORD   KeUserCallbackDispatcher;
+
+    //
+    // Lowest kernel mode address
+    //
+    DWORD   SystemRangeStart;
+
+    //
+    // offset in thread object to pointer to the current callback backing
+    // store frame in kernel stack.
+    //
+    DWORD   ThCallbackBStore;
+
+    DWORD  Reserved[8];
+
+} KDHELP, *PKDHELP;
+
+__inline
+void
+KdHelp32To64(
+    PKDHELP p32,
+    PKDHELP64 p64
+    )
+{
+    p64->Thread = p32->Thread;
+    p64->ThCallbackStack = p32->ThCallbackStack;
+    p64->NextCallback = p32->NextCallback;
+    p64->FramePointer = p32->FramePointer;
+    p64->KiCallUserMode = p32->KiCallUserMode;
+    p64->KeUserCallbackDispatcher = p32->KeUserCallbackDispatcher;
+    p64->SystemRangeStart = p32->SystemRangeStart;
+}
+#endif
+
+typedef struct _tagSTACKFRAME64 {
+    ADDRESS64   AddrPC;               // program counter
+    ADDRESS64   AddrReturn;           // return address
+    ADDRESS64   AddrFrame;            // frame pointer
+    ADDRESS64   AddrStack;            // stack pointer
+    ADDRESS64   AddrBStore;           // backing store pointer
+    PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL
+    DWORD64     Params[4];            // possible arguments to the function
+    BOOL        Far;                  // WOW far call
+    BOOL        Virtual;              // is this a virtual frame?
+    DWORD64     Reserved[3];
+    KDHELP64    KdHelp;
+} STACKFRAME64, *LPSTACKFRAME64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define STACKFRAME STACKFRAME64
+#define LPSTACKFRAME LPSTACKFRAME64
+#else
+typedef struct _tagSTACKFRAME {
+    ADDRESS     AddrPC;               // program counter
+    ADDRESS     AddrReturn;           // return address
+    ADDRESS     AddrFrame;            // frame pointer
+    ADDRESS     AddrStack;            // stack pointer
+    PVOID       FuncTableEntry;       // pointer to pdata/fpo or NULL
+    DWORD       Params[4];            // possible arguments to the function
+    BOOL        Far;                  // WOW far call
+    BOOL        Virtual;              // is this a virtual frame?
+    DWORD       Reserved[3];
+    KDHELP      KdHelp;
+    ADDRESS     AddrBStore;           // backing store pointer
+} STACKFRAME, *LPSTACKFRAME;
+#endif
+
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(
+    HANDLE      hProcess,
+    DWORD64     qwBaseAddress,
+    PVOID       lpBuffer,
+    DWORD       nSize,
+    LPDWORD     lpNumberOfBytesRead
+    );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(
+    HANDLE  hProcess,
+    DWORD64 AddrBase
+    );
+
+typedef
+DWORD64
+(__stdcall *PGET_MODULE_BASE_ROUTINE64)(
+    HANDLE  hProcess,
+    DWORD64 Address
+    );
+
+typedef
+DWORD64
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(
+    HANDLE    hProcess,
+    HANDLE    hThread,
+    LPADDRESS64 lpaddr
+    );
+
+BOOL
+IMAGEAPI
+StackWalk64(
+    DWORD                             MachineType,
+    HANDLE                            hProcess,
+    HANDLE                            hThread,
+    LPSTACKFRAME64                    StackFrame,
+    PVOID                             ContextRecord,
+    PREAD_PROCESS_MEMORY_ROUTINE64    ReadMemoryRoutine,
+    PFUNCTION_TABLE_ACCESS_ROUTINE64  FunctionTableAccessRoutine,
+    PGET_MODULE_BASE_ROUTINE64        GetModuleBaseRoutine,
+    PTRANSLATE_ADDRESS_ROUTINE64      TranslateAddress
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PREAD_PROCESS_MEMORY_ROUTINE PREAD_PROCESS_MEMORY_ROUTINE64
+#define PFUNCTION_TABLE_ACCESS_ROUTINE PFUNCTION_TABLE_ACCESS_ROUTINE64
+#define PGET_MODULE_BASE_ROUTINE PGET_MODULE_BASE_ROUTINE64
+#define PTRANSLATE_ADDRESS_ROUTINE PTRANSLATE_ADDRESS_ROUTINE64
+
+#define StackWalk StackWalk64
+
+#else
+
+typedef
+BOOL
+(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE)(
+    HANDLE  hProcess,
+    DWORD   lpBaseAddress,
+    PVOID   lpBuffer,
+    DWORD   nSize,
+    PDWORD  lpNumberOfBytesRead
+    );
+
+typedef
+PVOID
+(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE)(
+    HANDLE  hProcess,
+    DWORD   AddrBase
+    );
+
+typedef
+DWORD
+(__stdcall *PGET_MODULE_BASE_ROUTINE)(
+    HANDLE  hProcess,
+    DWORD   Address
+    );
+
+typedef
+DWORD
+(__stdcall *PTRANSLATE_ADDRESS_ROUTINE)(
+    HANDLE    hProcess,
+    HANDLE    hThread,
+    LPADDRESS lpaddr
+    );
+
+BOOL
+IMAGEAPI
+StackWalk(
+    DWORD                             MachineType,
+    HANDLE                            hProcess,
+    HANDLE                            hThread,
+    LPSTACKFRAME                      StackFrame,
+    PVOID                             ContextRecord,
+    PREAD_PROCESS_MEMORY_ROUTINE      ReadMemoryRoutine,
+    PFUNCTION_TABLE_ACCESS_ROUTINE    FunctionTableAccessRoutine,
+    PGET_MODULE_BASE_ROUTINE          GetModuleBaseRoutine,
+    PTRANSLATE_ADDRESS_ROUTINE        TranslateAddress
+    );
+
+#endif
+
+
+#define API_VERSION_NUMBER 9
+
+typedef struct API_VERSION {
+    USHORT  MajorVersion;
+    USHORT  MinorVersion;
+    USHORT  Revision;
+    USHORT  Reserved;
+} API_VERSION, *LPAPI_VERSION;
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersion(
+    VOID
+    );
+
+LPAPI_VERSION
+IMAGEAPI
+ImagehlpApiVersionEx(
+    LPAPI_VERSION AppVersion
+    );
+
+DWORD
+IMAGEAPI
+GetTimestampForLoadedLibrary(
+    HMODULE Module
+    );
+
+//
+// typedefs for function pointers
+//
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK64)(
+    PSTR ModuleName,
+    DWORD64 BaseOfDll,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64)(
+    PSTR SymbolName,
+    DWORD64 SymbolAddress,
+    ULONG SymbolSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK64W)(
+    PWSTR SymbolName,
+    DWORD64 SymbolAddress,
+    ULONG SymbolSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK64)(
+    PSTR ModuleName,
+    DWORD64 ModuleBase,
+    ULONG ModuleSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK64)(
+    HANDLE  hProcess,
+    ULONG   ActionCode,
+    ULONG64 CallbackData,
+    ULONG64 UserContext
+    );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK)(
+    HANDLE  hProcess,
+    DWORD   AddrBase,
+    PVOID   UserContext
+    );
+
+typedef
+PVOID
+(CALLBACK *PSYMBOL_FUNCENTRY_CALLBACK64)(
+    HANDLE  hProcess,
+    ULONG64 AddrBase,
+    ULONG64 UserContext
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+
+#define PSYM_ENUMMODULES_CALLBACK PSYM_ENUMMODULES_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACK PSYM_ENUMSYMBOLS_CALLBACK64
+#define PSYM_ENUMSYMBOLS_CALLBACKW PSYM_ENUMSYMBOLS_CALLBACK64W
+#define PENUMLOADED_MODULES_CALLBACK PENUMLOADED_MODULES_CALLBACK64
+#define PSYMBOL_REGISTERED_CALLBACK PSYMBOL_REGISTERED_CALLBACK64
+#define PSYMBOL_FUNCENTRY_CALLBACK PSYMBOL_FUNCENTRY_CALLBACK64
+
+#else
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMMODULES_CALLBACK)(
+    PSTR  ModuleName,
+    ULONG BaseOfDll,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK)(
+    PSTR  SymbolName,
+    ULONG SymbolAddress,
+    ULONG SymbolSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSYMBOLS_CALLBACKW)(
+    PWSTR  SymbolName,
+    ULONG SymbolAddress,
+    ULONG SymbolSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PENUMLOADED_MODULES_CALLBACK)(
+    PSTR  ModuleName,
+    ULONG ModuleBase,
+    ULONG ModuleSize,
+    PVOID UserContext
+    );
+
+typedef BOOL
+(CALLBACK *PSYMBOL_REGISTERED_CALLBACK)(
+    HANDLE  hProcess,
+    ULONG   ActionCode,
+    PVOID   CallbackData,
+    PVOID   UserContext
+    );
+
+#endif
+
+
+//
+// symbol flags
+//
+
+#define SYMF_OMAP_GENERATED   0x00000001
+#define SYMF_OMAP_MODIFIED    0x00000002
+#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
+ #define SYMF_USER_GENERATED   0x00000004
+#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
+#define SYMF_REGISTER         0x00000008
+#define SYMF_REGREL           0x00000010
+#define SYMF_FRAMEREL         0x00000020
+#define SYMF_PARAMETER        0x00000040
+#define SYMF_LOCAL            0x00000080
+#define SYMF_CONSTANT         0x00000100
+#define SYMF_EXPORT           0x00000200
+#define SYMF_FORWARDER        0x00000400
+#define SYMF_FUNCTION         0x00000800
+//
+// symbol type enumeration
+//
+typedef enum {
+    SymNone = 0,
+    SymCoff,
+    SymCv,
+    SymPdb,
+    SymExport,
+    SymDeferred,
+    SymSym,       // .sym file
+    SymDia,
+    NumSymTypes
+} SYM_TYPE;
+
+//
+// symbol data structure
+//
+
+typedef struct _IMAGEHLP_SYMBOL64 {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL64)
+    DWORD64                     Address;                // virtual address including dll base address
+    DWORD                       Size;                   // estimated size of symbol, can be zero
+    DWORD                       Flags;                  // info about the symbols, see the SYMF defines
+    DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'
+    CHAR                        Name[1];                // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_SYMBOL IMAGEHLP_SYMBOL64
+#define PIMAGEHLP_SYMBOL PIMAGEHLP_SYMBOL64
+#else
+typedef struct _IMAGEHLP_SYMBOL {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_SYMBOL)
+    DWORD                       Address;                // virtual address including dll base address
+    DWORD                       Size;                   // estimated size of symbol, can be zero
+    DWORD                       Flags;                  // info about the symbols, see the SYMF defines
+    DWORD                       MaxNameLength;          // maximum size of symbol name in 'Name'
+    CHAR                        Name[1];                // symbol name (null terminated string)
+} IMAGEHLP_SYMBOL, *PIMAGEHLP_SYMBOL;
+#endif
+
+//
+// module data structure
+//
+
+typedef struct _IMAGEHLP_MODULE64 {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
+    DWORD64                     BaseOfImage;            // base load address of module
+    DWORD                       ImageSize;              // virtual size of the loaded module
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       NumSyms;                // number of symbols in the symbol table
+    SYM_TYPE                    SymType;                // type of symbols loaded
+    CHAR                        ModuleName[32];         // module name
+    CHAR                        ImageName[256];         // image name
+    CHAR                        LoadedImageName[256];   // symbol file name
+} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
+
+typedef struct _IMAGEHLP_MODULE64W {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE64)
+    DWORD64                     BaseOfImage;            // base load address of module
+    DWORD                       ImageSize;              // virtual size of the loaded module
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       NumSyms;                // number of symbols in the symbol table
+    SYM_TYPE                    SymType;                // type of symbols loaded
+    WCHAR                       ModuleName[32];         // module name
+    WCHAR                       ImageName[256];         // image name
+    WCHAR                       LoadedImageName[256];   // symbol file name
+} IMAGEHLP_MODULEW64, *PIMAGEHLP_MODULEW64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_MODULE IMAGEHLP_MODULE64
+#define PIMAGEHLP_MODULE PIMAGEHLP_MODULE64
+#define IMAGEHLP_MODULEW IMAGEHLP_MODULEW64
+#define PIMAGEHLP_MODULEW PIMAGEHLP_MODULEW64
+#else
+typedef struct _IMAGEHLP_MODULE {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE)
+    DWORD                       BaseOfImage;            // base load address of module
+    DWORD                       ImageSize;              // virtual size of the loaded module
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       NumSyms;                // number of symbols in the symbol table
+    SYM_TYPE                    SymType;                // type of symbols loaded
+    CHAR                        ModuleName[32];         // module name
+    CHAR                        ImageName[256];         // image name
+    CHAR                        LoadedImageName[256];   // symbol file name
+} IMAGEHLP_MODULE, *PIMAGEHLP_MODULE;
+
+typedef struct _IMAGEHLP_MODULEW {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_MODULE)
+    DWORD                       BaseOfImage;            // base load address of module
+    DWORD                       ImageSize;              // virtual size of the loaded module
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       NumSyms;                // number of symbols in the symbol table
+    SYM_TYPE                    SymType;                // type of symbols loaded
+    WCHAR                       ModuleName[32];         // module name
+    WCHAR                       ImageName[256];         // image name
+    WCHAR                       LoadedImageName[256];   // symbol file name
+} IMAGEHLP_MODULEW, *PIMAGEHLP_MODULEW;
+#endif
+
+//
+// source file line data structure
+//
+
+typedef struct _IMAGEHLP_LINE64 {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE64)
+    PVOID                       Key;                    // internal
+    DWORD                       LineNumber;             // line number in file
+    PCHAR                       FileName;               // full filename
+    DWORD64                     Address;                // first instruction of line
+} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_LINE IMAGEHLP_LINE64
+#define PIMAGEHLP_LINE PIMAGEHLP_LINE64
+#else
+typedef struct _IMAGEHLP_LINE {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_LINE)
+    PVOID                       Key;                    // internal
+    DWORD                       LineNumber;             // line number in file
+    PCHAR                       FileName;               // full filename
+    DWORD                       Address;                // first instruction of line
+} IMAGEHLP_LINE, *PIMAGEHLP_LINE;
+#endif
+
+//
+// source file structure
+//
+
+typedef struct _SOURCEFILE {
+    DWORD64                     ModBase;                // base address of loaded module
+    PCHAR                       FileName;               // full filename of source
+} SOURCEFILE, *PSOURCEFILE;
+
+//
+// data structures used for registered symbol callbacks
+//
+
+#define CBA_DEFERRED_SYMBOL_LOAD_START          0x00000001
+#define CBA_DEFERRED_SYMBOL_LOAD_COMPLETE       0x00000002
+#define CBA_DEFERRED_SYMBOL_LOAD_FAILURE        0x00000003
+#define CBA_SYMBOLS_UNLOADED                    0x00000004
+#define CBA_DUPLICATE_SYMBOL                    0x00000005
+#define CBA_READ_MEMORY                         0x00000006
+#define CBA_DEFERRED_SYMBOL_LOAD_CANCEL         0x00000007
+#define CBA_SET_OPTIONS                         0x00000008
+#define CBA_EVENT                               0x00000010
+#define CBA_DEBUG_INFO                          0x10000000
+
+typedef struct _IMAGEHLP_CBA_READ_MEMORY {
+    DWORD64   addr;                                     // address to read from
+    PVOID     buf;                                      // buffer to read to
+    DWORD     bytes;                                    // amount of bytes to read
+    DWORD    *bytesread;                                // pointer to store amount of bytes read
+} IMAGEHLP_CBA_READ_MEMORY, *PIMAGEHLP_CBA_READ_MEMORY;
+
+enum {
+    sevInfo = 0,
+    sevProblem,
+    sevAttn,
+    sevFatal,
+    sevMax  // unused
+};
+
+typedef struct _IMAGEHLP_CBA_EVENT {
+    DWORD severity;                                     // values from sevInfo to sevFatal
+    DWORD code;                                         // numerical code IDs the error
+    PCHAR desc;                                         // may contain a text description of the error
+    PVOID object;                                       // value dependant upon the error code
+} IMAGEHLP_CBA_EVENT, *PIMAGEHLP_CBA_EVENT;
+
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD64 {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64)
+    DWORD64                     BaseOfImage;            // base load address of module
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    CHAR                        FileName[MAX_PATH];     // symbols file or image name
+    BOOLEAN                     Reparse;                // load failure reparse
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD64, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DEFERRED_SYMBOL_LOAD IMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#define PIMAGEHLP_DEFERRED_SYMBOL_LOAD PIMAGEHLP_DEFERRED_SYMBOL_LOAD64
+#else
+typedef struct _IMAGEHLP_DEFERRED_SYMBOL_LOAD {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD)
+    DWORD                       BaseOfImage;            // base load address of module
+    DWORD                       CheckSum;               // checksum from the pe header
+    DWORD                       TimeDateStamp;          // date/time stamp from pe header
+    CHAR                        FileName[MAX_PATH];     // symbols file or image name
+    BOOLEAN                     Reparse;                // load failure reparse
+} IMAGEHLP_DEFERRED_SYMBOL_LOAD, *PIMAGEHLP_DEFERRED_SYMBOL_LOAD;
+#endif
+
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL64 {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL64)
+    DWORD                       NumberOfDups;           // number of duplicates in the Symbol array
+    PIMAGEHLP_SYMBOL64          Symbol;                 // array of duplicate symbols
+    DWORD                       SelectedSymbol;         // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL64, *PIMAGEHLP_DUPLICATE_SYMBOL64;
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define IMAGEHLP_DUPLICATE_SYMBOL IMAGEHLP_DUPLICATE_SYMBOL64
+#define PIMAGEHLP_DUPLICATE_SYMBOL PIMAGEHLP_DUPLICATE_SYMBOL64
+#else
+typedef struct _IMAGEHLP_DUPLICATE_SYMBOL {
+    DWORD                       SizeOfStruct;           // set to sizeof(IMAGEHLP_DUPLICATE_SYMBOL)
+    DWORD                       NumberOfDups;           // number of duplicates in the Symbol array
+    PIMAGEHLP_SYMBOL            Symbol;                 // array of duplicate symbols
+    DWORD                       SelectedSymbol;         // symbol selected (-1 to start)
+} IMAGEHLP_DUPLICATE_SYMBOL, *PIMAGEHLP_DUPLICATE_SYMBOL;
+#endif
+
+
+//
+// options that are set/returned by SymSetOptions() & SymGetOptions()
+// these are used as a mask
+//
+#define SYMOPT_CASE_INSENSITIVE         0x00000001
+#define SYMOPT_UNDNAME                  0x00000002
+#define SYMOPT_DEFERRED_LOADS           0x00000004
+#define SYMOPT_NO_CPP                   0x00000008
+#define SYMOPT_LOAD_LINES               0x00000010
+#define SYMOPT_OMAP_FIND_NEAREST        0x00000020
+#define SYMOPT_LOAD_ANYTHING            0x00000040
+#define SYMOPT_IGNORE_CVREC             0x00000080
+#define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100
+#define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200
+#define SYMOPT_EXACT_SYMBOLS            0x00000400
+#define SYMOPT_WILD_UNDERSCORE          0x00000800
+#define SYMOPT_USE_DEFAULTS             0x00001000
+#define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000
+
+#define SYMOPT_DEBUG                    0x80000000
+
+DWORD
+IMAGEAPI
+SymSetOptions(
+    IN DWORD   SymOptions
+    );
+
+DWORD
+IMAGEAPI
+SymGetOptions(
+    VOID
+    );
+
+BOOL
+IMAGEAPI
+SymCleanup(
+    IN HANDLE hProcess
+    );
+
+BOOL
+IMAGEAPI
+SymMatchString(
+    IN LPSTR string,
+    IN LPSTR expression,
+    IN BOOL  fCase
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMSOURCFILES_CALLBACK)(
+    PSOURCEFILE pSourceFile,
+    PVOID       UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymEnumSourceFiles(
+    IN HANDLE  hProcess,
+    IN ULONG64 ModBase,
+    IN LPSTR   Mask,
+    IN PSYM_ENUMSOURCFILES_CALLBACK cbSrcFiles,
+    IN PVOID   UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymEnumerateModules64(
+    IN HANDLE                       hProcess,
+    IN PSYM_ENUMMODULES_CALLBACK64  EnumModulesCallback,
+    IN PVOID                        UserContext
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateModules SymEnumerateModules64
+#else
+BOOL
+IMAGEAPI
+SymEnumerateModules(
+    IN HANDLE                     hProcess,
+    IN PSYM_ENUMMODULES_CALLBACK  EnumModulesCallback,
+    IN PVOID                      UserContext
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymEnumerateSymbols64(
+    IN HANDLE                       hProcess,
+    IN DWORD64                      BaseOfDll,
+    IN PSYM_ENUMSYMBOLS_CALLBACK64  EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW64(
+    IN HANDLE                       hProcess,
+    IN DWORD64                      BaseOfDll,
+    IN PSYM_ENUMSYMBOLS_CALLBACK64W EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymEnumerateSymbols SymEnumerateSymbols64
+#define SymEnumerateSymbolsW SymEnumerateSymbolsW64
+#else
+BOOL
+IMAGEAPI
+SymEnumerateSymbols(
+    IN HANDLE                     hProcess,
+    IN DWORD                      BaseOfDll,
+    IN PSYM_ENUMSYMBOLS_CALLBACK  EnumSymbolsCallback,
+    IN PVOID                      UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymEnumerateSymbolsW(
+    IN HANDLE                       hProcess,
+    IN DWORD                        BaseOfDll,
+    IN PSYM_ENUMSYMBOLS_CALLBACKW   EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+#endif
+
+BOOL
+IMAGEAPI
+EnumerateLoadedModules64(
+    IN HANDLE                           hProcess,
+    IN PENUMLOADED_MODULES_CALLBACK64   EnumLoadedModulesCallback,
+    IN PVOID                            UserContext
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define EnumerateLoadedModules EnumerateLoadedModules64
+#else
+BOOL
+IMAGEAPI
+EnumerateLoadedModules(
+    IN HANDLE                         hProcess,
+    IN PENUMLOADED_MODULES_CALLBACK   EnumLoadedModulesCallback,
+    IN PVOID                          UserContext
+    );
+#endif
+
+PVOID
+IMAGEAPI
+SymFunctionTableAccess64(
+    HANDLE  hProcess,
+    DWORD64 AddrBase
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymFunctionTableAccess SymFunctionTableAccess64
+#else
+PVOID
+IMAGEAPI
+SymFunctionTableAccess(
+    HANDLE  hProcess,
+    DWORD   AddrBase
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetModuleInfo64(
+    IN  HANDLE                  hProcess,
+    IN  DWORD64                 qwAddr,
+    OUT PIMAGEHLP_MODULE64      ModuleInfo
+    );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW64(
+    IN  HANDLE                  hProcess,
+    IN  DWORD64                 qwAddr,
+    OUT PIMAGEHLP_MODULEW64     ModuleInfo
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleInfo   SymGetModuleInfo64
+#define SymGetModuleInfoW  SymGetModuleInfoW64
+#else
+BOOL
+IMAGEAPI
+SymGetModuleInfo(
+    IN  HANDLE              hProcess,
+    IN  DWORD               dwAddr,
+    OUT PIMAGEHLP_MODULE  ModuleInfo
+    );
+
+BOOL
+IMAGEAPI
+SymGetModuleInfoW(
+    IN  HANDLE              hProcess,
+    IN  DWORD               dwAddr,
+    OUT PIMAGEHLP_MODULEW  ModuleInfo
+    );
+#endif
+
+DWORD64
+IMAGEAPI
+SymGetModuleBase64(
+    IN  HANDLE              hProcess,
+    IN  DWORD64             qwAddr
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetModuleBase SymGetModuleBase64
+#else
+DWORD
+IMAGEAPI
+SymGetModuleBase(
+    IN  HANDLE              hProcess,
+    IN  DWORD               dwAddr
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymNext64(
+    IN     HANDLE              hProcess,
+    IN OUT PIMAGEHLP_SYMBOL64  Symbol
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymNext SymGetSymNext64
+#else
+BOOL
+IMAGEAPI
+SymGetSymNext(
+    IN     HANDLE            hProcess,
+    IN OUT PIMAGEHLP_SYMBOL  Symbol
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetSymPrev64(
+    IN     HANDLE              hProcess,
+    IN OUT PIMAGEHLP_SYMBOL64  Symbol
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymPrev SymGetSymPrev64
+#else
+BOOL
+IMAGEAPI
+SymGetSymPrev(
+    IN     HANDLE            hProcess,
+    IN OUT PIMAGEHLP_SYMBOL  Symbol
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineFromAddr64(
+    IN  HANDLE                  hProcess,
+    IN  DWORD64                 qwAddr,
+    OUT PDWORD                  pdwDisplacement,
+    OUT PIMAGEHLP_LINE64        Line
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromAddr SymGetLineFromAddr64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromAddr(
+    IN  HANDLE                hProcess,
+    IN  DWORD                 dwAddr,
+    OUT PDWORD                pdwDisplacement,
+    OUT PIMAGEHLP_LINE        Line
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineFromName64(
+    IN     HANDLE               hProcess,
+    IN     PSTR                 ModuleName,
+    IN     PSTR                 FileName,
+    IN     DWORD                dwLineNumber,
+       OUT PLONG                plDisplacement,
+    IN OUT PIMAGEHLP_LINE64     Line
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineFromName SymGetLineFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetLineFromName(
+    IN     HANDLE             hProcess,
+    IN     PSTR               ModuleName,
+    IN     PSTR               FileName,
+    IN     DWORD              dwLineNumber,
+       OUT PLONG              plDisplacement,
+    IN OUT PIMAGEHLP_LINE     Line
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLineNext64(
+    IN     HANDLE               hProcess,
+    IN OUT PIMAGEHLP_LINE64     Line
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLineNext SymGetLineNext64
+#else
+BOOL
+IMAGEAPI
+SymGetLineNext(
+    IN     HANDLE             hProcess,
+    IN OUT PIMAGEHLP_LINE     Line
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymGetLinePrev64(
+    IN     HANDLE               hProcess,
+    IN OUT PIMAGEHLP_LINE64     Line
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetLinePrev SymGetLinePrev64
+#else
+BOOL
+IMAGEAPI
+SymGetLinePrev(
+    IN     HANDLE             hProcess,
+    IN OUT PIMAGEHLP_LINE     Line
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymMatchFileName(
+    IN  PSTR  FileName,
+    IN  PSTR  Match,
+    OUT PSTR *FileNameStop,
+    OUT PSTR *MatchStop
+    );
+
+BOOL
+IMAGEAPI
+SymInitialize(
+    IN HANDLE   hProcess,
+    IN PSTR     UserSearchPath,
+    IN BOOL     fInvadeProcess
+    );
+
+BOOL
+IMAGEAPI
+SymGetSearchPath(
+    IN  HANDLE          hProcess,
+    OUT PSTR            SearchPath,
+    IN  DWORD           SearchPathLength
+    );
+
+BOOL
+IMAGEAPI
+SymSetSearchPath(
+    IN HANDLE           hProcess,
+    IN PSTR             SearchPath
+    );
+
+DWORD64
+IMAGEAPI
+SymLoadModule64(
+    IN  HANDLE          hProcess,
+    IN  HANDLE          hFile,
+    IN  PSTR            ImageName,
+    IN  PSTR            ModuleName,
+    IN  DWORD64         BaseOfDll,
+    IN  DWORD           SizeOfDll
+    );
+
+DWORD64
+IMAGEAPI
+SymLoadModuleEx(
+    IN  HANDLE         hProcess,
+    IN  HANDLE         hFile,
+    IN  PSTR           ImageName,
+    IN  PSTR           ModuleName,
+    IN  DWORD64        BaseOfDll,
+    IN  DWORD          DllSize,
+    IN  PMODLOAD_DATA  Data,
+    IN  DWORD          Flags
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymLoadModule SymLoadModule64
+#else
+DWORD
+IMAGEAPI
+SymLoadModule(
+    IN  HANDLE          hProcess,
+    IN  HANDLE          hFile,
+    IN  PSTR            ImageName,
+    IN  PSTR            ModuleName,
+    IN  DWORD           BaseOfDll,
+    IN  DWORD           SizeOfDll
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymUnloadModule64(
+    IN  HANDLE          hProcess,
+    IN  DWORD64         BaseOfDll
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnloadModule SymUnloadModule64
+#else
+BOOL
+IMAGEAPI
+SymUnloadModule(
+    IN  HANDLE          hProcess,
+    IN  DWORD           BaseOfDll
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymUnDName64(
+    IN  PIMAGEHLP_SYMBOL64 sym,               // Symbol to undecorate
+    OUT PSTR               UnDecName,         // Buffer to store undecorated name in
+    IN  DWORD              UnDecNameLength    // Size of the buffer
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymUnDName SymUnDName64
+#else
+BOOL
+IMAGEAPI
+SymUnDName(
+    IN  PIMAGEHLP_SYMBOL sym,               // Symbol to undecorate
+    OUT PSTR             UnDecName,         // Buffer to store undecorated name in
+    IN  DWORD            UnDecNameLength    // Size of the buffer
+    );
+#endif
+
+BOOL
+IMAGEAPI
+SymRegisterCallback64(
+    IN HANDLE                        hProcess,
+    IN PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
+    IN ULONG64                       UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback64(
+    IN HANDLE                       hProcess,
+    IN PSYMBOL_FUNCENTRY_CALLBACK64 CallbackFunction,
+    IN ULONG64                      UserContext
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymRegisterCallback SymRegisterCallback64
+#define SymRegisterFunctionEntryCallback SymRegisterFunctionEntryCallback64
+#else
+BOOL
+IMAGEAPI
+SymRegisterCallback(
+    IN HANDLE                      hProcess,
+    IN PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
+    IN PVOID                       UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymRegisterFunctionEntryCallback(
+    IN HANDLE                     hProcess,
+    IN PSYMBOL_FUNCENTRY_CALLBACK CallbackFunction,
+    IN PVOID                      UserContext
+    );
+#endif
+
+
+typedef struct _IMAGEHLP_SYMBOL_SRC {
+    DWORD sizeofstruct;
+    DWORD type;
+    char  file[MAX_PATH];
+} IMAGEHLP_SYMBOL_SRC, *PIMAGEHLP_SYMBOL_SRC;
+
+typedef struct _MODULE_TYPE_INFO { // AKA TYPTYP
+    USHORT      dataLength;
+    USHORT      leaf;
+    BYTE        data[1];
+} MODULE_TYPE_INFO, *PMODULE_TYPE_INFO;
+
+#define IMAGEHLP_SYMBOL_INFO_VALUEPRESENT          1
+#define IMAGEHLP_SYMBOL_INFO_REGISTER              SYMF_REGISTER        //  0x08
+#define IMAGEHLP_SYMBOL_INFO_REGRELATIVE           SYMF_REGREL          //  0x10
+#define IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE         SYMF_FRAMEREL        //  0x20
+#define IMAGEHLP_SYMBOL_INFO_PARAMETER             SYMF_PARAMETER       //  0x40
+#define IMAGEHLP_SYMBOL_INFO_LOCAL                 SYMF_LOCAL           //  0x80
+#define IMAGEHLP_SYMBOL_INFO_CONSTANT              SYMF_CONSTANT        // 0x100
+#define IMAGEHLP_SYMBOL_FUNCTION                   SYMF_FUNCTION        // 0x800
+
+typedef struct _SYMBOL_INFO {
+    ULONG       SizeOfStruct;
+    ULONG       TypeIndex;        // Type Index of symbol
+    ULONG64     Reserved[2];
+    ULONG       Reserved2;
+    ULONG       Size;
+    ULONG64     ModBase;          // Base Address of module comtaining this symbol
+    ULONG       Flags;
+    ULONG64     Value;            // Value of symbol, ValuePresent should be 1
+    ULONG64     Address;          // Address of symbol including base address of module
+    ULONG       Register;         // register holding value or pointer to value
+    ULONG       Scope;            // scope of the symbol
+    ULONG       Tag;              // pdb classification
+    ULONG       NameLen;          // Actual length of name
+    ULONG       MaxNameLen;
+    CHAR        Name[1];          // Name of symbol
+} SYMBOL_INFO, *PSYMBOL_INFO;
+
+typedef struct _IMAGEHLP_STACK_FRAME
+{
+    ULONG64 InstructionOffset;
+    ULONG64 ReturnOffset;
+    ULONG64 FrameOffset;
+    ULONG64 StackOffset;
+    ULONG64 BackingStoreOffset;
+    ULONG64 FuncTableEntry;
+    ULONG64 Params[4];
+    ULONG64 Reserved[5];
+    BOOL    Virtual;
+    ULONG   Reserved2;
+} IMAGEHLP_STACK_FRAME, *PIMAGEHLP_STACK_FRAME;
+
+typedef VOID IMAGEHLP_CONTEXT, *PIMAGEHLP_CONTEXT;
+
+
+ULONG
+IMAGEAPI
+SymSetContext(
+    HANDLE hProcess,
+    PIMAGEHLP_STACK_FRAME StackFrame,
+    PIMAGEHLP_CONTEXT Context
+    );
+
+BOOL
+IMAGEAPI
+SymFromAddr(
+    IN  HANDLE              hProcess,
+    IN  DWORD64             Address,
+    OUT PDWORD64            Displacement,
+    IN OUT PSYMBOL_INFO     Symbol
+    );
+
+// While SymFromName will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions.  That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymFromName(
+    IN  HANDLE              hProcess,
+    IN  LPSTR               Name,
+    OUT PSYMBOL_INFO        Symbol
+    );
+
+typedef BOOL
+(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(
+    PSYMBOL_INFO  pSymInfo,
+    ULONG         SymbolSize,
+    PVOID         UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymEnumSymbols(
+    IN HANDLE                       hProcess,
+    IN ULONG64                      BaseOfDll,
+    IN PCSTR                        Mask,
+    IN PSYM_ENUMERATESYMBOLS_CALLBACK    EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+
+typedef enum _IMAGEHLP_SYMBOL_TYPE_INFO {
+    TI_GET_SYMTAG,
+    TI_GET_SYMNAME,
+    TI_GET_LENGTH,
+    TI_GET_TYPE,
+    TI_GET_TYPEID,
+    TI_GET_BASETYPE,
+    TI_GET_ARRAYINDEXTYPEID,
+    TI_FINDCHILDREN,
+    TI_GET_DATAKIND,
+    TI_GET_ADDRESSOFFSET,
+    TI_GET_OFFSET,
+    TI_GET_VALUE,
+    TI_GET_COUNT,
+    TI_GET_CHILDRENCOUNT,
+    TI_GET_BITPOSITION,
+    TI_GET_VIRTUALBASECLASS,
+    TI_GET_VIRTUALTABLESHAPEID,
+    TI_GET_VIRTUALBASEPOINTEROFFSET,
+    TI_GET_CLASSPARENTID,
+    TI_GET_NESTED,
+    TI_GET_SYMINDEX,
+    TI_GET_LEXICALPARENT,
+    TI_GET_ADDRESS,
+    TI_GET_THISADJUST,
+} IMAGEHLP_SYMBOL_TYPE_INFO;
+
+typedef struct _TI_FINDCHILDREN_PARAMS {
+    ULONG Count;
+    ULONG Start;
+    ULONG ChildId[1];
+} TI_FINDCHILDREN_PARAMS;
+
+BOOL
+IMAGEAPI
+SymGetTypeInfo(
+    IN  HANDLE          hProcess,
+    IN  DWORD64         ModBase,
+    IN  ULONG           TypeId,
+    IN  IMAGEHLP_SYMBOL_TYPE_INFO GetType,
+    OUT PVOID           pInfo
+    );
+
+BOOL
+IMAGEAPI
+SymEnumTypes(
+    IN HANDLE                       hProcess,
+    IN ULONG64                      BaseOfDll,
+    IN PSYM_ENUMERATESYMBOLS_CALLBACK    EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+
+BOOL
+IMAGEAPI
+SymGetTypeFromName(
+    IN  HANDLE              hProcess,
+    IN  ULONG64             BaseOfDll,
+    IN  LPSTR               Name,
+    OUT PSYMBOL_INFO        Symbol
+    );
+
+//
+// Full user-mode dump creation.
+//
+
+typedef BOOL (WINAPI *PDBGHELP_CREATE_USER_DUMP_CALLBACK)(
+    DWORD       DataType,
+    PVOID*      Data,
+    LPDWORD     DataLength,
+    PVOID       UserData
+    );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDump(
+    IN LPSTR                              FileName,
+    IN PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+    IN PVOID                              UserData
+    );
+
+BOOL
+WINAPI
+DbgHelpCreateUserDumpW(
+    IN LPWSTR                             FileName,
+    IN PDBGHELP_CREATE_USER_DUMP_CALLBACK Callback,
+    IN PVOID                              UserData
+    );
+
+// -----------------------------------------------------------------
+// The following 4 legacy APIs are fully supported, but newer
+// ones are recommended.  SymFromName and SymFromAddr provide
+// much more detailed info on the returned symbol.
+
+BOOL
+IMAGEAPI
+SymGetSymFromAddr64(
+    IN  HANDLE              hProcess,
+    IN  DWORD64             qwAddr,
+    OUT PDWORD64            pdwDisplacement,
+    OUT PIMAGEHLP_SYMBOL64  Symbol
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromAddr SymGetSymFromAddr64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromAddr(
+    IN  HANDLE            hProcess,
+    IN  DWORD             dwAddr,
+    OUT PDWORD            pdwDisplacement,
+    OUT PIMAGEHLP_SYMBOL  Symbol
+    );
+#endif
+
+// While following two APIs will provide a symbol from a name,
+// SymEnumSymbols can provide the same matching information
+// for ALL symbols with a matching name, even regular
+// expressions.  That way you can search across modules
+// and differentiate between identically named symbols.
+
+BOOL
+IMAGEAPI
+SymGetSymFromName64(
+    IN  HANDLE              hProcess,
+    IN  PSTR                Name,
+    OUT PIMAGEHLP_SYMBOL64  Symbol
+    );
+
+#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
+#define SymGetSymFromName SymGetSymFromName64
+#else
+BOOL
+IMAGEAPI
+SymGetSymFromName(
+    IN  HANDLE            hProcess,
+    IN  PSTR              Name,
+    OUT PIMAGEHLP_SYMBOL  Symbol
+    );
+#endif
+
+
+// -----------------------------------------------------------------
+// The following APIs exist only for backwards compatibility
+// with a pre-release version documented in an MSDN release.
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInPath(
+    HANDLE hprocess,
+    LPSTR  SearchPath,
+    LPSTR  FileName,
+    PVOID  id,
+    DWORD  two,
+    DWORD  three,
+    DWORD  flags,
+    LPSTR  FilePath
+    );
+
+// You should use SymFindFileInPath if you want to maintain
+// future compatibility.
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+FindFileInSearchPath(
+    HANDLE hprocess,
+    LPSTR  SearchPath,
+    LPSTR  FileName,
+    DWORD  one,
+    DWORD  two,
+    DWORD  three,
+    LPSTR  FilePath
+    );
+
+DBHLP_DEPRECIATED
+BOOL
+IMAGEAPI
+SymEnumSym(
+    IN HANDLE                       hProcess,
+    IN ULONG64                      BaseOfDll,
+    IN PSYM_ENUMERATESYMBOLS_CALLBACK    EnumSymbolsCallback,
+    IN PVOID                        UserContext
+    );
+
+
+#include <pshpack4.h>
+
+#pragma warning(disable:4200) // Zero length array
+
+
+#define MINIDUMP_SIGNATURE ('PMDM')
+#define MINIDUMP_VERSION   (42899)
+typedef DWORD RVA;
+typedef ULONG64 RVA64;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR {
+    ULONG32 DataSize;
+    RVA Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR;
+
+typedef struct _MINIDUMP_LOCATION_DESCRIPTOR64 {
+    ULONG64 DataSize;
+    RVA64 Rva;
+} MINIDUMP_LOCATION_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR {
+    ULONG64 StartOfMemoryRange;
+    MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_MEMORY_DESCRIPTOR, *PMINIDUMP_MEMORY_DESCRIPTOR;
+
+// DESCRIPTOR64 is used for full-memory minidumps where
+// all of the raw memory is laid out sequentially at the
+// end of the dump.  There is no need for individual RVAs
+// as the RVA is the base RVA plus the sum of the preceeding
+// data blocks.
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR64 {
+    ULONG64 StartOfMemoryRange;
+    ULONG64 DataSize;
+} MINIDUMP_MEMORY_DESCRIPTOR64, *PMINIDUMP_MEMORY_DESCRIPTOR64;
+
+
+typedef struct _MINIDUMP_HEADER {
+    ULONG32 Signature;
+    ULONG32 Version;
+    ULONG32 NumberOfStreams;
+    RVA StreamDirectoryRva;
+    ULONG32 CheckSum;
+    union {
+        ULONG32 Reserved;
+        ULONG32 TimeDateStamp;
+    };
+    ULONG64 Flags;
+} MINIDUMP_HEADER, *PMINIDUMP_HEADER;
+
+//
+// The MINIDUMP_HEADER field StreamDirectoryRva points to 
+// an array of MINIDUMP_DIRECTORY structures.
+//
+
+typedef struct _MINIDUMP_DIRECTORY {
+    ULONG32 StreamType;
+    MINIDUMP_LOCATION_DESCRIPTOR Location;
+} MINIDUMP_DIRECTORY, *PMINIDUMP_DIRECTORY;
+
+
+typedef struct _MINIDUMP_STRING {
+    ULONG32 Length;         // Length in bytes of the string
+    WCHAR   Buffer [0];     // Variable size buffer
+} MINIDUMP_STRING, *PMINIDUMP_STRING;
+
+
+
+//
+// The MINIDUMP_DIRECTORY field StreamType may be one of the following types.
+// Types will be added in the future, so if a program reading the minidump
+// header encounters a stream type it does not understand it should ignore
+// the data altogether. Any tag above LastReservedStream will not be used by
+// the system and is reserved for program-specific information.
+//
+
+typedef enum _MINIDUMP_STREAM_TYPE {
+
+    UnusedStream                = 0,
+    ReservedStream0             = 1,
+    ReservedStream1             = 2,
+    ThreadListStream            = 3,
+    ModuleListStream            = 4,
+    MemoryListStream            = 5,
+    ExceptionStream             = 6,
+    SystemInfoStream            = 7,
+    ThreadExListStream          = 8,
+    Memory64ListStream          = 9,
+    CommentStreamA              = 10,
+    CommentStreamW              = 11,
+    HandleDataStream            = 12,
+    FunctionTableStream         = 13,
+
+    LastReservedStream          = 0xffff
+
+} MINIDUMP_STREAM_TYPE;
+
+
+//
+// The minidump system information contains processor and
+// Operating System specific information.
+// 
+    
+typedef struct _MINIDUMP_SYSTEM_INFO {
+
+    //
+    // ProcessorArchitecture, ProcessorLevel and ProcessorRevision are all
+    // taken from the SYSTEM_INFO structure obtained by GetSystemInfo( ).
+    //
+    
+    USHORT ProcessorArchitecture;
+    USHORT ProcessorLevel;
+    USHORT ProcessorRevision;
+
+    USHORT Reserved0;              // Reserved for future use. Must be zero.
+
+    //
+    // MajorVersion, MinorVersion, BuildNumber, PlatformId and
+    // CSDVersion are all taken from the OSVERSIONINFO structure
+    // returned by GetVersionEx( ).
+    //
+    
+    ULONG32 MajorVersion;
+    ULONG32 MinorVersion;
+    ULONG32 BuildNumber;
+    ULONG32 PlatformId;
+
+    //
+    // RVA to a CSDVersion string in the string table.
+    //
+    
+    RVA CSDVersionRva;
+
+    ULONG32 Reserved1;             // Reserved for future use.
+
+    //
+    // CPU information is obtained from one of two places.
+    //
+    //  1) On x86 computers, CPU_INFORMATION is obtained from the CPUID
+    //     instruction. You must use the X86 portion of the union for X86
+    //     computers.
+    //
+    //  2) On non-x86 architectures, CPU_INFORMATION is obtained by calling
+    //     IsProcessorFeatureSupported().
+    //
+    
+    union _CPU_INFORMATION {
+
+        //
+        // X86 platforms use CPUID function to obtain processor information.
+        //
+        
+        struct {
+
+            //
+            // CPUID Subfunction 0, register EAX (VendorId [0]),
+            // EBX (VendorId [1]) and ECX (VendorId [2]).
+            //
+            
+            ULONG32 VendorId [ 3 ];
+            
+            //
+            // CPUID Subfunction 1, register EAX
+            //
+            
+            ULONG32 VersionInformation;
+
+            //
+            // CPUID Subfunction 1, register EDX
+            //
+            
+            ULONG32 FeatureInformation;
+            
+
+            //
+            // CPUID, Subfunction 80000001, register EBX. This will only
+            // be obtained if the vendor id is "AuthenticAMD".
+            //
+            
+            ULONG32 AMDExtendedCpuFeatures;
+    
+        } X86CpuInfo;
+
+        //
+        // Non-x86 platforms use processor feature flags.
+        //
+        
+        struct {
+
+            ULONG64 ProcessorFeatures [ 2 ];
+            
+        } OtherCpuInfo;
+        
+    } Cpu;
+
+} MINIDUMP_SYSTEM_INFO, *PMINIDUMP_SYSTEM_INFO;
+
+typedef union _CPU_INFORMATION CPU_INFORMATION, *PCPU_INFORMATION;
+
+
+//
+// The minidump thread contains standard thread
+// information plus an RVA to the memory for this 
+// thread and an RVA to the CONTEXT structure for
+// this thread.
+//
+
+
+//
+// ThreadId must be 4 bytes on all architectures.
+//
+
+C_ASSERT (sizeof ( ((PPROCESS_INFORMATION)0)->dwThreadId ) == 4);
+
+typedef struct _MINIDUMP_THREAD {
+    ULONG32 ThreadId;
+    ULONG32 SuspendCount;
+    ULONG32 PriorityClass;
+    ULONG32 Priority;
+    ULONG64 Teb;
+    MINIDUMP_MEMORY_DESCRIPTOR Stack;
+    MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_THREAD, *PMINIDUMP_THREAD;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_LIST {
+    ULONG32 NumberOfThreads;
+    MINIDUMP_THREAD Threads [0];
+} MINIDUMP_THREAD_LIST, *PMINIDUMP_THREAD_LIST;
+
+
+typedef struct _MINIDUMP_THREAD_EX {
+    ULONG32 ThreadId;
+    ULONG32 SuspendCount;
+    ULONG32 PriorityClass;
+    ULONG32 Priority;
+    ULONG64 Teb;
+    MINIDUMP_MEMORY_DESCRIPTOR Stack;
+    MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+    MINIDUMP_MEMORY_DESCRIPTOR BackingStore;
+} MINIDUMP_THREAD_EX, *PMINIDUMP_THREAD_EX;
+
+//
+// The thread list is a container of threads.
+//
+
+typedef struct _MINIDUMP_THREAD_EX_LIST {
+    ULONG32 NumberOfThreads;
+    MINIDUMP_THREAD_EX Threads [0];
+} MINIDUMP_THREAD_EX_LIST, *PMINIDUMP_THREAD_EX_LIST;
+
+
+//
+// The MINIDUMP_EXCEPTION is the same as EXCEPTION on Win64.
+//
+
+typedef struct _MINIDUMP_EXCEPTION  {
+    ULONG32 ExceptionCode;
+    ULONG32 ExceptionFlags;
+    ULONG64 ExceptionRecord;
+    ULONG64 ExceptionAddress;
+    ULONG32 NumberParameters;
+    ULONG32 __unusedAlignment;
+    ULONG64 ExceptionInformation [ EXCEPTION_MAXIMUM_PARAMETERS ];
+} MINIDUMP_EXCEPTION, *PMINIDUMP_EXCEPTION;
+
+
+//
+// The exception information stream contains the id of the thread that caused
+// the exception (ThreadId), the exception record for the exception
+// (ExceptionRecord) and an RVA to the thread context where the exception
+// occured.
+//
+
+typedef struct MINIDUMP_EXCEPTION_STREAM {
+    ULONG32 ThreadId;
+    ULONG32  __alignment;
+    MINIDUMP_EXCEPTION ExceptionRecord;
+    MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;
+} MINIDUMP_EXCEPTION_STREAM, *PMINIDUMP_EXCEPTION_STREAM;
+
+
+//
+// The MINIDUMP_MODULE contains information about a
+// a specific module. It includes the CheckSum and
+// the TimeDateStamp for the module so the module
+// can be reloaded during the analysis phase.
+//
+
+typedef struct _MINIDUMP_MODULE {
+    ULONG64 BaseOfImage;
+    ULONG32 SizeOfImage;
+    ULONG32 CheckSum;
+    ULONG32 TimeDateStamp;
+    RVA ModuleNameRva;
+    VS_FIXEDFILEINFO VersionInfo;
+    MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
+    MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
+    ULONG64 Reserved0;                          // Reserved for future use.
+    ULONG64 Reserved1;                          // Reserved for future use.
+} MINIDUMP_MODULE, *PMINIDUMP_MODULE;   
+
+
+//
+// The minidump module list is a container for modules.
+//
+
+typedef struct _MINIDUMP_MODULE_LIST {
+    ULONG32 NumberOfModules;
+    MINIDUMP_MODULE Modules [ 0 ];
+} MINIDUMP_MODULE_LIST, *PMINIDUMP_MODULE_LIST;
+
+
+//
+// Memory Ranges
+//
+
+typedef struct _MINIDUMP_MEMORY_LIST {
+    ULONG32 NumberOfMemoryRanges;
+    MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges [0];
+} MINIDUMP_MEMORY_LIST, *PMINIDUMP_MEMORY_LIST;
+
+typedef struct _MINIDUMP_MEMORY64_LIST {
+    ULONG64 NumberOfMemoryRanges;
+    RVA64 BaseRva;
+    MINIDUMP_MEMORY_DESCRIPTOR64 MemoryRanges [0];
+} MINIDUMP_MEMORY64_LIST, *PMINIDUMP_MEMORY64_LIST;
+
+
+//
+// Support for user supplied exception information.
+//
+
+typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
+    DWORD ThreadId;
+    PEXCEPTION_POINTERS ExceptionPointers;
+    BOOL ClientPointers;
+} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
+
+
+//
+// Support for capturing system handle state at the time of the dump.
+//
+
+typedef struct _MINIDUMP_HANDLE_DESCRIPTOR {
+    ULONG64 Handle;
+    RVA TypeNameRva;
+    RVA ObjectNameRva;
+    ULONG32 Attributes;
+    ULONG32 GrantedAccess;
+    ULONG32 HandleCount;
+    ULONG32 PointerCount;
+} MINIDUMP_HANDLE_DESCRIPTOR, *PMINIDUMP_HANDLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_HANDLE_DATA_STREAM {
+    ULONG32 SizeOfHeader;
+    ULONG32 SizeOfDescriptor;
+    ULONG32 NumberOfDescriptors;
+    ULONG32 Reserved;
+} MINIDUMP_HANDLE_DATA_STREAM, *PMINIDUMP_HANDLE_DATA_STREAM;
+
+
+//
+// Support for capturing dynamic function table state at the time of the dump.
+//
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_DESCRIPTOR {
+    ULONG64 MinimumAddress;
+    ULONG64 MaximumAddress;
+    ULONG64 BaseAddress;
+    ULONG32 EntryCount;
+    ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_DESCRIPTOR, *PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR;
+
+typedef struct _MINIDUMP_FUNCTION_TABLE_STREAM {
+    ULONG32 SizeOfHeader;
+    ULONG32 SizeOfDescriptor;
+    ULONG32 SizeOfNativeDescriptor;
+    ULONG32 SizeOfFunctionEntry;
+    ULONG32 NumberOfDescriptors;
+    ULONG32 SizeOfAlignPad;
+} MINIDUMP_FUNCTION_TABLE_STREAM, *PMINIDUMP_FUNCTION_TABLE_STREAM;
+
+
+//
+// Support for arbitrary user-defined information.
+//
+
+typedef struct _MINIDUMP_USER_RECORD {
+    ULONG32 Type;
+    MINIDUMP_LOCATION_DESCRIPTOR Memory;
+} MINIDUMP_USER_RECORD, *PMINIDUMP_USER_RECORD;
+
+
+typedef struct _MINIDUMP_USER_STREAM {
+    ULONG32 Type;
+    ULONG BufferSize;
+    PVOID Buffer;
+
+} MINIDUMP_USER_STREAM, *PMINIDUMP_USER_STREAM;
+
+
+typedef struct _MINIDUMP_USER_STREAM_INFORMATION {
+    ULONG UserStreamCount;
+    PMINIDUMP_USER_STREAM UserStreamArray;
+} MINIDUMP_USER_STREAM_INFORMATION, *PMINIDUMP_USER_STREAM_INFORMATION;
+
+//
+// Callback support.
+//
+
+typedef enum _MINIDUMP_CALLBACK_TYPE {
+    ModuleCallback,
+    ThreadCallback,
+    ThreadExCallback,
+    IncludeThreadCallback,
+    IncludeModuleCallback,
+} MINIDUMP_CALLBACK_TYPE;
+
+
+typedef struct _MINIDUMP_THREAD_CALLBACK {
+    ULONG ThreadId;
+    HANDLE ThreadHandle;
+    CONTEXT Context;
+    ULONG SizeOfContext;
+    ULONG64 StackBase;
+    ULONG64 StackEnd;
+} MINIDUMP_THREAD_CALLBACK, *PMINIDUMP_THREAD_CALLBACK;
+
+
+typedef struct _MINIDUMP_THREAD_EX_CALLBACK {
+    ULONG ThreadId;
+    HANDLE ThreadHandle;
+    CONTEXT Context;
+    ULONG SizeOfContext;
+    ULONG64 StackBase;
+    ULONG64 StackEnd;
+    ULONG64 BackingStoreBase;
+    ULONG64 BackingStoreEnd;
+} MINIDUMP_THREAD_EX_CALLBACK, *PMINIDUMP_THREAD_EX_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_THREAD_CALLBACK {
+    ULONG ThreadId;
+} MINIDUMP_INCLUDE_THREAD_CALLBACK, *PMINIDUMP_INCLUDE_THREAD_CALLBACK;
+
+
+typedef enum _THREAD_WRITE_FLAGS {
+    ThreadWriteThread            = 0x0001,
+    ThreadWriteStack             = 0x0002,
+    ThreadWriteContext           = 0x0004,
+    ThreadWriteBackingStore      = 0x0008,
+    ThreadWriteInstructionWindow = 0x0010
+} THREAD_WRITE_FLAGS;
+
+typedef struct _MINIDUMP_MODULE_CALLBACK {
+    PWCHAR FullPath;
+    ULONG64 BaseOfImage;
+    ULONG SizeOfImage;
+    ULONG CheckSum;
+    ULONG TimeDateStamp;
+    VS_FIXEDFILEINFO VersionInfo;
+    PVOID CvRecord; 
+    ULONG SizeOfCvRecord;
+    PVOID MiscRecord;
+    ULONG SizeOfMiscRecord;
+} MINIDUMP_MODULE_CALLBACK, *PMINIDUMP_MODULE_CALLBACK;
+
+
+typedef struct _MINIDUMP_INCLUDE_MODULE_CALLBACK {
+    ULONG64 BaseOfImage;
+} MINIDUMP_INCLUDE_MODULE_CALLBACK, *PMINIDUMP_INCLUDE_MODULE_CALLBACK;
+
+
+typedef enum _MODULE_WRITE_FLAGS {
+    ModuleWriteModule        = 0x0001,
+    ModuleWriteDataSeg       = 0x0002,
+    ModuleWriteMiscRecord    = 0x0004,
+    ModuleWriteCvRecord      = 0x0008,
+    ModuleReferencedByMemory = 0x0010
+} MODULE_WRITE_FLAGS;
+
+
+typedef struct _MINIDUMP_CALLBACK_INPUT {
+    ULONG ProcessId;
+    HANDLE ProcessHandle;
+    ULONG CallbackType;
+    union {
+        MINIDUMP_THREAD_CALLBACK Thread;
+        MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
+        MINIDUMP_MODULE_CALLBACK Module;
+        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
+        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
+    };
+} MINIDUMP_CALLBACK_INPUT, *PMINIDUMP_CALLBACK_INPUT;
+
+typedef struct _MINIDUMP_CALLBACK_OUTPUT {
+    union {
+        ULONG ModuleWriteFlags;
+        ULONG ThreadWriteFlags;
+    };
+} MINIDUMP_CALLBACK_OUTPUT, *PMINIDUMP_CALLBACK_OUTPUT;
+
+        
+//
+// A normal minidump contains just the information
+// necessary to capture stack traces for all of the
+// existing threads in a process.
+//
+// A minidump with data segments includes all of the data
+// sections from loaded modules in order to capture
+// global variable contents.  This can make the dump much
+// larger if many modules have global data.
+//
+// A minidump with full memory includes all of the accessible
+// memory in the process and can be very large.  A minidump
+// with full memory always has the raw memory data at the end
+// of the dump so that the initial structures in the dump can
+// be mapped directly without having to include the raw
+// memory information.
+//
+// Stack and backing store memory can be filtered to remove
+// data unnecessary for stack walking.  This can improve
+// compression of stacks and also deletes data that may
+// be private and should not be stored in a dump.
+// Memory can also be scanned to see what modules are
+// referenced by stack and backing store memory to allow
+// omission of other modules to reduce dump size.
+// In either of these modes the ModuleReferencedByMemory flag
+// is set for all modules referenced before the base
+// module callbacks occur.
+//
+
+typedef enum _MINIDUMP_TYPE {
+    MiniDumpNormal         = 0x0000,
+    MiniDumpWithDataSegs   = 0x0001,
+    MiniDumpWithFullMemory = 0x0002,
+    MiniDumpWithHandleData = 0x0004,
+    MiniDumpFilterMemory   = 0x0008,
+    MiniDumpScanMemory     = 0x0010,
+} MINIDUMP_TYPE;
+
+
+//
+// The minidump callback should modify the FieldsToWrite parameter to reflect
+// what portions of the specified thread or module should be written to the
+// file.
+//
+
+typedef
+BOOL
+(WINAPI * MINIDUMP_CALLBACK_ROUTINE) (
+    IN PVOID CallbackParam,
+    IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput,
+    IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
+    );
+
+typedef struct _MINIDUMP_CALLBACK_INFORMATION {
+    MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
+    PVOID CallbackParam;
+} MINIDUMP_CALLBACK_INFORMATION, *PMINIDUMP_CALLBACK_INFORMATION;
+
+
+
+//++
+//
+// PVOID
+// RVA_TO_ADDR(
+//     PVOID Mapping,
+//     ULONG Rva
+//     )
+//
+// Routine Description:
+//
+//     Map an RVA that is contained within a mapped file to it's associated
+//     flat address.
+//
+// Arguments:
+//
+//     Mapping - Base address of mapped file containing the RVA.
+//
+//     Rva - An Rva to fixup.
+//
+// Return Values:
+//
+//     A pointer to the desired data.
+//
+//--
+
+#define RVA_TO_ADDR(Mapping,Rva) ((PVOID)(((ULONG_PTR) (Mapping)) + (Rva)))
+
+BOOL
+WINAPI
+MiniDumpWriteDump(
+    IN HANDLE hProcess,
+    IN DWORD ProcessId,
+    IN HANDLE hFile,
+    IN MINIDUMP_TYPE DumpType,
+    IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
+    IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
+    IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
+    );
+
+BOOL
+WINAPI
+MiniDumpReadDumpStream(
+    IN PVOID BaseOfDump,
+    IN ULONG StreamNumber,
+    OUT PMINIDUMP_DIRECTORY * Dir, OPTIONAL
+    OUT PVOID * StreamPointer, OPTIONAL
+    OUT ULONG * StreamSize OPTIONAL
+    );
+
+#include <poppack.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // _DBGHELP_
--- /dev/null
+++ b/Game/src/_functio.h
@@ -1,0 +1,258 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+// _functio.h
+
+// file created by makehead.exe
+// these headers contain default key assignments, as well as
+// default button assignments and game function names
+// axis defaults are also included
+
+
+#ifndef _function_private_
+#define _function_private_
+#ifdef __cplusplus
+extern "C" {
+#endif
+char * gamefunctions[] =
+   {
+   "Move_Forward",
+   "Move_Backward",
+   "Turn_Left",
+   "Turn_Right",
+   "Strafe",
+   "Fire",
+   "Open",
+   "Run",
+   "AutoRun",
+   "Jump",
+   "Crouch",
+   "Look_Up",
+   "Look_Down",
+   "Look_Left",
+   "Look_Right",
+   "Strafe_Left",
+   "Strafe_Right",
+   "Aim_Up",
+   "Aim_Down",
+   "Weapon_1",
+   "Weapon_2",
+   "Weapon_3",
+   "Weapon_4",
+   "Weapon_5",
+   "Weapon_6",
+   "Weapon_7",
+   "Weapon_8",
+   "Weapon_9",
+   "Weapon_10",
+   "Inventory",
+   "Inventory_Left",
+   "Inventory_Right",
+   "Holo_Duke",
+   "Jetpack",
+   "NightVision",
+   "MedKit",
+   "TurnAround",
+   "SendMessage",
+   "Map",
+   "Shrink_Screen",
+   "Enlarge_Screen",
+   "Center_View",
+   "Holster_Weapon",
+   "Show_Opponents_Weapon",
+   "Map_Follow_Mode",
+   "See_Coop_View",
+   "Mouse_Aiming",
+   "Toggle_Crosshair",
+   "Steroids",
+   "Quick_Kick",
+   "Next_Weapon",
+   "Previous_Weapon",
+   "Hide_Weapon",
+   "Auto_Aim",
+   "Console"
+   };
+
+	#define NUMKEYENTRIES 55  // Don't forget to change NUMGAMEFUNCTIONS as well
+
+// FIX_00011: duke3d.cfg not needed anymore to start the game. Will create a default one
+// if not found and use default keys.
+
+struct {
+	char* entryKey;
+	char* keyname1;
+	char* keyname2;
+} keydefaults[] = {
+	{ "Move_Forward", 	"Up", 		"Kpad8" }, 
+	{ "Move_Backward", 	"Down", 	"Kpad2" }, 
+	{ "Turn_Left", 		"Left", 	"Kpad4" }, 
+	{ "Turn_Right", 	"Right", 	"KPad6" }, 
+	{ "Strafe", 		"LAlt", 	"RAlt" }, 
+	{ "Fire", 			"LCtrl", 	"RCtrl" }, 
+	{ "Open", 			"Space", 	"" }, 
+	{ "Run", 			"LShift", 	"RShift" }, 
+	{ "AutoRun", 		"CapLck", 	"" }, 
+	{ "Jump", 			"A", 		"/" }, 
+	{ "Crouch", 		"Z", 		"" }, 
+	{ "Look_Up", 		"PgUp", 	"Kpad9" }, 
+	{ "Look_Down", 		"PgDn", 	"Kpad3" }, 
+	{ "Look_Left", 		"Insert", 	"Kpad0" }, 
+	{ "Look_Right", 	"Delete", 	"Kpad." }, 
+	{ "Strafe_Left", 	",", 		"" }, 
+	{ "Strafe_Right", 	".", 		"" }, 
+	{ "Aim_Up", 		"Home", 	"KPad7" }, 
+	{ "Aim_Down", 		"End", 		"Kpad1" }, 
+	{ "Weapon_1", 		"1", 		"" }, 
+	{ "Weapon_2", 		"2", 		"" }, 
+	{ "Weapon_3", 		"3", 		"" }, 
+	{ "Weapon_4", 		"4", 		"" }, 
+	{ "Weapon_5", 		"5", 		"" }, 
+	{ "Weapon_6", 		"6", 		"" }, 
+	{ "Weapon_7", 		"7", 		"" }, 
+	{ "Weapon_8", 		"8", 		"" }, 
+	{ "Weapon_9", 		"9", 		"" }, 
+	{ "Weapon_10",		"0", 		"" }, 
+	{ "Inventory", 		"Enter", 	"KpdEnt" }, 
+	{ "Inventory_Left",	"[", 		"" }, 
+	{ "Inventory_Right","]", 		"" }, 
+	{ "Holo_Duke", 		"H", 		"" }, 
+	{ "Jetpack", 		"J", 		"" }, 
+	{ "NightVision", 	"N", 		"" }, 
+	{ "MedKit", 		"M", "		" }, 
+	{ "TurnAround", 	"BakSpc", 	"" }, 
+	{ "SendMessage", 	"T", 		"" }, 
+	{ "Map", 			"Tab", 		"" }, 
+	{ "Shrink_Screen", 	"-", 		"Kpad-" }, 
+	{ "Enlarge_Screen", "=", 		"Kpad+" }, 
+	{ "Center_View", 	"KPad5", 	"" }, 
+	{ "Holster_Weapon", "ScrLck", 	"" }, 
+	{ "Show_Opponents_Weapon", "W", "" }, 
+	{ "Map_Follow_Mode","F", 		"" }, 
+	{ "See_Coop_View", 	"K", 		"" }, 
+	{ "Mouse_Aiming", 	"U", 		"" }, 
+	{ "Toggle_Crosshair", "I", 		"" }, 
+	{ "Steroids", 		"R", 		"" }, 
+	{ "Quick_Kick", 	"C", 		"" }, 
+	{ "Next_Weapon", 	"'", 		"" }, 
+	{ "Previous_Weapon",";", 		"" },
+	{ "Hide_Weapon", 	"S", 		"" },
+	{ "Auto_Aim", 		"V", 		"" },
+	{ "Console", 		"`", 		"" }
+};
+
+
+static char * mousedefaults[] =
+   {
+   "Fire",
+   "Strafe",
+   "Move_Forward",
+   };
+
+
+static char * mouseclickeddefaults[] =
+   {
+   "",
+   "Open",
+   "",
+   };
+
+
+static char * joystickdefaults[] =
+   {
+   "Fire",
+   "Strafe",
+   "Run",
+   "Open",
+   "Aim_Down",
+   "Look_Right",
+   "Aim_Up",
+   "Look_Left",
+   };
+
+
+static char * joystickclickeddefaults[] =
+   {
+   "",
+   "Inventory",
+   "Jump",
+   "Crouch",
+   "",
+   "",
+   "",
+   "",
+   };
+
+
+static char * mouseanalogdefaults[] =
+   {
+   "analog_turning",
+   "analog_moving",
+   };
+
+
+static char * mousedigitaldefaults[] =
+   {
+   "",
+   "",
+   "",
+   "",
+   };
+
+
+static char * gamepaddigitaldefaults[] =
+   {
+   "Turn_Left",
+   "Turn_Right",
+   "Move_Forward",
+   "Move_Backward",
+   };
+
+
+static char * joystickanalogdefaults[] =
+   {
+   "analog_turning",
+   "analog_moving",
+   "analog_strafing",
+   "",
+   };
+
+
+static char * joystickdigitaldefaults[] =
+   {
+   "",
+   "",
+   "",
+   "",
+   "",
+   "",
+   "Run",
+   "",
+   };
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/_rts.h
@@ -1,0 +1,54 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef __rts_private__
+#define __rts_private__
+
+//===============
+//   TYPES
+//===============
+
+typedef struct
+   {
+   char name[8];
+   int32 handle,position,size;
+   } lumpinfo_t;
+
+typedef struct
+   {
+   char identification[4];              // should be IWAD
+   int32 numlumps;
+   int32 infotableofs;
+   } wadinfo_t;
+
+typedef struct
+   {
+   int32 filepos;
+   int32 size;
+   char name[8];
+   } filelump_t;
+
+#endif
--- /dev/null
+++ b/Game/src/actors.c
@@ -1,0 +1,7157 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+
+extern long numenvsnds;
+char actor_tog;
+
+void updateinterpolations()  //Stick at beginning of domovethings
+{
+	long i;
+
+	for(i=numinterpolations-1;i>=0;i--) oldipos[i] = *curipos[i];
+}
+
+
+void setinterpolation(long *posptr)
+{
+	long i;
+
+	if (numinterpolations >= MAXINTERPOLATIONS) return;
+	for(i=numinterpolations-1;i>=0;i--)
+		if (curipos[i] == posptr) return;
+	curipos[numinterpolations] = posptr;
+	oldipos[numinterpolations] = *posptr;
+	numinterpolations++;
+}
+
+void stopinterpolation(long *posptr)
+{
+	long i;
+
+	for(i=numinterpolations-1;i>=startofdynamicinterpolations;i--)
+		if (curipos[i] == posptr)
+		{
+			numinterpolations--;
+			oldipos[i] = oldipos[numinterpolations];
+			bakipos[i] = bakipos[numinterpolations];
+			curipos[i] = curipos[numinterpolations];
+		}
+}
+
+void dointerpolations(long smoothratio)       //Stick at beginning of drawscreen
+{
+	long i, j, odelta, ndelta;
+
+	ndelta = 0; j = 0;
+	for(i=numinterpolations-1;i>=0;i--)
+	{
+		bakipos[i] = *curipos[i];
+		odelta = ndelta; ndelta = (*curipos[i])-oldipos[i];
+		if (odelta != ndelta) j = mulscale16(ndelta,smoothratio);
+		*curipos[i] = oldipos[i]+j;
+	}
+}
+
+void restoreinterpolations()  //Stick at end of drawscreen
+{
+	long i;
+
+	for(i=numinterpolations-1;i>=0;i--) *curipos[i] = bakipos[i];
+}
+
+long ceilingspace(short sectnum)
+{
+    if( (sector[sectnum].ceilingstat&1) && sector[sectnum].ceilingpal == 0 )
+    {
+        switch(sector[sectnum].ceilingpicnum)
+        {
+            case MOONSKY1:
+            case BIGORBIT1:
+                return 1;
+        }
+    }
+    return 0;
+}
+
+long floorspace(short sectnum)
+{
+    if( (sector[sectnum].floorstat&1) && sector[sectnum].ceilingpal == 0 )
+    {
+        switch(sector[sectnum].floorpicnum)
+        {
+            case MOONSKY1:
+            case BIGORBIT1:
+                return 1;
+        }
+    }
+    return 0;
+}
+
+void addammo( short weapon,struct player_struct *p,short amount)
+{
+   p->ammo_amount[weapon] += amount;
+
+   if( p->ammo_amount[weapon] > max_ammo_amount[weapon] )
+        p->ammo_amount[weapon] = max_ammo_amount[weapon];
+}
+
+void addweapon( struct player_struct *p,short weapon)
+{
+	int added_new_weapon = false;
+
+    if ( p->gotweapon[weapon] == 0 )
+    {
+        p->gotweapon[weapon] = 1;
+        if(weapon == SHRINKER_WEAPON)
+            p->gotweapon[GROW_WEAPON] = 1;
+	 
+	// FIX_00012: added "weapon autoswitch" toggle allowing to turn the autoswitch off
+	// when picking up new weapons. The weapon sound on pickup will remain on, to not 
+	// affect the opponent's gameplay (so he can still hear you picking up new weapons)
+  	if(p->weaponautoswitch)  // Anti antiswitch ordered
+			added_new_weapon = true;
+    }
+
+	if (added_new_weapon==false || 
+		ud.playing_demo_rev == BYTEVERSION_27     ||
+		ud.playing_demo_rev == BYTEVERSION_28     || 
+		ud.playing_demo_rev == BYTEVERSION_116    || 
+		ud.playing_demo_rev == BYTEVERSION_117 )
+		// don't block the weapon change on 1st pick up if playing an old demo
+	{
+		p->random_club_frame = 0;
+
+		if(p->holster_weapon == 0)
+		{
+			p->weapon_pos = -1;
+			p->last_weapon = p->curr_weapon;
+		}
+		else
+		{
+			p->weapon_pos = 10;
+			p->holster_weapon = 0;
+			p->last_weapon = -1;
+		}
+
+		p->kickback_pic = 0;
+		p->curr_weapon = weapon;
+	}
+
+    switch(weapon)
+    {
+        case KNEE_WEAPON:
+        case TRIPBOMB_WEAPON:
+        case HANDREMOTE_WEAPON:
+        case HANDBOMB_WEAPON:     break;
+        case SHOTGUN_WEAPON:      spritesound(SHOTGUN_COCK,p->i);break;
+        case PISTOL_WEAPON:       spritesound(INSERT_CLIP,p->i);break;
+                    default:      spritesound(SELECT_WEAPON,p->i);break;
+    }
+	
+	vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+
+}
+
+void checkavailinven( struct player_struct *p )
+{
+
+    if(p->firstaid_amount > 0)
+        p->inven_icon = 1;
+    else if(p->steroids_amount > 0)
+        p->inven_icon = 2;
+    else if(p->holoduke_amount > 0)
+        p->inven_icon = 3;
+    else if(p->jetpack_amount > 0)
+        p->inven_icon = 4;
+    else if(p->heat_amount > 0)
+        p->inven_icon = 5;
+    else if(p->scuba_amount > 0)
+        p->inven_icon = 6;
+    else if(p->boot_amount > 0)
+        p->inven_icon = 7;
+    else p->inven_icon = 0;
+}
+
+void checkavailweapon( struct player_struct *p )
+{
+    short i,snum;
+    int32 weap;
+
+    if(p->wantweaponfire >= 0)
+    {
+        weap = p->wantweaponfire;
+        p->wantweaponfire = -1;
+
+        if(weap == p->curr_weapon) return;
+        else if( p->gotweapon[weap] && p->ammo_amount[weap] > 0 )
+        {
+            addweapon(p,weap);
+            return;
+        }
+    }
+
+    weap = p->curr_weapon;
+    if( p->gotweapon[weap] && p->ammo_amount[weap] > 0 )
+        return;
+
+    snum = sprite[p->i].yvel;
+
+    for(i=0;i<10;i++)
+    {
+        weap = ud.wchoice[snum][i];
+		if (VOLUMEONE)
+			if(weap > 6) continue;
+
+        if(weap == 0) weap = 9;
+        else weap--;
+
+        if( weap == 0 || ( p->gotweapon[weap] && p->ammo_amount[weap] > 0 ) )
+            break;
+    }
+
+    if(i == 10) weap = 0;
+
+    // Found the weapon
+
+    p->last_weapon  = p->curr_weapon;
+    p->random_club_frame = 0;
+    p->curr_weapon  = weap;
+    p->kickback_pic = 0;
+    if(p->holster_weapon == 1)
+    {
+        p->holster_weapon = 0;
+        p->weapon_pos = 10;
+    }
+    else p->weapon_pos   = -1;
+}
+
+ /*
+void checkavailweapon( struct player_struct *p )
+{
+    short i,okay,check_shoot,check_bombs;
+
+    if(p->ammo_amount[p->curr_weapon] > 0) return;
+    okay = check_shoot = check_bombs = 0;
+
+    switch(p->curr_weapon)
+    {
+        case PISTOL_WEAPON:
+        case CHAINGUN_WEAPON:
+        case SHOTGUN_WEAPON:
+#ifndef VOLUMEONE
+        case FREEZE_WEAPON:
+        case DEVISTATOR_WEAPON:
+        case SHRINKER_WEAPON:
+        case GROW_WEAPON:
+#endif
+        case RPG_WEAPON:
+        case KNEE_WEAPON:
+            check_shoot = 1;
+            break;
+        case HANDBOMB_WEAPON:
+        case HANDREMOTE_WEAPON:
+#ifndef VOLUMEONE
+        case TRIPBOMB_WEAPON:
+#endif
+            check_bombs = 1;
+            break;
+    }
+
+    CHECK_SHOOT:
+    if(check_shoot)
+    {
+        for(i = p->curr_weapon+1; i < MAX_WEAPONS;i++)
+            switch(i)
+            {
+                case PISTOL_WEAPON:
+                case CHAINGUN_WEAPON:
+                case SHOTGUN_WEAPON:
+#ifndef VOLUMEONE
+                case FREEZE_WEAPON:
+                case SHRINKER_WEAPON:
+                case GROW_WEAPON:
+                case DEVISTATOR_WEAPON:
+#endif
+                    if ( p->gotweapon[i] && p->ammo_amount[i] > 0 )
+                    {
+                        okay = i;
+                        goto OKAY_HERE;
+                    }
+                    break;
+            }
+
+        for(i = p->curr_weapon-1; i > 0;i--)
+            switch(i)
+            {
+                case PISTOL_WEAPON:
+                case CHAINGUN_WEAPON:
+                case SHOTGUN_WEAPON:
+#ifndef VOLUMEONE
+                case FREEZE_WEAPON:
+                case DEVISTATOR_WEAPON:
+                case SHRINKER_WEAPON:
+                case GROW_WEAPON:
+#endif
+                    if ( p->gotweapon[i] && p->ammo_amount[i] > 0 )
+                    {
+                        okay = i;
+                        goto OKAY_HERE;
+                    }
+                    break;
+            }
+
+        if( p->gotweapon[RPG_WEAPON] && p->ammo_amount[RPG_WEAPON] > 0 )
+        {
+            okay = RPG_WEAPON;
+            goto OKAY_HERE;
+        }
+
+        if(check_bombs == 0)
+            check_bombs = 1;
+        else
+        {
+            addweapon(p,KNEE_WEAPON);
+            return;
+        }
+    }
+
+    if(check_bombs)
+    {
+        for(i = p->curr_weapon-1; i > 0;i--)
+            switch(i)
+            {
+                case HANDBOMB_WEAPON:
+#ifndef VOLUMEONE
+                case TRIPBOMB_WEAPON:
+#endif
+                    if ( p->gotweapon[i] && p->ammo_amount[i] > 0 )
+                    {
+                        okay = i;
+                        goto OKAY_HERE;
+                    }
+                    break;
+            }
+
+        for(i = p->curr_weapon+1; i < MAX_WEAPONS;i++)
+            switch(i)
+            {
+                case HANDBOMB_WEAPON:
+#ifdef VOLUMEONE
+                case TRIPBOMB_WEAPON:
+#endif
+                    if ( p->gotweapon[i] && p->ammo_amount[i] > 0 )
+                    {
+                        okay = i;
+                        goto OKAY_HERE;
+                    }
+                    break;
+            }
+
+        if(check_shoot == 0)
+        {
+            check_shoot = 1;
+            goto CHECK_SHOOT;
+        }
+        else
+        {
+            addweapon(p,KNEE_WEAPON);
+            return;
+        }
+    }
+
+    OKAY_HERE:
+
+    if(okay)
+    {
+        p->last_weapon  = p->curr_weapon;
+        p->random_club_frame = 0;
+        p->curr_weapon  = okay;
+        p->kickback_pic = 0;
+        if(p->holster_weapon == 1)
+        {
+            p->holster_weapon = 0;
+            p->weapon_pos = 10;
+        }
+        else p->weapon_pos   = -1;
+        return;
+    }
+}
+   */
+
+long ifsquished(short i, short p)
+{
+    sectortype *sc;
+    char squishme;
+    long floorceildist;
+
+    if(PN == APLAYER && ud.clipping)
+        return 0;
+
+    sc = &sector[SECT];
+    floorceildist = sc->floorz - sc->ceilingz;
+
+    if(sc->lotag != 23)
+    {
+        if(sprite[i].pal == 1)
+            squishme = floorceildist < (32<<8) && (sc->lotag&32768) == 0;
+        else
+            squishme = floorceildist < (12<<8); // && (sc->lotag&32768) == 0;
+    }
+    else squishme = 0;
+
+    if( squishme )
+    {
+        FTA(10,&ps[p],0);
+
+        if(badguy(&sprite[i])) sprite[i].xvel = 0;
+
+        if(sprite[i].pal == 1)
+        {
+            hittype[i].picnum = SHOTSPARK1;
+            hittype[i].extra = 1;
+            return 0;
+        }
+
+        return 1;
+    }
+    return 0;
+}
+
+void hitradius( short i, long  r, long  hp1, long  hp2, long  hp3, long  hp4 )
+{
+    spritetype *s,*sj;
+    walltype *wal;
+    long d, q, x1, y1;
+    long sectcnt, sectend, dasect, startwall, endwall, nextsect;
+    short j,k,p,x,nextj,sect;
+    char statlist[] = {0,1,6,10,12,2,5};
+    short *tempshort = (short *)tempbuf;
+
+    s = &sprite[i];
+
+    if(s->picnum == RPG && s->xrepeat < 11) goto SKIPWALLCHECK;
+
+    if(s->picnum != SHRINKSPARK)
+    {
+        tempshort[0] = s->sectnum;
+        dasect = s->sectnum;
+        sectcnt = 0; sectend = 1;
+
+        do
+        {
+            dasect = tempshort[sectcnt++];
+            if(((sector[dasect].ceilingz-s->z)>>8) < r)
+            {
+               d = klabs(wall[sector[dasect].wallptr].x-s->x)+klabs(wall[sector[dasect].wallptr].y-s->y);
+               if(d < r)
+                    checkhitceiling(dasect);
+               else
+               {
+                    d = klabs(wall[wall[wall[sector[dasect].wallptr].point2].point2].x-s->x)+klabs(wall[wall[wall[sector[dasect].wallptr].point2].point2].y-s->y);
+                    if(d < r)
+                        checkhitceiling(dasect);
+               }
+           }
+
+           startwall = sector[dasect].wallptr;
+           endwall = startwall+sector[dasect].wallnum;
+           for(x=startwall,wal=&wall[startwall];x<endwall;x++,wal++)
+               if( ( klabs(wal->x-s->x)+klabs(wal->y-s->y) ) < r)
+           {
+               nextsect = wal->nextsector;
+               if (nextsect >= 0)
+               {
+                   for(dasect=sectend-1;dasect>=0;dasect--)
+                       if (tempshort[dasect] == nextsect) break;
+                   if (dasect < 0) tempshort[sectend++] = nextsect;
+               }
+               x1 = (((wal->x+wall[wal->point2].x)>>1)+s->x)>>1;
+               y1 = (((wal->y+wall[wal->point2].y)>>1)+s->y)>>1;
+               updatesector(x1,y1,&sect);
+               if( sect >= 0 && cansee(x1,y1,s->z,sect,s->x,s->y,s->z,s->sectnum ) )
+                   checkhitwall(i,x,wal->x,wal->y,s->z,s->picnum);
+           }
+        }
+        while (sectcnt < sectend);
+    }
+
+    SKIPWALLCHECK:
+
+    q = -(16<<8)+(TRAND&((32<<8)-1));
+
+    for(x = 0;x<7;x++)
+    {
+        j = headspritestat[statlist[x]];
+        while(j >= 0)
+        {
+            nextj = nextspritestat[j];
+            sj = &sprite[j];
+
+            if( x == 0 || x >= 5 || AFLAMABLE(sj->picnum) )
+            {
+                if( s->picnum != SHRINKSPARK || (sj->cstat&257) )
+                    if( dist( s, sj ) < r )
+                    {
+                        if( badguy(sj) && !cansee( sj->x, sj->y,sj->z+q, sj->sectnum, s->x, s->y, s->z+q, s->sectnum) )
+                            goto BOLT;
+                        checkhitsprite( j, i );
+                    }
+            }
+            else if( sj->extra >= 0 && sj != s && ( sj->picnum == TRIPBOMB || badguy(sj) || sj->picnum == QUEBALL || sj->picnum == STRIPEBALL || (sj->cstat&257) || sj->picnum == DUKELYINGDEAD ) )
+            {
+                if( s->picnum == SHRINKSPARK && sj->picnum != SHARK && ( j == s->owner || sj->xrepeat < 24 ) )
+                {
+                    j = nextj;
+                    continue;
+                }
+                if( s->picnum == MORTER && j == s->owner)
+                {
+                    j = nextj;
+                    continue;
+                }
+
+                if(sj->picnum == APLAYER) sj->z -= PHEIGHT;
+                d = dist( s, sj );
+                if(sj->picnum == APLAYER) sj->z += PHEIGHT;
+
+                if ( d < r && cansee( sj->x, sj->y, sj->z-(8<<8), sj->sectnum, s->x, s->y, s->z-(12<<8), s->sectnum) )
+                {
+                    hittype[j].ang = getangle(sj->x-s->x,sj->y-s->y);
+
+                    if ( s->picnum == RPG && sj->extra > 0)
+                        hittype[j].picnum = RPG;
+                    else
+                    {
+                        if( s->picnum == SHRINKSPARK )
+                            hittype[j].picnum = SHRINKSPARK;
+                        else hittype[j].picnum = RADIUSEXPLOSION;
+                    }
+
+                    if(s->picnum != SHRINKSPARK)
+                    {
+                        if ( d < r/3 )
+                        {
+                            if(hp4 == hp3) hp4++;
+                            hittype[j].extra = hp3 + (TRAND%(hp4-hp3));
+                        }
+                        else if ( d < 2*r/3 )
+                        {
+                            if(hp3 == hp2) hp3++;
+                            hittype[j].extra = hp2 + (TRAND%(hp3-hp2));
+                        }
+                        else if ( d < r )
+                        {
+                            if(hp2 == hp1) hp2++;
+                            hittype[j].extra = hp1 + (TRAND%(hp2-hp1));
+                        }
+
+                        if( sprite[j].picnum != TANK && sprite[j].picnum != ROTATEGUN && sprite[j].picnum != RECON && sprite[j].picnum != BOSS1 && sprite[j].picnum != BOSS2 && sprite[j].picnum != BOSS3 && sprite[j].picnum != BOSS4 )
+                        {
+                            if(sj->xvel < 0) sj->xvel = 0;
+                            sj->xvel += (s->extra<<2);
+                        }
+
+                        if( sj->picnum == PODFEM1 || sj->picnum == FEM1 ||
+                            sj->picnum == FEM2 || sj->picnum == FEM3 ||
+                            sj->picnum == FEM4 || sj->picnum == FEM5 ||
+                            sj->picnum == FEM6 || sj->picnum == FEM7 ||
+                            sj->picnum == FEM8 || sj->picnum == FEM9 ||
+                            sj->picnum == FEM10 || sj->picnum == STATUE ||
+                            sj->picnum == STATUEFLASH || sj->picnum == SPACEMARINE || sj->picnum == QUEBALL || sj->picnum == STRIPEBALL)
+                                checkhitsprite( j, i );
+                    }
+                    else if(s->extra == 0) hittype[j].extra = 0;
+
+                    if ( sj->picnum != RADIUSEXPLOSION &&
+                        s->owner >= 0 && sprite[s->owner].statnum < MAXSTATUS )
+                    {
+                        if(sj->picnum == APLAYER)
+                        {
+                            p = sj->yvel;
+                            if(ps[p].newowner >= 0)
+                            {
+                                ps[p].newowner = -1;
+                                ps[p].posx = ps[p].oposx;
+                                ps[p].posy = ps[p].oposy;
+                                ps[p].posz = ps[p].oposz;
+                                ps[p].ang = ps[p].oang;
+                                updatesector(ps[p].posx,ps[p].posy,&ps[p].cursectnum);
+                                setpal(&ps[p]);
+
+                                k = headspritestat[1];
+                                while(k >= 0)
+                                {
+                                    if(sprite[k].picnum==CAMERA1)
+                                        sprite[k].yvel = 0;
+                                    k = nextspritestat[k];
+                                }
+                            }
+                        }
+                        hittype[j].owner = s->owner;
+                    }
+                }
+            }
+            BOLT:
+            j = nextj;
+        }
+    }
+}
+
+
+movesprite(short spritenum, long xchange, long ychange, long zchange, unsigned long cliptype)
+{
+    long daz,h, oldx, oldy;
+    short retval, dasectnum, a, cd;
+    char bg;
+
+    bg = badguy(&sprite[spritenum]);
+
+    if(sprite[spritenum].statnum == 5 || (bg && sprite[spritenum].xrepeat < 4 ) )
+    {
+        sprite[spritenum].x += (xchange*TICSPERFRAME)>>2;
+        sprite[spritenum].y += (ychange*TICSPERFRAME)>>2;
+        sprite[spritenum].z += (zchange*TICSPERFRAME)>>2;
+        if(bg)
+            setsprite(spritenum,sprite[spritenum].x,sprite[spritenum].y,sprite[spritenum].z);
+        return 0;
+    }
+
+    dasectnum = sprite[spritenum].sectnum;
+
+	daz = sprite[spritenum].z;
+    h = ((tilesizy[sprite[spritenum].picnum]*sprite[spritenum].yrepeat)<<1);
+    daz -= h;
+
+    if( bg )
+    {
+        oldx = sprite[spritenum].x;
+        oldy = sprite[spritenum].y;
+
+        if( sprite[spritenum].xrepeat > 60 )
+            retval = clipmove(&sprite[spritenum].x,&sprite[spritenum].y,&daz,&dasectnum,((xchange*TICSPERFRAME)<<11),((ychange*TICSPERFRAME)<<11),1024L,(4<<8),(4<<8),cliptype);
+        else
+        {
+            if(sprite[spritenum].picnum == LIZMAN)
+                cd = 292L;
+            else if( (actortype[sprite[spritenum].picnum]&3) )
+                cd = sprite[spritenum].clipdist<<2;
+            else
+                cd = 192L;
+
+            retval = clipmove(&sprite[spritenum].x,&sprite[spritenum].y,&daz,&dasectnum,((xchange*TICSPERFRAME)<<11),((ychange*TICSPERFRAME)<<11),cd,(4<<8),(4<<8),cliptype);
+        }
+
+        if( dasectnum < 0 || ( dasectnum >= 0 &&
+            ( ( hittype[spritenum].actorstayput >= 0 && hittype[spritenum].actorstayput != dasectnum ) ||
+              ( ( sprite[spritenum].picnum == BOSS2 ) && sprite[spritenum].pal == 0 && sector[dasectnum].lotag != 3 ) ||
+              ( ( sprite[spritenum].picnum == BOSS1 || sprite[spritenum].picnum == BOSS2 ) && sector[dasectnum].lotag == 1 ) ||
+              ( sector[dasectnum].lotag == 1 && ( sprite[spritenum].picnum == LIZMAN || ( sprite[spritenum].picnum == LIZTROOP && sprite[spritenum].zvel == 0 ) ) )
+            ) )
+          )
+        {
+                sprite[spritenum].x = oldx;
+                sprite[spritenum].y = oldy;
+                if(sector[dasectnum].lotag == 1 && sprite[spritenum].picnum == LIZMAN)
+                    sprite[spritenum].ang = (TRAND&2047);
+                else if( (hittype[spritenum].temp_data[0]&3) == 1 && sprite[spritenum].picnum != COMMANDER )
+                    sprite[spritenum].ang = (TRAND&2047);
+                setsprite(spritenum,oldx,oldy,sprite[spritenum].z);
+                if(dasectnum < 0) dasectnum = 0;
+                return (16384+dasectnum);
+        }
+        if( (retval&49152) >= 32768 && (hittype[spritenum].cgg==0) ) sprite[spritenum].ang += 768;
+    }
+    else
+    {
+        if(sprite[spritenum].statnum == 4)
+            retval =
+                clipmove(&sprite[spritenum].x,&sprite[spritenum].y,&daz,&dasectnum,((xchange*TICSPERFRAME)<<11),((ychange*TICSPERFRAME)<<11),8L,(4<<8),(4<<8),cliptype);
+        else
+            retval =
+                clipmove(&sprite[spritenum].x,&sprite[spritenum].y,&daz,&dasectnum,((xchange*TICSPERFRAME)<<11),((ychange*TICSPERFRAME)<<11),(long)(sprite[spritenum].clipdist<<2),(4<<8),(4<<8),cliptype);
+    }
+
+    if( dasectnum >= 0)
+        if ( (dasectnum != sprite[spritenum].sectnum) )
+            changespritesect(spritenum,dasectnum);
+    daz = sprite[spritenum].z + ((zchange*TICSPERFRAME)>>3);
+    if ((daz > hittype[spritenum].ceilingz) && (daz <= hittype[spritenum].floorz))
+        sprite[spritenum].z = daz;
+    else
+        if (retval == 0)
+            return(16384+dasectnum);
+
+	return(retval);
+}
+
+
+short ssp(short i,unsigned long cliptype) //The set sprite function
+{
+    spritetype *s;
+    long movetype;
+
+    s = &sprite[i];
+
+    movetype = movesprite(i,
+        (s->xvel*(sintable[(s->ang+512)&2047]))>>14,
+        (s->xvel*(sintable[s->ang&2047]))>>14,s->zvel,
+        cliptype);
+
+    return (movetype==0);
+}
+
+void insertspriteq(short i)
+{
+    if(spriteqamount > 0)
+    {
+        if(spriteq[spriteqloc] >= 0)
+            sprite[spriteq[spriteqloc]].xrepeat = 0;
+        spriteq[spriteqloc] = i;
+        spriteqloc = (spriteqloc+1)%spriteqamount;
+    }
+    else sprite[i].xrepeat = sprite[i].yrepeat = 0;
+}
+
+void lotsofmoney(spritetype *s, short n)
+{
+    short i ,j;
+    for(i=n;i>0;i--)
+    {
+        j = EGS(s->sectnum,s->x,s->y,s->z-(TRAND%(47<<8)),MONEY,-32,8,8,TRAND&2047,0,0,0,5);
+        sprite[j].cstat = TRAND&12;
+    }
+}
+
+void lotsofmail(spritetype *s, short n)
+{
+    short i ,j;
+    for(i=n;i>0;i--)
+    {
+        j = EGS(s->sectnum,s->x,s->y,s->z-(TRAND%(47<<8)),MAIL,-32,8,8,TRAND&2047,0,0,0,5);
+        sprite[j].cstat = TRAND&12;
+    }
+}
+
+void lotsofpaper(spritetype *s, short n)
+{
+    short i ,j;
+    for(i=n;i>0;i--)
+    {
+        j = EGS(s->sectnum,s->x,s->y,s->z-(TRAND%(47<<8)),PAPER,-32,8,8,TRAND&2047,0,0,0,5);
+        sprite[j].cstat = TRAND&12;
+    }
+}
+
+
+     
+void guts(spritetype *s,short gtype, short n, short p)
+{
+    long gutz,floorz;
+    short i,a,j;
+    char sx,sy;
+    signed char pal;
+
+    if(badguy(s) && s->xrepeat < 16)
+        sx = sy = 8;
+    else sx = sy = 32;
+
+    gutz = s->z-(8<<8);
+    floorz = getflorzofslope(s->sectnum,s->x,s->y);
+
+    if( gutz > ( floorz-(8<<8) ) )
+        gutz = floorz-(8<<8);
+
+    if(s->picnum == COMMANDER)
+        gutz -= (24<<8);
+
+    if( badguy(s) && s->pal == 6)
+        pal = 6;
+    else pal = 0;
+
+    for(j=0;j<n;j++)
+    {
+        a = TRAND&2047;
+        i = EGS(s->sectnum,s->x+(TRAND&255)-128,s->y+(TRAND&255)-128,gutz-(TRAND&8191),gtype,-32,sx,sy,a,48+(TRAND&31),-512-(TRAND&2047),ps[p].i,5);
+        if(PN == JIBS2)
+        {
+            sprite[i].xrepeat >>= 2;
+            sprite[i].yrepeat >>= 2;
+        }
+        if(pal == 6)
+            sprite[i].pal = 6;
+    }
+}
+
+void gutsdir(spritetype *s,short gtype, short n, short p)
+{
+    long gutz,floorz;
+    short i,a,j;
+    char sx,sy;
+
+    if(badguy(s) && s->xrepeat < 16)
+        sx = sy = 8;
+    else sx = sy = 32;
+
+    gutz = s->z-(8<<8);
+    floorz = getflorzofslope(s->sectnum,s->x,s->y);
+
+    if( gutz > ( floorz-(8<<8) ) )
+        gutz = floorz-(8<<8);
+
+    if(s->picnum == COMMANDER)
+        gutz -= (24<<8);
+
+    for(j=0;j<n;j++)
+    {
+        a = TRAND&2047;
+        i = EGS(s->sectnum,s->x,s->y,gutz,gtype,-32,sx,sy,a,256+(TRAND&127),-512-(TRAND&2047),ps[p].i,5);
+    }
+}
+
+void setsectinterpolate(short i)
+{
+    long j, k, startwall,endwall;
+
+    startwall = sector[SECT].wallptr;
+    endwall = startwall+sector[SECT].wallnum;
+
+    for(j=startwall;j<endwall;j++)
+    {
+        setinterpolation(&wall[j].x);
+        setinterpolation(&wall[j].y);
+        k = wall[j].nextwall;
+        if(k >= 0)
+        {
+            setinterpolation(&wall[k].x);
+            setinterpolation(&wall[k].y);
+            k = wall[k].point2;
+            setinterpolation(&wall[k].x);
+            setinterpolation(&wall[k].y);
+        }
+    }
+}
+
+void clearsectinterpolate(short i)
+{
+    short j,startwall,endwall;
+
+    startwall = sector[SECT].wallptr;
+    endwall = startwall+sector[SECT].wallnum;
+    for(j=startwall;j<endwall;j++)
+    {
+        stopinterpolation(&wall[j].x);
+        stopinterpolation(&wall[j].y);
+        if(wall[j].nextwall >= 0)
+        {
+            stopinterpolation(&wall[wall[j].nextwall].x);
+            stopinterpolation(&wall[wall[j].nextwall].y);
+        }
+    }
+}
+
+void ms(short i)
+{
+    //T1,T2 and T3 are used for all the sector moving stuff!!!
+
+    short startwall,endwall,x;
+    long tx,ty,j,k;
+    spritetype *s;
+
+    s = &sprite[i];
+
+    s->x += (s->xvel*(sintable[(s->ang+512)&2047]))>>14;
+    s->y += (s->xvel*(sintable[s->ang&2047]))>>14;
+
+    j = T2;
+    k = T3;
+
+    startwall = sector[s->sectnum].wallptr;
+    endwall = startwall+sector[s->sectnum].wallnum;
+    for(x=startwall;x<endwall;x++)
+    {
+        rotatepoint(
+            0,0,
+            msx[j],msy[j],
+            k&2047,&tx,&ty);
+
+        dragpoint(x,s->x+tx,s->y+ty);
+
+        j++;
+    }
+}
+
+void movefta(void)
+{
+    long x, px, py, sx, sy;
+    short i, j, p, psect, ssect, nexti;
+    spritetype *s;
+
+    i = headspritestat[2];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        s = &sprite[i];
+        p = findplayer(s,&x);
+
+        ssect = psect = s->sectnum;
+
+        if(sprite[ps[p].i].extra > 0 )
+        {
+            if( x < 30000 )
+            {
+                hittype[i].timetosleep++;
+                if( hittype[i].timetosleep >= (x>>8) )
+                {
+                    if(badguy(s))
+                    {
+                        px = ps[p].oposx+64-(TRAND&127);
+                        py = ps[p].oposy+64-(TRAND&127);
+                        updatesector(px,py,&psect);
+                        if(psect == -1)
+                        {
+                            i = nexti;
+                            continue;
+                        }
+                        sx = s->x+64-(TRAND&127);
+                        sy = s->y+64-(TRAND&127);
+                        updatesector(px,py,&ssect);
+                        if(ssect == -1)
+                        {
+                            i = nexti;
+                            continue;
+                        }
+                        j = cansee(sx,sy,s->z-(TRAND%(52<<8)),s->sectnum,px,py,ps[p].oposz-(TRAND%(32<<8)),ps[p].cursectnum);
+                    }
+                    else
+                        j = cansee(s->x,s->y,s->z-((TRAND&31)<<8),s->sectnum,ps[p].oposx,ps[p].oposy,ps[p].oposz-((TRAND&31)<<8),ps[p].cursectnum);
+
+       //             j = 1;
+
+                    if(j) switch(s->picnum)
+                    {
+                        case RUBBERCAN:
+                        case EXPLODINGBARREL:
+                        case WOODENHORSE:
+                        case HORSEONSIDE:
+                        case CANWITHSOMETHING:
+                        case CANWITHSOMETHING2:
+                        case CANWITHSOMETHING3:
+                        case CANWITHSOMETHING4:
+                        case FIREBARREL:
+                        case FIREVASE:
+                        case NUKEBARREL:
+                        case NUKEBARRELDENTED:
+                        case NUKEBARRELLEAKED:
+                        case TRIPBOMB:
+                            if (sector[s->sectnum].ceilingstat&1)
+                                s->shade = sector[s->sectnum].ceilingshade;
+                            else s->shade = sector[s->sectnum].floorshade;
+
+                            hittype[i].timetosleep = 0;
+                            changespritestat(i,6);
+                            break;
+                        default:
+                            hittype[i].timetosleep = 0;
+                            check_fta_sounds(i);
+                            changespritestat(i,1);
+                            break;
+                    }
+                    else hittype[i].timetosleep = 0;
+                }
+            }
+            if( badguy( s ) )
+            {
+                if (sector[s->sectnum].ceilingstat&1)
+                    s->shade = sector[s->sectnum].ceilingshade;
+                else s->shade = sector[s->sectnum].floorshade;
+            }
+        }
+        i = nexti;
+    }
+}
+
+short ifhitsectors(short sectnum)
+{
+    short i;
+
+    i = headspritestat[5];
+    while(i >= 0)
+    {
+        if( PN == EXPLOSION2 && sectnum == SECT )
+            return i;
+        i = nextspritestat[i];
+    }
+    return -1;
+}
+
+short ifhitbyweapon(short sn)
+{
+    short j, k, p;
+    spritetype *npc;
+
+    if( hittype[sn].extra >= 0 )
+    {
+        if(sprite[sn].extra >= 0 )
+        {
+            npc = &sprite[sn];
+
+            if(npc->picnum == APLAYER)
+            {
+                if(ud.god && hittype[sn].picnum != SHRINKSPARK ) return -1;
+
+                p = npc->yvel;
+                j = hittype[sn].owner;
+
+                if( j >= 0 &&
+                    sprite[j].picnum == APLAYER &&
+                    ud.coop == 1 &&
+                    ud.ffire == 0 )
+                        return -1;
+
+                npc->extra -= hittype[sn].extra;
+
+                if(j >= 0)
+                {
+                    if(npc->extra <= 0 && hittype[sn].picnum != FREEZEBLAST)
+                    {
+                        npc->extra = 0;
+
+                        ps[p].wackedbyactor = j;
+
+                        if( sprite[hittype[sn].owner].picnum == APLAYER && p != sprite[hittype[sn].owner].yvel )
+                            ps[p].frag_ps = sprite[j].yvel;
+
+                        hittype[sn].owner = ps[p].i;
+                    }
+                }
+
+                switch(hittype[sn].picnum)
+                {
+                    case RADIUSEXPLOSION:
+                    case RPG:
+                    case HYDRENT:
+                    case HEAVYHBOMB:
+                    case SEENINE:
+                    case OOZFILTER:
+                    case EXPLODINGBARREL:
+                        ps[p].posxv +=
+                            hittype[sn].extra*(sintable[(hittype[sn].ang+512)&2047])<<2;
+                        ps[p].posyv +=
+                            hittype[sn].extra*(sintable[hittype[sn].ang&2047])<<2;
+                        break;
+                    default:
+                        ps[p].posxv +=
+                            hittype[sn].extra*(sintable[(hittype[sn].ang+512)&2047])<<1;
+                        ps[p].posyv +=
+                            hittype[sn].extra*(sintable[hittype[sn].ang&2047])<<1;
+                        break;
+                }
+            }
+            else
+            {
+                if(hittype[sn].extra == 0 )
+                    if( hittype[sn].picnum == SHRINKSPARK && npc->xrepeat < 24 )
+                        return -1;
+
+                npc->extra -= hittype[sn].extra;
+                if(npc->picnum != RECON && npc->owner >= 0 && sprite[npc->owner].statnum < MAXSTATUS )
+                    npc->owner = hittype[sn].owner;
+            }
+
+            hittype[sn].extra = -1;
+            return hittype[sn].picnum;
+        }
+    }
+
+    hittype[sn].extra = -1;
+    return -1;
+}
+
+void movecyclers(void)
+{
+    short q, j, x, t, s, *c;
+    walltype *wal;
+    char cshade;
+
+    for(q=numcyclers-1;q>=0;q--)
+    {
+
+        c = &cyclers[q][0];
+        s = c[0];
+        
+        t = c[3];
+        j = t+(sintable[c[1]&2047]>>10);
+        cshade = c[2];
+
+        if( j < cshade ) j = cshade;
+        else if( j > t )  j = t;
+
+        c[1] += sector[s].extra;
+        if(c[5])
+        {
+            wal = &wall[sector[s].wallptr];
+            for(x = sector[s].wallnum;x>0;x--,wal++)
+                if( wal->hitag != 1 )
+            {
+                wal->shade = j;
+
+                if( (wal->cstat&2) && wal->nextwall >= 0)
+                    wall[wal->nextwall].shade = j;
+
+            }
+            sector[s].floorshade = sector[s].ceilingshade = j;
+        }
+    }
+}
+
+void movedummyplayers(void)
+{
+    short i, p, nexti;
+
+    i = headspritestat[13];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        p = sprite[OW].yvel;
+
+        if( ps[p].on_crane >= 0 || sector[ps[p].cursectnum].lotag != 1 || sprite[ps[p].i].extra <= 0 )
+        {
+            ps[p].dummyplayersprite = -1;
+            KILLIT(i);
+        }
+        else
+        {
+            if(ps[p].on_ground && ps[p].on_warping_sector == 1 && sector[ps[p].cursectnum].lotag == 1 )
+            {
+                CS = 257;
+                SZ = sector[SECT].ceilingz+(27<<8);
+                SA = ps[p].ang;
+                if(T1 == 8)
+                    T1 = 0;
+                else T1++;
+            }
+            else
+            {
+                if(sector[SECT].lotag != 2) SZ = sector[SECT].floorz;
+                CS = (short) 32768;
+            }
+        }
+
+        SX += (ps[p].posx-ps[p].oposx);
+        SY += (ps[p].posy-ps[p].oposy);
+        setsprite(i,SX,SY,SZ);
+
+        BOLT:
+
+        i = nexti;
+    }
+}
+
+
+short otherp;
+void moveplayers(void) //Players
+{
+    short i , nexti;
+    long otherx;
+    spritetype *s;
+    struct player_struct *p;
+
+    i = headspritestat[10];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        s = &sprite[i];
+        p = &ps[s->yvel];
+        if(s->owner >= 0)
+        {
+            if(p->newowner >= 0 ) //Looking thru the camera
+            {
+                s->x = p->oposx;
+                s->y = p->oposy;
+                hittype[i].bposz = s->z = p->oposz+PHEIGHT;
+                s->ang = p->oang;
+                setsprite(i,s->x,s->y,s->z);
+            }
+            else
+            {
+                if(ud.multimode > 1)
+                    otherp = findotherplayer(s->yvel,&otherx);
+                else
+                {
+                    otherp = s->yvel;
+                    otherx = 0;
+                }
+
+                execute(i,s->yvel,otherx);
+
+                if(ud.multimode > 1)
+                    if( sprite[ps[otherp].i].extra > 0 )
+                {
+                    if( s->yrepeat > 32 && sprite[ps[otherp].i].yrepeat < 32)
+                    {
+                        if( otherx < 1400 && p->knee_incs == 0 )
+                        {
+                            p->knee_incs = 1;
+                            p->weapon_pos = -1;
+                            p->actorsqu = ps[otherp].i;
+                        }
+                    }
+                }
+                if(ud.god)
+                {
+                    s->extra = max_player_health;
+                    s->cstat = 257;
+                    p->jetpack_amount =     1599;
+                }
+
+
+                if( s->extra > 0 )
+                {
+                    hittype[i].owner = i;
+
+                    if(ud.god == 0)
+                        if( ceilingspace(s->sectnum) || floorspace(s->sectnum) )
+                            quickkill(p);
+                }
+                else
+                {
+                    
+                    p->posx = s->x;
+                    p->posy = s->y;
+                    p->posz = s->z-(20<<8);
+
+                    p->newowner = -1;
+
+                    if( p->wackedbyactor >= 0 && sprite[p->wackedbyactor].statnum < MAXSTATUS )
+                    {
+                        p->ang += getincangle(p->ang,getangle(sprite[p->wackedbyactor].x-p->posx,sprite[p->wackedbyactor].y-p->posy))>>1;
+                        p->ang &= 2047;
+                    }
+
+                }
+                s->ang = p->ang;
+            }
+        }
+        else
+        {
+            if(p->holoduke_on == -1)
+                KILLIT(i);
+
+            hittype[i].bposx = s->x;
+            hittype[i].bposy = s->y;
+            hittype[i].bposz = s->z;
+
+            s->cstat = 0;
+
+            if(s->xrepeat < 42)
+            {
+                s->xrepeat += 4;
+                s->cstat |= 2;
+            }
+            else s->xrepeat = 42;
+            if(s->yrepeat < 36)
+                s->yrepeat += 4;
+            else
+            {
+                s->yrepeat = 36;
+                if(sector[s->sectnum].lotag != 2)
+                    makeitfall(i);
+                if(s->zvel == 0 && sector[s->sectnum].lotag == 1)
+                    s->z += (32<<8);
+            }
+
+            if(s->extra < 8)
+            {
+                s->xvel = 128;
+                s->ang = p->ang;
+                s->extra++;
+                IFMOVING;
+            }
+            else
+            {
+                s->ang = 2047-p->ang;
+                setsprite(i,s->x,s->y,s->z);
+            }
+        }
+
+        if (sector[s->sectnum].ceilingstat&1)
+            s->shade += (sector[s->sectnum].ceilingshade-s->shade)>>1;
+        else
+            s->shade += (sector[s->sectnum].floorshade-s->shade)>>1;
+
+        BOLT:
+        i = nexti;
+    }
+}
+
+
+void movefx(void)
+{
+    short i, j, nexti, p;
+    long x, ht;
+    spritetype *s;
+
+    i = headspritestat[11];
+    while(i >= 0)
+    {
+        s = &sprite[i];
+
+        nexti = nextspritestat[i];
+
+        switch(s->picnum)
+        {
+            case RESPAWN:
+                if(sprite[i].extra == 66)
+                {
+                    j = spawn(i,SHT);
+//                    sprite[j].pal = sprite[i].pal;
+                    KILLIT(i);
+                }
+                else if(sprite[i].extra > (66-13))
+                    sprite[i].extra++;
+                break;
+
+            case MUSICANDSFX:
+
+                ht = s->hitag;
+
+                if(T2 != SoundToggle)
+                {
+                    T2 = SoundToggle;
+                    T1 = 0;
+                }
+
+                if(s->lotag >= 1000 && s->lotag < 2000)
+                {
+                    x = ldist(&sprite[ps[screenpeek].i],s);
+                    if( x < ht && T1 == 0 )
+                    {
+                        FX_SetReverb( s->lotag - 1000 );
+                        T1 = 1;
+                    }
+                    if( x >= ht && T1 == 1 )
+                    {
+                        FX_SetReverb(0);
+                        FX_SetReverbDelay(0);
+                        T1 = 0;
+                    }
+                }
+                else if(s->lotag < 999 && (unsigned)sector[s->sectnum].lotag < 9 && AmbienceToggle && sector[SECT].floorz != sector[SECT].ceilingz)
+                {
+                    if( (soundm[s->lotag]&2) )
+                    {
+                        x = dist(&sprite[ps[screenpeek].i],s);
+                        if( x < ht && T1 == 0 && FX_VoiceAvailable(soundpr[s->lotag]-1) )
+                        {
+                            if(numenvsnds == NumVoices)
+                            {
+                                j = headspritestat[11];
+                                while(j >= 0)
+                                {
+                                    if( PN == MUSICANDSFX && j != i && sprite[j].lotag < 999 && hittype[j].temp_data[0] == 1 && dist(&sprite[j],&sprite[ps[screenpeek].i]) > x )
+                                    {
+                                        stopenvsound(sprite[j].lotag,j);
+                                        break;
+                                    }
+                                    j = nextspritestat[j];
+                                }
+                                if(j == -1) goto BOLT;
+                            }
+                            spritesound(s->lotag,i);
+                            T1 = 1;
+                        }
+                        if( x >= ht && T1 == 1 )
+                        {
+                            T1 = 0;
+                            stopenvsound(s->lotag,i);
+                        }
+                    }
+                    if( (soundm[s->lotag]&16) )
+                    {
+                        if(T5 > 0) T5--;
+                        else for(p=connecthead;p>=0;p=connectpoint2[p])
+                            if( p == myconnectindex && ps[p].cursectnum == s->sectnum )
+                        {
+                            j = s->lotag+((unsigned)global_random%(s->hitag+1));
+                            sound(j);
+                            T5 =  26*40 + (global_random%(26*40));
+                        }
+                    }
+                }
+                break;
+        }
+        BOLT:
+        i = nexti;
+    }
+}
+
+
+
+void movefallers(void)
+{
+    short i, nexti, sect, j;
+    spritetype *s;
+    long x;
+
+    i = headspritestat[12];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+        s = &sprite[i];
+
+        sect = s->sectnum;
+
+        if( T1 == 0 )
+        {
+            s->z -= (16<<8);
+            T2 = s->ang;
+            x = s->extra;
+            IFHIT
+            {
+                if( j == FIREEXT || j == RPG || j == RADIUSEXPLOSION || j == SEENINE || j == OOZFILTER )
+                {
+                    if(s->extra <= 0)
+                    {
+                        T1 = 1;
+                        j = headspritestat[12];
+                        while(j >= 0)
+                        {
+                            if(sprite[j].hitag == SHT)
+                            {
+                                hittype[j].temp_data[0] = 1;
+                                sprite[j].cstat &= (65535-64);
+                                if(sprite[j].picnum == CEILINGSTEAM || sprite[j].picnum == STEAM)
+                                    sprite[j].cstat |= 32768;
+                            }
+                            j = nextspritestat[j];
+                        }
+                    }
+                }
+                else
+                {
+                    hittype[i].extra = 0;
+                    s->extra = x;
+                }
+            }
+            s->ang = T2;
+            s->z += (16<<8);
+        }
+        else if(T1 == 1)
+        {
+            if(s->lotag > 0)
+            {
+                s->lotag-=3;
+                if(s->lotag <= 0)
+                {
+                    s->xvel = (32+TRAND&63);
+                    s->zvel = -(1024+(TRAND&1023));
+                }
+            }
+            else
+            {
+                if( s->xvel > 0)
+                {
+                    s->xvel -= 8;
+                    ssp(i,CLIPMASK0);
+                }
+
+                if( floorspace(s->sectnum) ) x = 0;
+                else
+                {
+                    if(ceilingspace(s->sectnum))
+                        x = gc/6;
+                    else
+                        x = gc;
+                }
+
+                if( s->z < (sector[sect].floorz-FOURSLEIGHT) )
+                {
+                    s->zvel += x;
+                    if(s->zvel > 6144)
+                        s->zvel = 6144;
+                    s->z += s->zvel;
+                }
+                if( (sector[sect].floorz-s->z) < (16<<8) )
+                {
+                    j = 1+(TRAND&7);
+                    for(x=0;x<j;x++) RANDOMSCRAP;
+                    KILLIT(i);
+                }
+            }
+        }
+
+        BOLT:
+        i = nexti;
+    }
+}
+
+void movestandables(void)
+{
+    short i, j, k, m, nexti, nextj, nextk, p, q, sect;
+    long l=0, x, *t, x1, y1;
+    spritetype *s;
+
+    i = headspritestat[6];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        t = &hittype[i].temp_data[0];
+        s = &sprite[i];
+        sect = s->sectnum;
+
+        if( sect < 0 ) KILLIT(i);
+
+        hittype[i].bposx = s->x;
+        hittype[i].bposy = s->y;
+        hittype[i].bposz = s->z;
+
+        IFWITHIN(CRANE,CRANE+3)
+        {
+            //t[0] = state
+            //t[1] = checking sector number
+
+            if(s->xvel) getglobalz(i);
+
+            if( t[0] == 0 ) //Waiting to check the sector
+            {
+                j = headspritesect[t[1]];
+                while(j>=0)
+                {
+                    nextj = nextspritesect[j];
+                    switch( sprite[j].statnum )
+                    {
+                        case 1:
+                        case 2:
+                        case 6:
+                        case 10:
+                            s->ang = getangle(msx[t[4]+1]-s->x,msy[t[4]+1]-s->y);
+                            setsprite(j,msx[t[4]+1],msy[t[4]+1],sprite[j].z);
+                            t[0]++;
+                            goto BOLT;
+                    }
+                    j = nextj;
+                }
+            }
+
+            else if(t[0]==1)
+            {
+                if( s->xvel < 184 )
+                {
+                    s->picnum = CRANE+1;
+                    s->xvel += 8;
+                }
+                IFMOVING;
+                if(sect == t[1])
+                    t[0]++;
+            }
+            else if(t[0]==2 || t[0]==7)
+            {
+                s->z += (1024+512);
+
+                if(t[0]==2)
+                {
+                    if( (sector[sect].floorz - s->z) < (64<<8) )
+                        if(s->picnum > CRANE) s->picnum--;
+
+                    if( (sector[sect].floorz - s->z) < (4096+1024))
+                        t[0]++;
+                }
+                if(t[0]==7)
+                {
+                    if( (sector[sect].floorz - s->z) < (64<<8) )
+                    {
+                        if(s->picnum > CRANE) s->picnum--;
+                        else
+                        {
+                            if(s->owner==-2)
+                            {
+                                spritesound(DUKE_GRUNT,ps[p].i);
+                                p = findplayer(s,&x);
+                                if(ps[p].on_crane == i)
+                                    ps[p].on_crane = -1;
+                            }
+                            t[0]++;
+                            s->owner = -1;
+                        }
+                    }
+                }
+            }
+            else if(t[0]==3)
+            {
+                s->picnum++;
+                if( s->picnum == (CRANE+2) )
+                {
+                    p = checkcursectnums(t[1]);
+                    if(p >= 0 && ps[p].on_ground)
+                    {
+                        s->owner = -2;
+                        ps[p].on_crane = i;
+                        spritesound(DUKE_GRUNT,ps[p].i);
+                        ps[p].ang = s->ang+1024;
+                    }
+                    else
+                    {
+                        j = headspritesect[t[1]];
+                        while(j>=0)
+                        {
+                            switch( sprite[j].statnum )
+                            {
+                                case 1:
+                                case 6:
+                                    s->owner = j;
+                                    break;
+                            }
+                            j = nextspritesect[j];
+                        }
+                    }
+
+                    t[0]++;//Grabbed the sprite
+                    t[2]=0;
+                    goto BOLT;
+                }
+            }
+            else if(t[0]==4) //Delay before going up
+            {
+                t[2]++;
+                if(t[2] > 10)
+                    t[0]++;
+            }
+            else if(t[0]==5 || t[0] == 8)
+            {
+                if(t[0]==8 && s->picnum < (CRANE+2))
+                    if( (sector[sect].floorz-s->z) > 8192)
+                        s->picnum++;
+
+                if(s->z < msx[t[4]+2])
+                {
+                    t[0]++;
+                    s->xvel = 0;
+                }
+                else
+                    s->z -= (1024+512);
+            }
+            else if(t[0]==6)
+            {
+                if( s->xvel < 192 )
+                    s->xvel += 8;
+                s->ang = getangle(msx[t[4]]-s->x,msy[t[4]]-s->y);
+                IFMOVING;
+                if( ((s->x-msx[t[4]])*(s->x-msx[t[4]])+(s->y-msy[t[4]])*(s->y-msy[t[4]]) ) < (128*128) )
+                    t[0]++;
+            }
+
+            else if(t[0]==9)
+                t[0] = 0;
+
+            setsprite(msy[t[4]+2],s->x,s->y,s->z-(34<<8));
+
+            if(s->owner != -1)
+            {
+                p = findplayer(s,&x);
+
+                IFHIT
+                {
+                    if(s->owner == -2)
+                        if(ps[p].on_crane == i)
+                            ps[p].on_crane = -1;
+                    s->owner = -1;
+                    s->picnum = CRANE;
+                    goto BOLT;
+                }
+
+                if(s->owner >= 0)
+                {
+                    setsprite(s->owner,s->x,s->y,s->z);
+
+                    hittype[s->owner].bposx = s->x;
+                    hittype[s->owner].bposy = s->y;
+                    hittype[s->owner].bposz = s->z;
+
+                    s->zvel = 0;
+                }
+                else if(s->owner == -2)
+                {
+                    ps[p].oposx = ps[p].posx = s->x-(sintable[(ps[p].ang+512)&2047]>>6);
+                    ps[p].oposy = ps[p].posy = s->y-(sintable[ps[p].ang&2047]>>6);
+                    ps[p].oposz = ps[p].posz = s->z+(2<<8);
+                    setsprite(ps[p].i,ps[p].posx,ps[p].posy,ps[p].posz);
+                    ps[p].cursectnum = sprite[ps[p].i].sectnum;
+                }
+            }
+
+            goto BOLT;
+        }
+
+        IFWITHIN(WATERFOUNTAIN,WATERFOUNTAIN+3)
+        {
+            if(t[0] > 0)
+            {
+                if( t[0] < 20 )
+                {
+                    t[0]++;
+
+                    s->picnum++;
+
+                    if( s->picnum == ( WATERFOUNTAIN+3 ) )
+                        s->picnum = WATERFOUNTAIN+1;
+                }
+                else
+                {
+                    p = findplayer(s,&x);
+
+                    if(x > 512)
+                    {
+                        t[0] = 0;
+                        s->picnum = WATERFOUNTAIN;
+                    }
+                    else t[0] = 1;
+                }
+            }
+            goto BOLT;
+        }
+
+        if( AFLAMABLE(s->picnum) )
+        {
+            if(T1 == 1)
+            {
+                T2++;
+                if( (T2&3) > 0) goto BOLT;
+
+                if( s->picnum == TIRE && T2 == 32 )
+                {
+                    s->cstat = 0;
+                    j = spawn(i,BLOODPOOL);
+                    sprite[j].shade = 127;
+                }
+                else
+                {
+                    if(s->shade < 64) s->shade++;
+                    else KILLIT(i);
+                }
+
+                j = s->xrepeat-(TRAND&7);
+                if(j < 10)
+                {
+                    KILLIT(i);
+                }
+
+                s->xrepeat = j;
+
+                j = s->yrepeat-(TRAND&7);
+                if(j < 4) { KILLIT(i); }
+                s->yrepeat = j;
+            }
+            if(s->picnum == BOX)
+            {
+                makeitfall(i);
+                hittype[i].ceilingz = sector[s->sectnum].ceilingz;
+            }
+            goto BOLT;
+        }
+
+        if(s->picnum == TRIPBOMB)
+        {
+            if(T3 > 0)
+            {
+                T3--;
+                if(T3 == 8)
+                {
+                    spritesound(LASERTRIP_EXPLODE,i);
+                    for(j=0;j<5;j++) RANDOMSCRAP;
+                    x = s->extra;
+                    hitradius( i, tripbombblastradius, x>>2,x>>1,x-(x>>2),x);
+
+                    j = spawn(i,EXPLOSION2);
+                    sprite[j].ang = s->ang;
+                    sprite[j].xvel = 348;
+                    ssp(j,CLIPMASK0);
+
+                    j = headspritestat[5];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].picnum == LASERLINE && s->hitag == sprite[j].hitag)
+                            sprite[j].xrepeat = sprite[j].yrepeat = 0;
+                        j = nextspritestat[j];
+                    }
+                    KILLIT(i);
+                }
+                goto BOLT;
+            }
+            else
+            {
+                x = s->extra;
+                s->extra = 1;
+                l = s->ang;
+                IFHIT { T3 = 16; }
+                s->extra = x;
+                s->ang = l;
+            }
+
+            if( T1 < 32 )
+            {
+                p = findplayer(s,&x);
+                if( x > 768 ) T1++;
+                else if(T1 > 16) T1++;
+            }
+            if( T1 == 32 )
+            {
+                l = s->ang;
+                s->ang = T6;
+
+                T4 = s->x;T5 = s->y;
+                s->x += sintable[(T6+512)&2047]>>9;
+                s->y += sintable[(T6)&2047]>>9;
+                s->z -= (3<<8);
+                setsprite(i,s->x,s->y,s->z);
+
+                x = hitasprite(i,&m);
+
+                hittype[i].lastvx = x;
+
+                s->ang = l;
+
+                k = 0;
+
+                while(x > 0)
+                {
+                    j = spawn(i,LASERLINE);
+                    setsprite(j,sprite[j].x,sprite[j].y,sprite[j].z);
+                    sprite[j].hitag = s->hitag;
+                    hittype[j].temp_data[1] = sprite[j].z;
+
+                    s->x += sintable[(T6+512)&2047]>>4;
+                    s->y += sintable[(T6)&2047]>>4;
+
+                    if( x < 1024 )
+                    {
+                        sprite[j].xrepeat = x>>5;
+                        break;
+                    }
+                    x -= 1024;
+                }
+
+                T1++;
+                s->x = T4;s->y = T5;
+                s->z += (3<<8);
+                setsprite(i,s->x,s->y,s->z);
+                T4 = 0;
+                if( m >= 0 )
+                {
+                    T3 = 13;
+                    spritesound(LASERTRIP_ARMING,i);
+                }
+                else T3 = 0;
+            }
+            if(T1 == 33)
+            {
+                T2++;
+
+
+                T4 = s->x;T5 = s->y;
+                s->x += sintable[(T6+512)&2047]>>9;
+                s->y += sintable[(T6)&2047]>>9;
+                s->z -= (3<<8);
+                setsprite(i,s->x,s->y,s->z);
+
+                x = hitasprite(i,&m);
+
+                s->x = T4;s->y = T5;
+                s->z += (3<<8);
+                setsprite(i,s->x,s->y,s->z);
+
+                if( hittype[i].lastvx != x )
+                {
+                    T3 = 13;
+                    spritesound(LASERTRIP_ARMING,i);
+                }
+            }
+            goto BOLT;
+        }
+
+
+        if( s->picnum >= CRACK1 && s->picnum <= CRACK4 )
+        {
+            if(s->hitag > 0)
+            {
+                t[0] = s->cstat;
+                t[1] = s->ang;
+                j = ifhitbyweapon(i);
+                if(j == FIREEXT || j == RPG || j == RADIUSEXPLOSION || j == SEENINE || j == OOZFILTER )
+                {
+                    j = headspritestat[6];
+                    while(j >= 0)
+                    {
+                        if(s->hitag == sprite[j].hitag && ( sprite[j].picnum == OOZFILTER || sprite[j].picnum == SEENINE ) )
+                            if(sprite[j].shade != -32)
+                                sprite[j].shade = -32;
+                        j = nextspritestat[j];
+                    }
+
+                    goto DETONATE;
+                }
+                else
+                {
+                    s->cstat = t[0];
+                    s->ang = t[1];
+                    s->extra = 0;
+                }
+            }
+            goto BOLT;
+        }
+
+        if( s->picnum == FIREEXT )
+        {
+            j = ifhitbyweapon(i);
+            if( j == -1 ) goto BOLT;
+
+            for(k=0;k<16;k++)
+            {
+                j = EGS(SECT,SX,SY,SZ-(TRAND%(48<<8)),SCRAP3+(TRAND&3),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(sprite[i].zvel>>2),i,5);
+                sprite[j].pal = 2;
+            }
+
+            spawn(i,EXPLOSION2);
+            spritesound(PIPEBOMB_EXPLODE,i);
+            spritesound(GLASS_HEAVYBREAK,i);
+
+            if(s->hitag > 0)
+            {
+                j = headspritestat[6];
+                while(j >= 0)
+                {
+                    if(s->hitag == sprite[j].hitag && ( sprite[j].picnum == OOZFILTER || sprite[j].picnum == SEENINE ) )
+                        if(sprite[j].shade != -32)
+                            sprite[j].shade = -32;
+                    j = nextspritestat[j];
+                }
+
+                x = s->extra;
+                spawn(i,EXPLOSION2);
+                hitradius( i, pipebombblastradius,x>>2, x-(x>>1),x-(x>>2), x);
+                spritesound(PIPEBOMB_EXPLODE,i);
+
+                goto DETONATE;
+            }
+            else
+            {
+                hitradius(i,seenineblastradius,10,15,20,25);
+                KILLIT(i);
+            }
+            goto BOLT;
+        }
+
+        if(s->picnum == OOZFILTER || s->picnum == SEENINE || s->picnum == SEENINEDEAD || s->picnum == (SEENINEDEAD+1) )
+        {
+            if(s->shade != -32 && s->shade != -33)
+            {
+                if(s->xrepeat)
+                    j = (ifhitbyweapon(i) >= 0);
+                else
+                    j = 0;
+
+                if( j || s->shade == -31 )
+                {
+                    if(j) s->lotag = 0;
+
+                    t[3] = 1;
+
+                    j = headspritestat[6];
+                    while(j >= 0)
+                    {
+                        if(s->hitag == sprite[j].hitag && ( sprite[j].picnum == SEENINE || sprite[j].picnum == OOZFILTER ) )
+                            sprite[j].shade = -32;
+                        j = nextspritestat[j];
+                    }
+                }
+            }
+            else
+            {
+                if(s->shade == -32)
+                {
+                    if(s->lotag > 0)
+                    {
+                        s->lotag-=3;
+                        if(s->lotag <= 0) s->lotag = -99;
+                    }
+                    else
+                        s->shade = -33;
+                }
+                else
+                {
+                    if( s->xrepeat > 0 )
+                    {
+                        T3++;
+                        if(T3 == 3)
+                        {
+                            if( s->picnum == OOZFILTER )
+                            {
+                                T3 = 0;
+                                goto DETONATE;
+                            }
+                            if( s->picnum != (SEENINEDEAD+1) )
+                            {
+                                T3 = 0;
+
+                                if(s->picnum == SEENINEDEAD) s->picnum++;
+                                else if(s->picnum == SEENINE)
+                                    s->picnum = SEENINEDEAD;
+                            }
+                            else goto DETONATE;
+                        }
+                        goto BOLT;
+                    }
+
+                    DETONATE:
+
+                    earthquaketime = 16;
+
+                    j = headspritestat[3];
+                    while(j >= 0)
+                    {
+                        if( s->hitag == sprite[j].hitag )
+                        {
+                            if(sprite[j].lotag == 13)
+                            {
+                                if( hittype[j].temp_data[2] == 0 )
+                                    hittype[j].temp_data[2] = 1;
+                            }
+                            else if(sprite[j].lotag == 8)
+                                hittype[j].temp_data[4] = 1;
+                            else if(sprite[j].lotag == 18)
+                            {
+                                if(hittype[j].temp_data[0] == 0)
+                                    hittype[j].temp_data[0] = 1;
+                            }
+                            else if(sprite[j].lotag == 21)
+                                hittype[j].temp_data[0] = 1;
+                        }
+                        j = nextspritestat[j];
+                    }
+
+                    s->z -= (32<<8);
+
+                    if( ( t[3] == 1 && s->xrepeat ) || s->lotag == -99 )
+                    {
+                        x = s->extra;
+                        spawn(i,EXPLOSION2);
+                        hitradius( i,seenineblastradius,x>>2, x-(x>>1),x-(x>>2), x);
+                        spritesound(PIPEBOMB_EXPLODE,i);
+                    }
+
+                    if(s->xrepeat)
+                        for(x=0;x<8;x++) RANDOMSCRAP;
+
+                    KILLIT(i);
+                }
+            }
+            goto BOLT;
+        }
+
+        if(s->picnum == MASTERSWITCH)
+        {
+            if(s->yvel == 1)
+                {
+                    s->hitag--;
+                    if(s->hitag <= 0)
+                    {
+                        operatesectors(sect,i);
+
+                        j = headspritesect[sect];
+                        while(j >= 0)
+                        {
+                            if(sprite[j].statnum == 3)
+                            {
+                                switch(sprite[j].lotag)
+                                {
+                                    case 2:
+                                    case 21:
+                                    case 31:
+                                    case 32:
+                                    case 36:
+                                        hittype[j].temp_data[0] = 1;
+                                        break;
+                                    case 3:
+                                        hittype[j].temp_data[4] = 1;
+                                        break;
+                                }
+                            }
+                            else if(sprite[j].statnum == 6)
+                            {
+                                switch(sprite[j].picnum)
+                                {
+                                    case SEENINE:
+                                    case OOZFILTER:
+                                        sprite[j].shade = -31;
+                                        break;
+                                }
+                            }
+                            j = nextspritesect[j];
+                        }
+                        KILLIT(i);
+                    }
+                }
+                goto BOLT;
+        }
+
+        switch(s->picnum)
+        {
+            case VIEWSCREEN:
+            case VIEWSCREEN2:
+
+                if(s->xrepeat == 0) KILLIT(i);
+
+                p = findplayer(s, &x);
+
+                if( x < 2048 )
+                {
+                    if( SP == 1 )
+                        camsprite = i;
+                }
+                else if( camsprite != -1 && T1 == 1)
+                {
+                    camsprite = -1;
+                    T1 = 0;
+                    loadtile(s->picnum);
+                }
+
+                goto BOLT;
+
+            case TRASH:
+
+                if(s->xvel == 0) s->xvel = 1;
+                IFMOVING
+                {
+                    makeitfall(i);
+                    if(TRAND&1) s->zvel -= 256;
+                    if( klabs(s->xvel) < 48 )
+                        s->xvel += (TRAND&3);
+                }
+                else KILLIT(i);
+                break;
+
+            case SIDEBOLT1:
+            case SIDEBOLT1+1:
+            case SIDEBOLT1+2:
+            case SIDEBOLT1+3:
+                p = findplayer(s, &x);
+                if( x > 20480 ) goto BOLT;
+
+                CLEAR_THE_BOLT2:
+                if(t[2])
+                {
+                    t[2]--;
+                    goto BOLT;
+                }
+                if( (s->xrepeat|s->yrepeat) == 0 )
+                {
+                    s->xrepeat=t[0];
+                    s->yrepeat=t[1];
+                }
+                if( (TRAND&8) == 0 )
+                {
+                    t[0]=s->xrepeat;
+                    t[1]=s->yrepeat;
+                    t[2] = global_random&4;
+                    s->xrepeat=s->yrepeat=0;
+                    goto CLEAR_THE_BOLT2;
+                }
+                s->picnum++;
+
+                if(l&1) s->cstat ^= 2; // l not defined here. Line is met in 2nd demo with l = 0.
+
+                if( (TRAND&1) && sector[sect].floorpicnum == HURTRAIL )
+                    spritesound(SHORT_CIRCUIT,i);
+
+                if(s->picnum == SIDEBOLT1+4) s->picnum = SIDEBOLT1;
+
+                goto BOLT;
+
+            case BOLT1:
+            case BOLT1+1:
+            case BOLT1+2:
+            case BOLT1+3:
+                p = findplayer(s, &x);
+                if( x > 20480 ) goto BOLT;
+
+                if( t[3] == 0 )
+                    t[3]=sector[sect].floorshade;
+
+                CLEAR_THE_BOLT:
+                if(t[2])
+                {
+                    t[2]--;
+                    sector[sect].floorshade = 20;
+                    sector[sect].ceilingshade = 20;
+                    goto BOLT;
+                }
+                if( (s->xrepeat|s->yrepeat) == 0 )
+                {
+                    s->xrepeat=t[0];
+                    s->yrepeat=t[1];
+                }
+                else if( (TRAND&8) == 0 )
+                {
+                    t[0]=s->xrepeat;
+                    t[1]=s->yrepeat;
+                    t[2] = global_random&4;
+                    s->xrepeat=s->yrepeat=0;
+                    goto CLEAR_THE_BOLT;
+                }
+                s->picnum++;
+
+                l = global_random&7;
+                s->xrepeat=l+8;
+
+                if(l&1) s->cstat ^= 2;
+
+                if( s->picnum == (BOLT1+1) && (TRAND&7) == 0 && sector[sect].floorpicnum == HURTRAIL )
+                    spritesound(SHORT_CIRCUIT,i);
+
+                if(s->picnum==BOLT1+4) s->picnum=BOLT1;
+
+                if(s->picnum&1)
+                {
+                    sector[sect].floorshade = 0;
+                    sector[sect].ceilingshade = 0;
+                }
+                else
+                {
+                    sector[sect].floorshade = 20;
+                    sector[sect].ceilingshade = 20;
+                }
+                goto BOLT;
+                
+            case WATERDRIP:
+
+                if( t[1] )
+                {
+                    t[1]--;
+                    if(t[1] == 0)
+                        s->cstat &= 32767;
+                }
+                else
+                {
+                    makeitfall(i);
+                    ssp(i,CLIPMASK0);
+                    if(s->xvel > 0) s->xvel -= 2;
+
+                    if(s->zvel == 0)
+                    {
+                        s->cstat |= 32768;
+
+                        if(s->pal != 2 && s->hitag == 0)
+                            spritesound(SOMETHING_DRIPPING,i);
+
+                        if(sprite[s->owner].picnum != WATERDRIP)
+                        {
+                            KILLIT(i);
+                        }
+                        else
+                        {
+                            hittype[i].bposz = s->z = t[0];
+                            t[1] = 48+(TRAND&31);
+                        }
+                    }
+                }
+
+
+                goto BOLT;
+
+            case DOORSHOCK:
+                j = klabs(sector[sect].ceilingz-sector[sect].floorz)>>9;
+                s->yrepeat = j+4;
+                s->xrepeat = 16;
+                s->z = sector[sect].floorz;
+                goto BOLT;
+
+            case TOUCHPLATE:
+                if( t[1] == 1 && s->hitag >= 0) //Move the sector floor
+                {
+                    x = sector[sect].floorz;
+
+                    if(t[3] == 1)
+                    {
+                        if(x >= t[2])
+                        {
+                            sector[sect].floorz = x;
+                            t[1] = 0;
+                        }
+                        else
+                        {
+                            sector[sect].floorz += sector[sect].extra;
+                            p = checkcursectnums(sect);
+                            if(p >= 0) ps[p].posz += sector[sect].extra;
+                        }
+                    }
+                    else
+                    {
+                        if(x <= s->z)
+                        {
+                            sector[sect].floorz = s->z;
+                            t[1] = 0;
+                        }
+                        else
+                        {
+                            sector[sect].floorz -= sector[sect].extra;
+                            p = checkcursectnums(sect);
+                            if(p >= 0)
+                                ps[p].posz -= sector[sect].extra;
+                        }
+                    }
+                    goto BOLT;
+                }
+
+                if(t[5] == 1) goto BOLT;
+
+                p = checkcursectnums(sect);
+                if( p >= 0 && ( ps[p].on_ground || s->ang == 512) )
+                {
+                    if( t[0] == 0 && !check_activator_motion(s->lotag) )
+                    {
+                        t[0] = 1;
+                        t[1] = 1;
+                        t[3] = !t[3];
+                        operatemasterswitches(s->lotag);
+                        operateactivators(s->lotag,p);
+                        if(s->hitag > 0)
+                        {
+                            s->hitag--;
+                            if(s->hitag == 0) t[5] = 1;
+                        }
+                    }
+                }
+                else t[0] = 0;
+
+                if(t[1] == 1)
+                {
+                    j = headspritestat[6];
+                    while(j >= 0)
+                    {
+                        if(j != i && sprite[j].picnum == TOUCHPLATE && sprite[j].lotag == s->lotag)
+                        {
+                            hittype[j].temp_data[1] = 1;
+                            hittype[j].temp_data[3] = t[3];
+                        }
+                        j = nextspritestat[j];
+                    }
+                }
+                goto BOLT;
+
+            case CANWITHSOMETHING:
+            case CANWITHSOMETHING2:
+            case CANWITHSOMETHING3:
+            case CANWITHSOMETHING4:
+                makeitfall(i);
+                IFHIT
+                {
+                    spritesound(VENT_BUST,i);
+                    for(j=0;j<10;j++)
+                        RANDOMSCRAP;
+
+                    if(s->lotag) spawn(i,s->lotag);
+
+                    KILLIT(i);
+                }
+                goto BOLT;
+
+            case EXPLODINGBARREL:
+            case WOODENHORSE:
+            case HORSEONSIDE:
+            case FLOORFLAME:
+            case FIREBARREL:
+            case FIREVASE:
+            case NUKEBARREL:
+            case NUKEBARRELDENTED:
+            case NUKEBARRELLEAKED:
+            case TOILETWATER:
+            case RUBBERCAN:
+            case STEAM:
+            case CEILINGSTEAM:
+                p = findplayer(s, &x);
+                execute(i,p,x);
+                goto BOLT;
+            case WATERBUBBLEMAKER:
+                p = findplayer(s, &x);
+                execute(i,p,x);
+                goto BOLT;
+        }
+
+        BOLT:
+        i = nexti;
+    }
+}
+
+void bounce(short i)
+{
+    long k, l, daang, dax, day, daz, xvect, yvect, zvect;
+    short hitsect;
+    spritetype *s = &sprite[i];
+
+    xvect = mulscale10(s->xvel,sintable[(s->ang+512)&2047]);
+    yvect = mulscale10(s->xvel,sintable[s->ang&2047]);
+    zvect = s->zvel;
+
+    hitsect = s->sectnum;
+
+    k = sector[hitsect].wallptr; l = wall[k].point2;
+    daang = getangle(wall[l].x-wall[k].x,wall[l].y-wall[k].y);
+
+    if ( s->z < (hittype[i].floorz+hittype[i].ceilingz)>>1)
+        k = sector[hitsect].ceilingheinum;
+    else
+        k = sector[hitsect].floorheinum;
+
+    dax = mulscale14(k,sintable[(daang)&2047]);
+    day = mulscale14(k,sintable[(daang+1536)&2047]);
+    daz = 4096;
+
+    k = xvect*dax+yvect*day+zvect*daz;
+    l = dax*dax+day*day+daz*daz;
+    if ((klabs(k)>>14) < l)
+    {
+        k = divscale17(k,l);
+        xvect -= mulscale16(dax,k);
+        yvect -= mulscale16(day,k);
+        zvect -= mulscale16(daz,k);
+    }
+
+    s->zvel = zvect;
+    s->xvel = ksqrt(dmulscale8(xvect,xvect,yvect,yvect));
+    s->ang = getangle(xvect,yvect);
+}
+     
+void moveweapons(void)
+{
+    short i, j, k, nexti, p, q, tempsect;
+    long dax,day,daz, x, l, ll, x1, y1;
+    unsigned long qq;
+    spritetype *s;
+
+    i = headspritestat[4];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+        s = &sprite[i];
+
+        if(s->sectnum < 0) KILLIT(i);
+
+        hittype[i].bposx = s->x;
+        hittype[i].bposy = s->y;
+        hittype[i].bposz = s->z;
+
+        switch(s->picnum)
+        {
+            case RADIUSEXPLOSION:
+            case KNEE:
+                KILLIT(i);
+            case TONGUE:
+                T1 = sintable[(T2)&2047]>>9;
+                T2 += 32;
+                if(T2 > 2047) KILLIT(i);
+
+                if(sprite[s->owner].statnum == MAXSTATUS)
+                    if(badguy(&sprite[s->owner]) == 0)
+                        KILLIT(i);
+
+                s->ang = sprite[s->owner].ang;
+                s->x = sprite[s->owner].x;
+                s->y = sprite[s->owner].y;
+                if(sprite[s->owner].picnum == APLAYER)
+                    s->z = sprite[s->owner].z-(34<<8);
+                for(k=0;k<T1;k++)
+                {
+                    q = EGS(s->sectnum,
+                        s->x+((k*sintable[(s->ang+512)&2047])>>9),
+                        s->y+((k*sintable[s->ang&2047])>>9),
+                        s->z+((k*ksgn(s->zvel))*klabs(s->zvel/12)),TONGUE,-40+(k<<1),
+                        8,8,0,0,0,i,5);
+                    sprite[q].cstat = 128;
+                    sprite[q].pal = 8;
+                }
+                q = EGS(s->sectnum,
+                    s->x+((k*sintable[(s->ang+512)&2047])>>9),
+                    s->y+((k*sintable[s->ang&2047])>>9),
+                    s->z+((k*ksgn(s->zvel))*klabs(s->zvel/12)),INNERJAW,-40,
+                    32,32,0,0,0,i,5);
+                sprite[q].cstat = 128;
+                if( T2 > 512 && T2 < (1024) )
+                    sprite[q].picnum = INNERJAW+1;
+
+                goto BOLT;
+
+            case FREEZEBLAST:
+                if(s->yvel < 1 || s->extra < 2 || (s->xvel|s->zvel) == 0)
+                {
+                    j = spawn(i,TRANSPORTERSTAR);
+                    sprite[j].pal = 1;
+                    sprite[j].xrepeat = 32;
+                    sprite[j].yrepeat = 32;
+                    KILLIT(i);
+                }
+            case SHRINKSPARK:
+            case RPG:
+            case FIRELASER:
+            case SPIT:
+            case COOLEXPLOSION1:
+
+                if( s->picnum == COOLEXPLOSION1 )
+                    if( Sound[WIERDSHOT_FLY].num == 0 )
+                        spritesound(WIERDSHOT_FLY,i);
+
+                p = -1;
+
+                if(s->picnum == RPG && sector[s->sectnum].lotag == 2)
+                {
+                    k = s->xvel>>1;
+                    ll = s->zvel>>1;
+                }
+                else
+                {
+                    k = s->xvel;
+                    ll = s->zvel;
+                }
+
+                dax = s->x; day = s->y; daz = s->z;
+
+                getglobalz(i);
+                qq = CLIPMASK1;
+
+                switch(s->picnum)
+                {
+                    case RPG:
+                        if(hittype[i].picnum != BOSS2 && s->xrepeat >= 10 && sector[s->sectnum].lotag != 2)
+                        {
+                            j = spawn(i,SMALLSMOKE);
+                            sprite[j].z += (1<<8);
+                        }
+                        break;
+                }
+
+                j = movesprite(i,
+                    (k*(sintable[(s->ang+512)&2047]))>>14,
+                    (k*(sintable[s->ang&2047]))>>14,ll,qq);
+
+                if(s->picnum == RPG && s->yvel >= 0)
+                    if( FindDistance2D(s->x-sprite[s->yvel].x,s->y-sprite[s->yvel].y) < 256 )
+                        j = 49152|s->yvel;
+
+                if(s->sectnum < 0) { KILLIT(i); }
+
+                if( (j&49152) != 49152)
+                    if(s->picnum != FREEZEBLAST)
+                {
+                    if(s->z < hittype[i].ceilingz)
+                    {
+                        j = 16384|(s->sectnum);
+                        s->zvel = -1;
+                    }
+                    else
+                        if( ( s->z > hittype[i].floorz && sector[s->sectnum].lotag != 1 ) ||
+                            ( s->z > hittype[i].floorz+(16<<8) && sector[s->sectnum].lotag == 1 ) )
+                    {
+                        j = 16384|(s->sectnum);
+                        if(sector[s->sectnum].lotag != 1)
+                            s->zvel = 1;
+                    }
+                }
+
+                if(s->picnum == FIRELASER)
+                {
+                    for(k=-3;k<2;k++)
+                    {
+                        x = EGS(s->sectnum,
+                            s->x+((k*sintable[(s->ang+512)&2047])>>9),
+                            s->y+((k*sintable[s->ang&2047])>>9),
+                            s->z+((k*ksgn(s->zvel))*klabs(s->zvel/24)),FIRELASER,-40+(k<<2),
+                            s->xrepeat,s->yrepeat,0,0,0,s->owner,5);
+
+                        sprite[x].cstat = 128;
+                        sprite[x].pal = s->pal;
+                    }
+                }
+                else if(s->picnum == SPIT) if(s->zvel < 6144)
+                    s->zvel += gc-112;
+
+                if( j != 0 )
+                {
+                    if(s->picnum == COOLEXPLOSION1)
+                    {
+                        if( (j&49152) == 49152 && sprite[j&(MAXSPRITES-1)].picnum != APLAYER)
+                            goto BOLT;
+                        s->xvel = 0;
+                        s->zvel = 0;
+                    }
+
+                    if( (j&49152) == 49152 )
+                    {
+                        j &= (MAXSPRITES-1);
+
+                        if(s->picnum == FREEZEBLAST && sprite[j].pal == 1 )
+                            if( badguy(&sprite[j]) || sprite[j].picnum == APLAYER )
+                        {
+                            j = spawn(i,TRANSPORTERSTAR);
+                            sprite[j].pal = 1;
+                            sprite[j].xrepeat = 32;
+                            sprite[j].yrepeat = 32;
+
+                            KILLIT(i);
+                        }
+
+                        checkhitsprite(j,i);
+
+                        if(sprite[j].picnum == APLAYER)
+                        {
+                            p = sprite[j].yvel;
+                            spritesound(PISTOL_BODYHIT,j);
+
+                            if(s->picnum == SPIT)
+                            {
+                                ps[p].horiz += 32;
+                                ps[p].return_to_center = 8;
+
+                                if(ps[p].loogcnt == 0)
+                                {
+                                    if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                        spritesound(DUKE_LONGTERM_PAIN,ps[p].i);
+
+                                    j = 3+(TRAND&3);
+                                    ps[p].numloogs = j;
+                                    ps[p].loogcnt = 24*4;
+                                    for(x=0;x < j;x++)
+                                    {
+                                        ps[p].loogiex[x] = TRAND%xdim;
+                                        ps[p].loogiey[x] = TRAND%ydim;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    else if( (j&49152) == 32768 )
+                    {
+                        j &= (MAXWALLS-1);
+
+                        if(s->picnum != RPG && s->picnum != FREEZEBLAST && s->picnum != SPIT && ( wall[j].overpicnum == MIRROR || wall[j].picnum == MIRROR ) )
+                        {
+                            k = getangle(
+                                    wall[wall[j].point2].x-wall[j].x,
+                                    wall[wall[j].point2].y-wall[j].y);
+                            s->ang = ((k<<1) - s->ang)&2047;
+                            s->owner = i;
+                            spawn(i,TRANSPORTERSTAR);
+                            goto BOLT;
+                        }
+                        else
+                        {
+                            setsprite(i,dax,day,daz);
+                            checkhitwall(i,j,s->x,s->y,s->z,s->picnum);
+
+                            if(s->picnum == FREEZEBLAST)
+                            {
+                                if( wall[j].overpicnum != MIRROR && wall[j].picnum != MIRROR )
+                                {
+                                    s->extra >>= 1;
+                                    s->yvel--;
+                                }
+
+                                k = getangle(
+                                    wall[wall[j].point2].x-wall[j].x,
+                                    wall[wall[j].point2].y-wall[j].y);
+                                s->ang = ((k<<1) - s->ang)&2047;
+                                goto BOLT;
+                            }
+                        }
+                    }
+                    else if( (j&49152) == 16384)
+                    {
+                        setsprite(i,dax,day,daz);
+
+                        if(s->zvel < 0)
+                        {
+                            if( sector[s->sectnum].ceilingstat&1 )
+                                if(sector[s->sectnum].ceilingpal == 0)
+                                    KILLIT(i);
+
+                            checkhitceiling(s->sectnum);
+                        }
+
+                        if(s->picnum == FREEZEBLAST)
+                        {
+                            bounce(i);
+                            ssp(i,qq);
+                            s->extra >>= 1;
+                            if(s->xrepeat > 8)
+                                s->xrepeat -= 2;
+                            if(s->yrepeat > 8)
+                                s->yrepeat -= 2;
+                            s->yvel--;
+                            goto BOLT;
+                        }
+                    }
+
+                    if(s->picnum != SPIT)
+                    {
+                        if(s->picnum == RPG)
+                        {
+                            k = spawn(i,EXPLOSION2);
+                            sprite[k].x = dax;
+                            sprite[k].y = day;
+                            sprite[k].z = daz;
+
+                            if(s->xrepeat < 10)
+                            {
+                                sprite[k].xrepeat = 6;
+                                sprite[k].yrepeat = 6;
+                            }
+                            else if( (j&49152) == 16384)
+                            {
+                                if( s->zvel > 0)
+                                    spawn(i,EXPLOSION2BOT);
+                                else { sprite[k].cstat |= 8; sprite[k].z += (48<<8); }
+                            }
+                        }
+                        else if(s->picnum == SHRINKSPARK)
+                        {
+                            spawn(i,SHRINKEREXPLOSION);
+                            spritesound(SHRINKER_HIT,i);
+                            hitradius(i,shrinkerblastradius,0,0,0,0);
+                        }
+                        else if( s->picnum != COOLEXPLOSION1 && s->picnum != FREEZEBLAST && s->picnum != FIRELASER)
+                        {
+                            k = spawn(i,EXPLOSION2);
+                            sprite[k].xrepeat = sprite[k].yrepeat = s->xrepeat>>1;
+                            if( (j&49152) == 16384)
+                            {
+                                if( s->zvel < 0)
+                                    { sprite[k].cstat |= 8; sprite[k].z += (72<<8); }
+                            }
+                        }
+                        if( s->picnum == RPG )
+                        {
+                            spritesound(RPG_EXPLODE,i);
+
+                            if(s->xrepeat >= 10)
+                            {
+                                x = s->extra;
+                                hitradius( i,rpgblastradius, x>>2,x>>1,x-(x>>2),x);
+                            }
+                            else
+                            {
+                                x = s->extra+(global_random&3);
+                                hitradius( i,(rpgblastradius>>1),x>>2,x>>1,x-(x>>2),x);
+                            }
+                        }
+                    }
+                    if(s->picnum != COOLEXPLOSION1) KILLIT(i);
+                }
+                if(s->picnum == COOLEXPLOSION1)
+                {
+                    s->shade++;
+                    if(s->shade >= 40) KILLIT(i);
+                }
+                else if(s->picnum == RPG && sector[s->sectnum].lotag == 2 && s->xrepeat >= 10 && rnd(140))
+                    spawn(i,WATERBUBBLE);
+
+                goto BOLT;
+
+
+            case SHOTSPARK1:
+                p = findplayer(s,&x);
+                execute(i,p,x);
+                goto BOLT;
+        }
+        BOLT:
+        i = nexti;
+    }
+}
+
+
+void movetransports(void)
+{
+    char warpspriteto;
+    short i, j, k, l, p, sect, sectlotag, nexti, nextj, nextk;
+    long ll,onfloorz,q;
+
+    i = headspritestat[9]; //Transporters
+
+    while(i >= 0)
+    {
+        sect = SECT;
+        sectlotag = sector[sect].lotag;
+
+        nexti = nextspritestat[i];
+
+        if(OW == i)
+        {
+            i = nexti;
+            continue;
+        }
+
+        onfloorz = T5;
+
+        if(T1 > 0) T1--;
+
+        j = headspritesect[sect];
+        while(j >= 0)
+        {
+            nextj = nextspritesect[j];
+
+            switch(sprite[j].statnum)
+            {
+                case 10:    // Player
+
+                    if( sprite[j].owner != -1 )
+                    {
+                        p = sprite[j].yvel;
+
+                        ps[p].on_warping_sector = 1;
+
+                        if( ps[p].transporter_hold == 0 && ps[p].jumping_counter == 0 )
+                        {
+                            if(ps[p].on_ground && sectlotag == 0 && onfloorz && ps[p].jetpack_on == 0 )
+                            {
+                                if(sprite[i].pal == 0)
+                                {
+                                    spawn(i,TRANSPORTERBEAM);
+                                    spritesound(TELEPORTER,i);
+                                }
+
+                                for(k=connecthead;k>=0;k=connectpoint2[k])
+                                    if(ps[k].cursectnum == sprite[OW].sectnum)
+                                {
+                                    ps[k].frag_ps = p;
+                                    sprite[ps[k].i].extra = 0;
+                                }
+
+                                ps[p].ang = sprite[OW].ang;
+
+                                if(sprite[OW].owner != OW)
+                                {
+                                    T1 = 13;
+                                    hittype[OW].temp_data[0] = 13;
+                                    ps[p].transporter_hold = 13;
+                                }
+
+                                ps[p].bobposx = ps[p].oposx = ps[p].posx = sprite[OW].x;
+                                ps[p].bobposy = ps[p].oposy = ps[p].posy = sprite[OW].y;
+                                ps[p].oposz = ps[p].posz = sprite[OW].z-PHEIGHT;
+
+                                changespritesect(j,sprite[OW].sectnum);
+                                ps[p].cursectnum = sprite[j].sectnum;
+
+                                if(sprite[i].pal == 0)
+                                {
+                                    k = spawn(OW,TRANSPORTERBEAM);
+                                    spritesound(TELEPORTER,k);
+                                }
+
+                                break;
+                            }
+                        }
+                        else if( !(sectlotag == 1 && ps[p].on_ground == 1)  ) break;
+
+                        if(onfloorz == 0 && klabs(SZ-ps[p].posz) < 6144 )
+                            if( (ps[p].jetpack_on == 0 ) || (ps[p].jetpack_on && (sync[p].bits&1) ) ||
+                                (ps[p].jetpack_on && (sync[p].bits&2) ) )
+                        {
+                            ps[p].oposx = ps[p].posx += sprite[OW].x-SX;
+                            ps[p].oposy = ps[p].posy += sprite[OW].y-SY;
+
+                            if( ps[p].jetpack_on && ( (sync[p].bits&1) || ps[p].jetpack_on < 11 ) )
+                                ps[p].posz = sprite[OW].z-6144;
+                            else ps[p].posz = sprite[OW].z+6144;
+                            ps[p].oposz = ps[p].posz;
+
+                            hittype[ps[p].i].bposx = ps[p].posx;
+                            hittype[ps[p].i].bposy = ps[p].posy;
+                            hittype[ps[p].i].bposz = ps[p].posz;
+
+                            changespritesect(j,sprite[OW].sectnum);
+                            ps[p].cursectnum = sprite[OW].sectnum;
+
+                            break;
+                        }
+
+                        k = 0;
+
+                        if( onfloorz && sectlotag == 1 && ps[p].on_ground && ps[p].posz > (sector[sect].floorz-(16<<8)) && ( (sync[p].bits&2) || ps[p].poszv > 2048 ) )
+//                        if( onfloorz && sectlotag == 1 && ps[p].posz > (sector[sect].floorz-(6<<8)) )
+                        {
+                            k = 1;
+                            if(screenpeek == p)
+                            {
+                                FX_StopAllSounds();
+                                clearsoundlocks();
+                            }
+                            if(sprite[ps[p].i].extra > 0)
+                                spritesound(DUKE_UNDERWATER,j);
+                            ps[p].oposz = ps[p].posz =
+                                sector[sprite[OW].sectnum].ceilingz+(7<<8);
+
+                            ps[p].posxv = 4096-(TRAND&8192);
+                            ps[p].posyv = 4096-(TRAND&8192);
+
+                        }
+
+                        if( onfloorz && sectlotag == 2 && ps[p].posz < (sector[sect].ceilingz+(6<<8)) )
+                        {
+                            k = 1;
+//                            if( sprite[j].extra <= 0) break;
+                            if(screenpeek == p)
+                            {
+                                FX_StopAllSounds();
+                                clearsoundlocks();
+                            }
+                            spritesound(DUKE_GASP,j);
+
+                            ps[p].oposz = ps[p].posz =
+                                sector[sprite[OW].sectnum].floorz-(7<<8);
+
+                            ps[p].jumping_toggle = 1;
+                            ps[p].jumping_counter = 0;
+                        }
+
+                        if(k == 1)
+                        {
+                            ps[p].oposx = ps[p].posx += sprite[OW].x-SX;
+                            ps[p].oposy = ps[p].posy += sprite[OW].y-SY;
+
+                            if(sprite[OW].owner != OW)
+                                ps[p].transporter_hold = -2;
+                            ps[p].cursectnum = sprite[OW].sectnum;
+
+                            changespritesect(j,sprite[OW].sectnum);
+                            setsprite(ps[p].i,ps[p].posx,ps[p].posy,ps[p].posz+PHEIGHT);
+
+                            setpal(&ps[p]);
+
+                            if( (TRAND&255) < 32 )
+                                spawn(j,WATERSPLASH2);
+
+                            if(sectlotag == 1)
+                                for(l = 0;l < 9;l++)
+                            {
+                                q = spawn(ps[p].i,WATERBUBBLE);
+                                sprite[q].z += TRAND&16383;
+                            }
+                        }
+                    }
+                    break;
+
+                case 1:
+                    switch(sprite[j].picnum)
+                    {
+                        case SHARK:
+                        case COMMANDER:
+                        case OCTABRAIN:
+                        case GREENSLIME:
+                        case GREENSLIME+1:
+                        case GREENSLIME+2:
+                        case GREENSLIME+3:
+                        case GREENSLIME+4:
+                        case GREENSLIME+5:
+                        case GREENSLIME+6:
+                        case GREENSLIME+7:
+                            if(sprite[j].extra > 0)
+                                goto JBOLT;
+                    }
+                case 4:
+                case 5:
+                case 12:
+                case 13:
+
+                    ll = klabs(sprite[j].zvel);
+
+                    {
+                        warpspriteto = 0;
+                        if( ll && sectlotag == 2 && sprite[j].z < (sector[sect].ceilingz+ll) )
+                            warpspriteto = 1;
+
+                        if( ll && sectlotag == 1 && sprite[j].z > (sector[sect].floorz-ll) )
+                            warpspriteto = 1;
+
+                        if( sectlotag == 0 && ( onfloorz || klabs(sprite[j].z-SZ) < 4096) )
+                        {
+                            if( sprite[OW].owner != OW && onfloorz && T1 > 0 && sprite[j].statnum != 5 )
+                            {
+                                T1++;
+                                goto BOLT;
+                            }
+                            warpspriteto = 1;
+                        }
+
+                        if( warpspriteto ) switch(sprite[j].picnum)
+                        {
+                            case TRANSPORTERSTAR:
+                            case TRANSPORTERBEAM:
+                            case TRIPBOMB:
+                            case BULLETHOLE:
+                            case WATERSPLASH2:
+                            case BURNING:
+                            case BURNING2:
+                            case FIRE:
+                            case FIRE2:
+                            case TOILETWATER:
+                            case LASERLINE:
+                                goto JBOLT;
+                            case PLAYERONWATER:
+                                if(sectlotag == 2)
+                                {
+                                    sprite[j].cstat &= 32767;
+                                    break;
+                                }
+                            default:
+                                if(sprite[j].statnum == 5 && !(sectlotag == 1 || sectlotag == 2) )
+                                    break;
+
+                            case WATERBUBBLE:
+//                                if( rnd(192) && sprite[j].picnum == WATERBUBBLE)
+  //                                 break;
+
+                                if(sectlotag > 0)
+                                {
+                                    k = spawn(j,WATERSPLASH2);
+                                    if( sectlotag == 1 && sprite[j].statnum == 4 )
+                                    {
+                                        sprite[k].xvel = sprite[j].xvel>>1;
+                                        sprite[k].ang = sprite[j].ang;
+                                        ssp(k,CLIPMASK0);
+                                    }
+                                }
+
+                                switch(sectlotag)
+                                {
+                                    case 0:
+                                        if(onfloorz)
+                                        {
+                                            if( sprite[j].statnum == 4 || ( checkcursectnums(sect) == -1 && checkcursectnums(sprite[OW].sectnum)  == -1 ) )
+                                            {
+                                                sprite[j].x += (sprite[OW].x-SX);
+                                                sprite[j].y += (sprite[OW].y-SY);
+                                                sprite[j].z -= SZ - sector[sprite[OW].sectnum].floorz;
+                                                sprite[j].ang = sprite[OW].ang;
+
+                                                hittype[j].bposx = sprite[j].x;
+                                                hittype[j].bposy = sprite[j].y;
+                                                hittype[j].bposz = sprite[j].z;
+
+                                                if(sprite[i].pal == 0)
+                                                {
+                                                    k = spawn(i,TRANSPORTERBEAM);
+                                                    spritesound(TELEPORTER,k);
+
+                                                    k = spawn(OW,TRANSPORTERBEAM);
+                                                    spritesound(TELEPORTER,k);
+                                                }
+
+                                                if( sprite[OW].owner != OW )
+                                                {
+                                                    T1 = 13;
+                                                    hittype[OW].temp_data[0] = 13;
+                                                }
+
+                                                changespritesect(j,sprite[OW].sectnum);
+                                            }
+                                        }
+                                        else
+                                        {
+                                            sprite[j].x += (sprite[OW].x-SX);
+                                            sprite[j].y += (sprite[OW].y-SY);
+                                            sprite[j].z = sprite[OW].z+4096;
+
+                                            hittype[j].bposx = sprite[j].x;
+                                            hittype[j].bposy = sprite[j].y;
+                                            hittype[j].bposz = sprite[j].z;
+
+                                            changespritesect(j,sprite[OW].sectnum);
+                                        }
+                                        break;
+                                    case 1:
+                                        sprite[j].x += (sprite[OW].x-SX);
+                                        sprite[j].y += (sprite[OW].y-SY);
+                                        sprite[j].z = sector[sprite[OW].sectnum].ceilingz+ll;
+
+                                        hittype[j].bposx = sprite[j].x;
+                                        hittype[j].bposy = sprite[j].y;
+                                        hittype[j].bposz = sprite[j].z;
+
+                                        changespritesect(j,sprite[OW].sectnum);
+
+                                        break;
+                                    case 2:
+                                        sprite[j].x += (sprite[OW].x-SX);
+                                        sprite[j].y += (sprite[OW].y-SY);
+                                        sprite[j].z = sector[sprite[OW].sectnum].floorz-ll;
+
+                                        hittype[j].bposx = sprite[j].x;
+                                        hittype[j].bposy = sprite[j].y;
+                                        hittype[j].bposz = sprite[j].z;
+
+                                        changespritesect(j,sprite[OW].sectnum);
+
+                                        break;
+                                }
+
+                                break;
+                        }
+                }
+                break;
+
+            }
+            JBOLT:
+            j = nextj;
+        }
+        BOLT:
+        i = nexti;
+    }
+}
+
+
+
+void moveactors(void)
+{
+    long x, m, l, *t;
+    short a, i, j, nexti, nextj, sect, p;
+    spritetype *s;
+    unsigned short k;
+
+    i = headspritestat[1];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        s = &sprite[i];
+
+        sect = s->sectnum;
+
+        if( s->xrepeat == 0 || sect < 0 || sect >= MAXSECTORS)
+            KILLIT(i);
+
+        t = &hittype[i].temp_data[0];
+
+        hittype[i].bposx = s->x;
+        hittype[i].bposy = s->y;
+        hittype[i].bposz = s->z;
+
+        switch(s->picnum)
+        {
+            case DUCK:
+            case TARGET:
+                if(s->cstat&32)
+                {
+                    t[0]++;
+                    if(t[0] > 60)
+                    {
+                        t[0] = 0;
+                        s->cstat = 128+257+16;
+                        s->extra = 1;
+                    }
+                }
+                else
+                {
+                    j = ifhitbyweapon(i);
+                    if( j >= 0 )
+                    {
+                        s->cstat = 32+128;
+                        k = 1;
+
+                        j = headspritestat[1];
+                        while(j >= 0)
+                        {
+                            if( sprite[j].lotag == s->lotag &&
+                                sprite[j].picnum == s->picnum )
+                            {
+                                if( ( sprite[j].hitag && !(sprite[j].cstat&32) ) ||
+                                    ( !sprite[j].hitag && (sprite[j].cstat&32) )
+                                  )
+                                {
+                                    k = 0;
+                                    break;
+                                }
+                            }
+
+                            j = nextspritestat[j];
+                        }
+
+                        if(k == 1)
+                        {
+                            operateactivators(s->lotag,-1);
+                            operateforcefields(i,s->lotag);
+                            operatemasterswitches(s->lotag);
+                        }
+                    }
+                }
+                goto BOLT;
+
+            case RESPAWNMARKERRED:
+            case RESPAWNMARKERYELLOW:
+            case RESPAWNMARKERGREEN:
+                T1++;
+                if(T1 > respawnitemtime)
+                {
+                    KILLIT(i);
+                }
+                if( T1 >= (respawnitemtime>>1) && T1 < ((respawnitemtime>>1)+(respawnitemtime>>2)) )
+                    PN = RESPAWNMARKERYELLOW;
+                else if( T1 > ((respawnitemtime>>1)+(respawnitemtime>>2)) )
+                    PN = RESPAWNMARKERGREEN;
+                makeitfall(i);
+                break;
+
+            case HELECOPT:
+            case DUKECAR:
+
+                s->z += s->zvel;
+                t[0]++;
+
+                if(t[0] == 4) spritesound(WAR_AMBIENCE2,i);
+
+                if( t[0] > (26*8) )
+                {
+                    sound(RPG_EXPLODE);
+                    for(j=0;j<32;j++) RANDOMSCRAP;
+                    earthquaketime = 16;
+                    KILLIT(i);
+                }
+                else if((t[0]&3) == 0)
+                    spawn(i,EXPLOSION2);
+                ssp(i,CLIPMASK0);
+                break;
+            case RAT:
+                makeitfall(i);
+                IFMOVING
+                {
+                    if( (TRAND&255) < 3 ) spritesound(RATTY,i);
+                    s->ang += (TRAND&31)-15+(sintable[(t[0]<<8)&2047]>>11);
+                }
+                else
+                {
+                    T1++;
+                    if(T1 > 1) { KILLIT(i); }
+                    else s->ang = (TRAND&2047);
+                }
+                if(s->xvel < 128)
+                    s->xvel+=2;
+                s->ang += (TRAND&3)-6;
+                break;
+            case QUEBALL:
+            case STRIPEBALL:
+                if(s->xvel)
+                {
+                    j = headspritestat[0];
+                    while(j >= 0)
+                    {
+                        nextj = nextspritestat[j];
+                        if( sprite[j].picnum == POCKET && ldist(&sprite[j],s) < 52 ) KILLIT(i);
+                        j = nextj;
+                    }
+
+                    j = clipmove(&s->x,&s->y,&s->z,&s->sectnum,
+                        (((s->xvel*(sintable[(s->ang+512)&2047]))>>14)*TICSPERFRAME)<<11,
+                        (((s->xvel*(sintable[s->ang&2047]))>>14)*TICSPERFRAME)<<11,
+                        24L,(4<<8),(4<<8),CLIPMASK1);
+
+                    if(j&49152)
+                    {
+                        if( (j&49152) == 32768 )
+                        {
+                            j &= (MAXWALLS-1);
+                            k = getangle(
+                                wall[wall[j].point2].x-wall[j].x,
+                                wall[wall[j].point2].y-wall[j].y);
+                            s->ang = ((k<<1) - s->ang)&2047;
+                        }
+                        else if( (j&49152) == 49152 )
+                        {
+                            j &= (MAXSPRITES-1);
+                            checkhitsprite(i,j);
+                        }
+                    }
+                    s->xvel --;
+                    if(s->xvel < 0) s->xvel = 0;
+                    if( s->picnum == STRIPEBALL )
+                    {
+                        s->cstat = 257;
+                        s->cstat |= 4&s->xvel;
+                        s->cstat |= 8&s->xvel;
+                    }
+                }
+                else
+                {
+                    p = findplayer(s,&x);
+
+                    if( x < 1596)
+                    {
+
+//                        if(s->pal == 12)
+                        {
+                            j = getincangle(ps[p].ang,getangle(s->x-ps[p].posx,s->y-ps[p].posy));
+                            if( j > -64 && j < 64 && (sync[p].bits&(1<<29)) )
+                                if(ps[p].toggle_key_flag == 1)
+                            {
+                                a = headspritestat[1];
+                                while(a >= 0)
+                                {
+                                    if(sprite[a].picnum == QUEBALL || sprite[a].picnum == STRIPEBALL)
+                                    {
+                                        j = getincangle(ps[p].ang,getangle(sprite[a].x-ps[p].posx,sprite[a].y-ps[p].posy));
+                                        if( j > -64 && j < 64 )
+                                        {
+                                            findplayer(&sprite[a],&l);
+                                            if(x > l) break;
+                                        }
+                                    }
+                                    a = nextspritestat[a];
+                                }
+                                if(a == -1)
+                                {
+                                    if(s->pal == 12)
+                                        s->xvel = 164;
+                                    else s->xvel = 140;
+                                    s->ang = ps[p].ang;
+                                    ps[p].toggle_key_flag = 2;
+                                }
+                            }
+                        }
+                    }
+                    if( x < 512 && s->sectnum == ps[p].cursectnum )
+                    {
+                        s->ang = getangle(s->x-ps[p].posx,s->y-ps[p].posy);
+                        s->xvel = 48;
+                    }
+                }
+
+                break;
+            case FORCESPHERE:
+
+                if(s->yvel == 0)
+                {
+                    s->yvel = 1;
+
+                    for(l=512;l<(2048-512);l+= 128)
+                        for(j=0;j<2048;j += 128)
+                    {
+                        k = spawn(i,FORCESPHERE);
+                        sprite[k].cstat = 257+128;
+                        sprite[k].clipdist = 64;
+                        sprite[k].ang = j;
+                        sprite[k].zvel = sintable[l&2047]>>5;
+                        sprite[k].xvel = sintable[(l+512)&2047]>>9;
+                        sprite[k].owner = i;
+                    }
+                }
+
+                if(t[3] > 0)
+                {
+                    if(s->zvel < 6144)
+                        s->zvel += 192;
+                    s->z += s->zvel;
+                    if(s->z > sector[sect].floorz)
+                        s->z = sector[sect].floorz;
+                    t[3]--;
+                    if(t[3] == 0)
+                        KILLIT(i);
+                }
+                else if(t[2] > 10)
+                {
+                    j = headspritestat[5];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].owner == i && sprite[j].picnum == FORCESPHERE)
+                            hittype[j].temp_data[1] = 1+(TRAND&63);
+                        j = nextspritestat[j];
+                    }
+                    t[3] = 64;
+                }
+
+                goto BOLT;
+
+            case RECON:
+
+                getglobalz(i);
+
+                if (sector[s->sectnum].ceilingstat&1)
+                   s->shade += (sector[s->sectnum].ceilingshade-s->shade)>>1;
+                else s->shade += (sector[s->sectnum].floorshade-s->shade)>>1;
+
+                if( s->z < sector[sect].ceilingz+(32<<8) )
+                    s->z = sector[sect].ceilingz+(32<<8);
+
+                if( ud.multimode < 2 )
+                {
+                    if( actor_tog == 1)
+                    {
+                        s->cstat = (short)32768;
+                        goto BOLT;
+                    }
+                    else if(actor_tog == 2) s->cstat = 257;
+                }
+                IFHIT
+                {
+                    if( s->extra < 0 && t[0] != -1 )
+                    {
+                        t[0] = -1;
+                        s->extra = 0;
+                    }
+                    spritesound(RECO_PAIN,i);
+                    RANDOMSCRAP;
+                }
+
+                if(t[0] == -1)
+                {
+                    s->z += 1024;
+                    t[2]++;
+                    if( (t[2]&3) == 0) spawn(i,EXPLOSION2);
+                    getglobalz(i);
+                    s->ang += 96;
+                    s->xvel = 128;
+                    j = ssp(i,CLIPMASK0);
+                    if(j != 1 || s->z > hittype[i].floorz)
+                    {
+                        for(l=0;l<16;l++)
+                            RANDOMSCRAP;
+                        spritesound(LASERTRIP_EXPLODE,i);
+                        spawn(i,PIGCOP);
+                        ps[myconnectindex].actors_killed++;
+                        KILLIT(i);
+                    }
+                    goto BOLT;
+                }
+                else
+                {
+                    if( s->z > hittype[i].floorz-(48<<8) )
+                        s->z = hittype[i].floorz-(48<<8);
+                }
+
+                p = findplayer(s,&x);
+                j = s->owner;
+
+                // 3 = findplayerz, 4 = shoot
+
+                if( t[0] >= 4 )
+                {
+                    t[2]++;
+                    if( (t[2]&15) == 0 )
+                    {
+                        a = s->ang;
+                        s->ang = hittype[i].tempang;
+                        spritesound(RECO_ATTACK,i);
+                        shoot(i,FIRELASER);
+                        s->ang = a;
+                    }
+                    if( t[2] > (26*3) || !cansee(s->x,s->y,s->z-(16<<8),s->sectnum, ps[p].posx,ps[p].posy,ps[p].posz,ps[p].cursectnum ) )
+                    {
+                        t[0] = 0;
+                        t[2] = 0;
+                    }
+                    else hittype[i].tempang +=
+                        getincangle(hittype[i].tempang,getangle(ps[p].posx-s->x,ps[p].posy-s->y))/3;
+                }
+                else if(t[0] == 2 || t[0] == 3)
+                {
+                    t[3] = 0;
+                    if(s->xvel > 0) s->xvel -= 16;
+                    else s->xvel = 0;
+
+                    if(t[0] == 2)
+                    {
+                        l = ps[p].posz-s->z;
+                        if( klabs(l) < (48<<8) ) t[0] = 3;
+                        else s->z += sgn(ps[p].posz-s->z)<<10;
+                    }
+                    else
+                    {
+                        t[2]++;
+                        if( t[2] > (26*3) || !cansee(s->x,s->y,s->z-(16<<8),s->sectnum, ps[p].posx,ps[p].posy,ps[p].posz,ps[p].cursectnum ) )
+                        {
+                            t[0] = 1;
+                            t[2] = 0;
+                        }
+                        else if( (t[2]&15) == 0 )
+                        {
+                            spritesound(RECO_ATTACK,i);
+                            shoot(i,FIRELASER);
+                        }
+                    }
+                    s->ang += getincangle(s->ang,getangle(ps[p].posx-s->x,ps[p].posy-s->y))>>2;
+                }
+
+                if( t[0] != 2 && t[0] != 3 )
+                {
+                    l = ldist(&sprite[j],s);
+                    if(l <= 1524)
+                    {
+                        a = s->ang;
+                        s->xvel >>= 1;
+                    }
+                    else a = getangle(sprite[j].x-s->x,sprite[j].y-s->y);
+
+                    if(t[0] == 1 || t[0] == 4) // Found a locator and going with it
+                    {
+                        l = dist(&sprite[j],s);
+
+                        if( l <= 1524 ) { if(t[0] == 1) t[0] = 0; else t[0] = 5; }
+                        else
+                        {
+                            // Control speed here
+                            if(l > 1524) { if( s->xvel < 256 ) s->xvel += 32; }
+                            else
+                            {
+                                if(s->xvel > 0) s->xvel -= 16;
+                                else s->xvel = 0;
+                            }
+                        }
+
+                        if(t[0] < 2) t[2]++;
+
+                        if( x < 6144 && t[0] < 2 && t[2] > (26*4) )
+                        {
+                            t[0] = 2+(TRAND&2);
+                            t[2] = 0;
+                            hittype[i].tempang = s->ang;
+                        }
+                    }
+
+                    if(t[0] == 0 || t[0] == 5)
+                    {
+                        if(t[0] == 0)
+                            t[0] = 1;
+                        else t[0] = 4;
+                        j = s->owner = LocateTheLocator(s->hitag,-1);
+                        if(j == -1)
+                        {
+                            s->hitag = j = hittype[i].temp_data[5];
+                            s->owner = LocateTheLocator(j,-1);
+                            j = s->owner;
+                            if(j == -1) KILLIT(i);
+                        }
+                        else s->hitag++;
+                    }
+
+                    t[3] = getincangle(s->ang,a);
+                    s->ang += t[3]>>3;
+
+                    if(s->z < sprite[j].z)
+                        s->z += 1024;
+                    else s->z -= 1024;
+                }
+
+                if(Sound[RECO_ROAM].num == 0 )
+                    spritesound(RECO_ROAM,i);
+
+                ssp(i,CLIPMASK0);
+
+                goto BOLT;
+
+            case OOZ:
+            case OOZ2:
+
+                getglobalz(i);
+
+                j = (hittype[i].floorz-hittype[i].ceilingz)>>9;
+                if(j > 255) j = 255;
+
+                x = 25-(j>>1);
+                if(x < 8) x = 8;
+                else if(x > 48) x = 48;
+
+                s->yrepeat = j;
+                s->xrepeat = x;
+                s->z = hittype[i].floorz;
+
+                goto BOLT;
+
+            case GREENSLIME:
+            case GREENSLIME+1:
+            case GREENSLIME+2:
+            case GREENSLIME+3:
+            case GREENSLIME+4:
+            case GREENSLIME+5:
+            case GREENSLIME+6:
+            case GREENSLIME+7:
+
+// #ifndef VOLUMEONE
+                if( ud.multimode < 2 )
+                {
+                    if( actor_tog == 1)
+                    {
+                        s->cstat = (short)32768;
+                        goto BOLT;
+                    }
+                    else if(actor_tog == 2) s->cstat = 257;
+                }
+// #endif
+
+                t[1]+=128;
+
+                if(sector[sect].floorstat&1)
+                    KILLIT(i);
+
+                p = findplayer(s,&x);
+
+                if(x > 20480)
+                {
+                    hittype[i].timetosleep++;
+                    if( hittype[i].timetosleep > SLEEPTIME )
+                    {
+                        hittype[i].timetosleep = 0;
+                        changespritestat(i,2);
+                        goto BOLT;
+                    }
+                }
+
+                if(t[0] == -5) // FROZEN
+                {
+                    t[3]++;
+                    if(t[3] > 280)
+                    {
+                        s->pal = 0;
+                        t[0] = 0;
+                        goto BOLT;
+                    }
+                    makeitfall(i);
+                    s->cstat = 257;
+                    s->picnum = GREENSLIME+2;
+                    s->extra = 1;
+                    s->pal = 1;
+                    IFHIT
+                    {
+                        if(j == FREEZEBLAST) goto BOLT;
+                        for(j=16; j >= 0 ;j--)
+                        {
+                            k = EGS(SECT,SX,SY,SZ,GLASSPIECES+(j%3),-32,36,36,TRAND&2047,32+(TRAND&63),1024-(TRAND&1023),i,5);
+                            sprite[k].pal = 1;
+                        }
+                        spritesound(GLASS_BREAKING,i);
+                        KILLIT(i);
+                    }
+                    else if(x < 1024 && ps[p].quick_kick == 0)
+                    {
+                        j = getincangle(ps[p].ang,getangle(SX-ps[p].posx,SY-ps[p].posy));
+                        if( j > -128 && j < 128 )
+                            ps[p].quick_kick = 14;
+                    }
+
+                    goto BOLT;
+                }
+
+                if(x < 1596)
+                    s->cstat = 0;
+                else s->cstat = 257;
+
+                if(t[0] == -4) //On the player
+                {
+                    if( sprite[ps[p].i].extra < 1 )
+                    {
+                        t[0] = 0;
+                        goto BOLT;
+                    }
+
+                    setsprite(i,s->x,s->y,s->z);
+
+                    s->ang = ps[p].ang;
+
+                    if( ( (sync[p].bits&4) || (ps[p].quick_kick > 0) ) && sprite[ps[p].i].extra > 0 )
+                        if( ps[p].quick_kick > 0 || ( ps[p].curr_weapon != HANDREMOTE_WEAPON && ps[p].curr_weapon != HANDBOMB_WEAPON && ps[p].curr_weapon != TRIPBOMB_WEAPON && ps[p].ammo_amount[ps[p].curr_weapon] >= 0) )
+                    {
+                        for(x=0;x<8;x++)
+                        {
+                            j = EGS(sect,s->x,s->y,s->z-(8<<8),SCRAP3+(TRAND&3),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(s->zvel>>2),i,5);
+                            sprite[j].pal = 6;
+                        }
+
+                        spritesound(SLIM_DYING,i);
+                        spritesound(SQUISHED,i);
+                        if( (TRAND&255) < 32 )
+                        {
+                            j = spawn(i,BLOODPOOL);
+                            sprite[j].pal = 0;
+                        }
+                        ps[p].actors_killed ++;
+                        t[0] = -3;
+                        if(ps[p].somethingonplayer == i)
+                            ps[p].somethingonplayer = -1;
+                        KILLIT(i);
+                    }
+
+                    s->z = ps[p].posz+ps[p].pyoff-t[2]+(8<<8);
+
+                    s->z += (100-ps[p].horiz)<<4;
+
+                    if( t[2] > 512)
+                        t[2] -= 128;
+
+                    if( t[2] < 348)
+                        t[2] += 128;
+
+                    if(ps[p].newowner >= 0)
+                    {
+                        ps[p].newowner = -1;
+                        ps[p].posx = ps[p].oposx;
+                        ps[p].posy = ps[p].oposy;
+                        ps[p].posz = ps[p].oposz;
+                        ps[p].ang = ps[p].oang;
+
+                        updatesector(ps[p].posx,ps[p].posy,&ps[p].cursectnum);
+                        setpal(&ps[p]);
+
+                        j = headspritestat[1];
+                        while(j >= 0)
+                        {
+                            if(sprite[j].picnum==CAMERA1) sprite[j].yvel = 0;
+                            j = nextspritestat[j];
+                        }
+                    }
+
+                    if(t[3]>0)
+                    {
+                        short frames[] = {5,5,6,6,7,7,6,5};
+
+                        s->picnum = GREENSLIME+frames[t[3]];
+
+                        if( t[3] == 5 )
+                        {
+                            sprite[ps[p].i].extra += -(5+(TRAND&3));
+                            spritesound(SLIM_ATTACK,i);
+                        }
+
+                        if(t[3] < 7) t[3]++;
+                        else t[3] = 0;
+
+                    }
+                    else
+                    {
+                        s->picnum = GREENSLIME+5;
+                        if(rnd(32))
+                            t[3] = 1;
+                    }
+
+                    s->xrepeat = 20+(sintable[t[1]&2047]>>13);
+                    s->yrepeat = 15+(sintable[t[1]&2047]>>13);
+
+                    s->x = ps[p].posx + (sintable[(ps[p].ang+512)&2047]>>7);
+                    s->y = ps[p].posy + (sintable[ps[p].ang&2047]>>7);
+
+                    goto BOLT;
+                }
+
+                else if(s->xvel < 64 && x < 768)
+                {
+                    if(ps[p].somethingonplayer == -1)
+                    {
+                        ps[p].somethingonplayer = i;
+                        if(t[0] == 3 || t[0] == 2) //Falling downward
+                            t[2] = (12<<8);
+                        else t[2] = -(13<<8); //Climbing up duke
+                        t[0] = -4;
+                    }
+                }
+
+                    IFHIT
+                    {
+                        spritesound(SLIM_DYING,i);
+
+                        ps[p].actors_killed ++;
+                        if(ps[p].somethingonplayer == i)
+                            ps[p].somethingonplayer = -1;
+
+                        if(j == FREEZEBLAST)
+                        {
+                            spritesound(SOMETHINGFROZE,i); t[0] = -5 ; t[3] = 0 ;
+                            goto BOLT;
+                        }
+
+                        if( (TRAND&255) < 32 )
+                        {
+                            j = spawn(i,BLOODPOOL);
+                            sprite[j].pal = 0;
+                        }
+
+                        for(x=0;x<8;x++)
+                        {
+                            j = EGS(sect,s->x,s->y,s->z-(8<<8),SCRAP3+(TRAND&3),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(s->zvel>>2),i,5);
+                            sprite[j].pal = 6;
+                        }
+                        t[0] = -3;
+                        KILLIT(i);
+                    }
+                        // All weap
+                if(t[0] == -1) //Shrinking down
+                {
+                    makeitfall(i);
+
+                    s->cstat &= 65535-8;
+                    s->picnum = GREENSLIME+4;
+
+//                    if(s->yrepeat > 62)
+  //                      guts(s,JIBS6,5,myconnectindex);
+
+                    if(s->xrepeat > 32) s->xrepeat -= TRAND&7;
+                    if(s->yrepeat > 16) s->yrepeat -= TRAND&7;
+                    else
+                    {
+                        s->xrepeat = 40;
+                        s->yrepeat = 16;
+                        t[5] = -1;
+                        t[0] = 0;
+                    }
+
+                    goto BOLT;
+                }
+                else if(t[0] != -2) getglobalz(i);
+
+                if(t[0] == -2) //On top of somebody
+                {
+                    makeitfall(i);
+                    sprite[t[5]].xvel = 0;
+
+                    l = sprite[t[5]].ang;
+
+                    s->z = sprite[t[5]].z;
+                    s->x = sprite[t[5]].x+(sintable[(l+512)&2047]>>11);
+                    s->y = sprite[t[5]].y+(sintable[l&2047]>>11);
+
+                    s->picnum =  GREENSLIME+2+(global_random&1);
+
+                    if(s->yrepeat < 64) s->yrepeat+=2;
+                    else
+                    {
+                        if(s->xrepeat < 32) s->xrepeat += 4;
+                        else
+                        {
+                            t[0] = -1;
+                            x = ldist(s,&sprite[t[5]]);
+                            if(x < 768) sprite[t[5]].xrepeat = 0;
+                        }
+                    }
+
+                    goto BOLT;
+                }
+
+                //Check randomly to see of there is an actor near
+                if(rnd(32))
+                {
+                    j = headspritesect[sect];
+                    while(j>=0)
+                    {
+                        switch(sprite[j].picnum)
+                        {
+                            case LIZTROOP:
+                            case LIZMAN:
+                            case PIGCOP:
+                            case NEWBEAST:
+                                if( ldist(s,&sprite[j]) < 768 && (klabs(s->z-sprite[j].z)<8192) ) //Gulp them
+                                {
+                                    t[5] = j;
+                                    t[0] = -2;
+                                    t[1] = 0;
+                                    goto BOLT;
+                                }
+                        }
+
+                        j = nextspritesect[j];
+                    }
+                }
+
+                //Moving on the ground or ceiling
+
+                if(t[0] == 0 || t[0] == 2)
+                {
+                    s->picnum = GREENSLIME;
+
+                    if( (TRAND&511) == 0 )
+                        spritesound(SLIM_ROAM,i);
+
+                    if(t[0]==2)
+                    {
+                        s->zvel = 0;
+                        s->cstat &= (65535-8);
+
+                        if( (sector[sect].ceilingstat&1) || (hittype[i].ceilingz+6144) < s->z)
+                        {
+                            s->z += 2048;
+                            t[0] = 3;
+                            goto BOLT;
+                        }
+                    }
+                    else
+                    {
+                        s->cstat |= 8;
+                        makeitfall(i);
+                    }
+
+                    if( everyothertime&1 ) ssp(i,CLIPMASK0);
+
+                    if(s->xvel > 96)
+                    {
+                        s->xvel -= 2;
+                        goto BOLT;
+                    }
+                    else
+                    {
+                        if(s->xvel < 32) s->xvel += 4;
+                        s->xvel = 64 - (sintable[(t[1]+512)&2047]>>9);
+
+                        s->ang += getincangle(s->ang,
+                               getangle(ps[p].posx-s->x,ps[p].posy-s->y))>>3;
+// TJR
+                    }
+
+                    s->xrepeat = 36 + (sintable[(t[1]+512)&2047]>>11);
+                    s->yrepeat = 16 + (sintable[t[1]&2047]>>13);
+
+                    if(rnd(4) && (sector[sect].ceilingstat&1) == 0 &&
+                        klabs(hittype[i].floorz-hittype[i].ceilingz)
+                            < (192<<8) )
+                            {
+                                s->zvel = 0;
+                                t[0]++;
+                            }
+
+                }
+
+                if(t[0]==1)
+                {
+                    s->picnum = GREENSLIME;
+                    if(s->yrepeat < 40) s->yrepeat+=8;
+                    if(s->xrepeat > 8) s->xrepeat-=4;
+                    if(s->zvel > -(2048+1024))
+                        s->zvel -= 348;
+                    s->z += s->zvel;
+                    if(s->z < hittype[i].ceilingz+4096)
+                    {
+                        s->z = hittype[i].ceilingz+4096;
+                        s->xvel = 0;
+                        t[0] = 2;
+                    }
+                }
+
+                if(t[0]==3)
+                {
+                    s->picnum = GREENSLIME+1;
+
+                    makeitfall(i);
+
+                    if(s->z > hittype[i].floorz-(8<<8))
+                    {
+                        s->yrepeat-=4;
+                        s->xrepeat+=2;
+                    }
+                    else
+                    {
+                        if(s->yrepeat < (40-4)) s->yrepeat+=8;
+                        if(s->xrepeat > 8) s->xrepeat-=4;
+                    }
+
+                    if(s->z > hittype[i].floorz-2048)
+                    {
+                        s->z = hittype[i].floorz-2048;
+                        t[0] = 0;
+                        s->xvel = 0;
+                    }
+                }
+                goto BOLT;
+
+            case BOUNCEMINE:
+            case MORTER:
+                j = spawn(i,FRAMEEFFECT1);
+                hittype[j].temp_data[0] = 3;
+
+            case HEAVYHBOMB:
+
+                if( (s->cstat&32768) )
+                {
+                    t[2]--;
+                    if(t[2] <= 0)
+                    {
+                        spritesound(TELEPORTER,i);
+                        spawn(i,TRANSPORTERSTAR);
+                        s->cstat = 257;
+                    }
+                    goto BOLT;
+                }
+
+                p = findplayer(s,&x);
+
+                if( x < 1220 ) s->cstat &= ~257;
+                else s->cstat |= 257;
+
+                if(t[3] == 0 )
+                {
+                    j = ifhitbyweapon(i);
+                    if(j >= 0)
+                    {
+                        t[3] = 1;
+                        t[4] = 0;
+                        l = 0;
+                        s->xvel = 0;
+                        goto DETONATEB;
+                    }
+                }
+
+                if( s->picnum != BOUNCEMINE )
+                {
+                    makeitfall(i);
+
+                    if( sector[sect].lotag != 1 && s->z >= hittype[i].floorz-(FOURSLEIGHT) && s->yvel < 3 )
+                    {
+                        if( s->yvel > 0 || (s->yvel == 0 && hittype[i].floorz == sector[sect].floorz ))
+                            spritesound(PIPEBOMB_BOUNCE,i);
+                        s->zvel = -((4-s->yvel)<<8);
+                        if(sector[s->sectnum].lotag== 2)
+                            s->zvel >>= 2;
+                        s->yvel++;
+                    }
+                    if( s->z < hittype[i].ceilingz ) // && sector[sect].lotag != 2 )
+                    {
+                        s->z = hittype[i].ceilingz+(3<<8);
+                        s->zvel = 0;
+                    }
+                }
+
+                j = movesprite(i,
+                    (s->xvel*(sintable[(s->ang+512)&2047]))>>14,
+                    (s->xvel*(sintable[s->ang&2047]))>>14,
+                    s->zvel,CLIPMASK0);
+
+                if(sector[SECT].lotag == 1 && s->zvel == 0)
+                {
+                    s->z += (32<<8);
+                    if(t[5] == 0)
+                    {
+                        t[5] = 1;
+                        spawn(i,WATERSPLASH2);
+                    }
+                }
+                else t[5] = 0;
+
+                if(t[3] == 0 && ( s->picnum == BOUNCEMINE || s->picnum == MORTER ) && (j || x < 844) )
+                {
+                    t[3] = 1;
+                    t[4] = 0;
+                    l = 0;
+                    s->xvel = 0;
+                    goto DETONATEB;
+                }
+
+                if(sprite[s->owner].picnum == APLAYER)
+                    l = sprite[s->owner].yvel;
+                else l = -1;
+
+                if(s->xvel > 0)
+                {
+                    s->xvel -= 5;
+                    if(sector[sect].lotag == 2)
+                        s->xvel -= 10;
+
+                    if(s->xvel < 0)
+                        s->xvel = 0;
+                    if(s->xvel&8) s->cstat ^= 4;
+                }
+
+                if( (j&49152) == 32768 )
+                {
+                    j &= (MAXWALLS-1);
+
+                    checkhitwall(i,j,s->x,s->y,s->z,s->picnum);
+
+                    k = getangle(
+                        wall[wall[j].point2].x-wall[j].x,
+                        wall[wall[j].point2].y-wall[j].y);
+
+                    s->ang = ((k<<1) - s->ang)&2047;
+                    s->xvel >>= 1;
+                }
+
+                DETONATEB:
+
+                if( ( l >= 0 && ps[l].hbomb_on == 0 ) || t[3] == 1)
+                {
+                    t[4]++;
+
+                    if(t[4] == 2)
+                    {
+                        x = s->extra;
+                        m = 0;
+                        switch(s->picnum)
+                        {
+                            case HEAVYHBOMB: m = pipebombblastradius;break;
+                            case MORTER: m = morterblastradius;break;
+                            case BOUNCEMINE: m = bouncemineblastradius;break;
+                        }
+
+                        hitradius( i, m,x>>2,x>>1,x-(x>>2),x);
+                        spawn(i,EXPLOSION2);
+                        if( s->zvel == 0 )
+                            spawn(i,EXPLOSION2BOT);
+                        spritesound(PIPEBOMB_EXPLODE,i);
+                        for(x=0;x<8;x++)
+                            RANDOMSCRAP;
+                    }
+
+                    if(s->yrepeat)
+                    {
+                        s->yrepeat = 0;
+                        goto BOLT;
+                    }
+
+                    if(t[4] > 20)
+                    {
+                        if(s->owner != i || ud.respawn_items == 0)
+                        {
+                            KILLIT(i);
+                        }
+                        else
+                        {
+                            t[2] = respawnitemtime;
+                            spawn(i,RESPAWNMARKERRED);
+                            s->cstat = (short) 32768;
+                            s->yrepeat = 9;
+                            goto BOLT;
+                        }
+                    }
+                }
+                else if(s->picnum == HEAVYHBOMB && x < 788 && t[0] > 7 && s->xvel == 0)
+                    if( cansee(s->x,s->y,s->z-(8<<8),s->sectnum,ps[p].posx,ps[p].posy,ps[p].posz,ps[p].cursectnum) )
+                        if(ps[p].ammo_amount[HANDBOMB_WEAPON] < max_ammo_amount[HANDBOMB_WEAPON] )
+                {
+                    if(ud.coop >= 1 && s->owner == i)
+                    {
+                        for(j=0;j<ps[p].weapreccnt;j++)
+                            if(ps[p].weaprecs[j] == s->picnum)
+                                goto BOLT;
+
+                        if(ps[p].weapreccnt < 255)
+                            ps[p].weaprecs[ps[p].weapreccnt++] = s->picnum;
+                    }
+
+                    addammo(HANDBOMB_WEAPON,&ps[p],1);
+                    spritesound(DUKE_GET,ps[p].i);
+
+                    if( ps[p].gotweapon[HANDBOMB_WEAPON] == 0 || s->owner == ps[p].i )
+                        addweapon(&ps[p],HANDBOMB_WEAPON);
+
+                    if( sprite[s->owner].picnum != APLAYER )
+                    {
+                        ps[p].pals[0] = 0;
+                        ps[p].pals[1] = 32;
+                        ps[p].pals[2] = 0;
+                        ps[p].pals_time = 32;
+                    }
+
+                    if( s->owner != i || ud.respawn_items == 0 )
+                    {
+                        if(s->owner == i && ud.coop >= 1)
+                            goto BOLT;
+                        KILLIT(i);
+                    }
+                    else
+                    {
+                        t[2] = respawnitemtime;
+                        spawn(i,RESPAWNMARKERRED);
+                        s->cstat = (short) 32768;
+                    }
+                }
+
+                if(t[0] < 8) t[0]++;
+                goto BOLT;
+
+            case REACTORBURNT:
+            case REACTOR2BURNT:
+                goto BOLT;
+
+            case REACTOR:
+            case REACTOR2:
+
+                if( t[4] == 1 )
+                {
+                    j = headspritesect[sect];
+                    while(j >= 0)
+                    {
+                        switch(sprite[j].picnum)
+                        {
+                            case SECTOREFFECTOR:
+                                if(sprite[j].lotag == 1)
+                                {
+                                    sprite[j].lotag = (short) 65535;
+                                    sprite[j].hitag = (short) 65535;
+                                }
+                                break;
+                            case REACTOR:
+                                sprite[j].picnum = REACTORBURNT;
+                                break;
+                            case REACTOR2:
+                                sprite[j].picnum = REACTOR2BURNT;
+                                break;
+                            case REACTORSPARK:
+                            case REACTOR2SPARK:
+                                sprite[j].cstat = (short) 32768;
+                                break;
+                        }
+                        j = nextspritesect[j];
+                    }
+                    goto BOLT;
+                }
+
+                if(t[1] >= 20)
+                {
+                    t[4] = 1;
+                    goto BOLT;
+                }
+
+                p = findplayer(s,&x);
+
+                t[2]++;
+                if( t[2] == 4 ) t[2]=0;
+
+                if( x < 4096 )
+                {
+                    if( (TRAND&255) < 16 )
+                    {
+                        if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                            spritesound(DUKE_LONGTERM_PAIN,ps[p].i);
+
+                        spritesound(SHORT_CIRCUIT,i);
+
+                        sprite[ps[p].i].extra --;
+                        ps[p].pals_time = 32;
+                        ps[p].pals[0] = 32;
+                        ps[p].pals[1] = 0;
+                        ps[p].pals[2] = 0;
+                    }
+                    t[0] += 128;
+                    if( t[3] == 0 )
+                        t[3] = 1;
+                }
+                else t[3] = 0;
+
+                if( t[1] )
+                {
+                    t[1]++;
+
+                    t[4] = s->z;
+                    s->z = sector[sect].floorz-(TRAND%(sector[sect].floorz-sector[sect].ceilingz));
+
+                    switch( t[1] )
+                    {
+                        case 3:
+                            //Turn on all of those flashing sectoreffector.
+                            hitradius( i, 4096,
+                                       impact_damage<<2,
+                                       impact_damage<<2,
+                                       impact_damage<<2,
+                                       impact_damage<<2 );
+/*
+                            j = headspritestat[3];
+                            while(j>=0)
+                            {
+                                if( sprite[j].lotag  == 3 )
+                                    hittype[j].temp_data[4]=1;
+                                else if(sprite[j].lotag == 12)
+                                {
+                                    hittype[j].temp_data[4] = 1;
+                                    sprite[j].lotag = 3;
+                                    sprite[j].owner = 0;
+                                    hittype[j].temp_data[0] = s->shade;
+                                }
+                                j = nextspritestat[j];
+                            }
+*/
+                            j = headspritestat[6];
+                            while(j >= 0)
+                            {
+                                if(sprite[j].picnum == MASTERSWITCH)
+                                    if(sprite[j].hitag == s->hitag)
+                                        if(sprite[j].yvel == 0)
+                                            sprite[j].yvel = 1;
+                                j = nextspritestat[j];
+                            }
+                            break;
+
+                        case 4:
+                        case 7:
+                        case 10:
+                        case 15:
+                            j = headspritesect[sect];
+                            while(j >= 0)
+                            {
+                                l = nextspritesect[j];
+
+                                if(j != i)
+                                {
+                                    deletesprite(j);
+                                    break;
+                                }
+                                j = l;
+                            }
+                            break;
+                    }
+                    for(x=0;x<16;x++)
+                        RANDOMSCRAP;
+
+                    s->z = t[4];
+                    t[4] = 0;
+
+                }
+                else
+                {
+                    IFHIT
+                    {
+                        for(x=0;x<32;x++)
+                            RANDOMSCRAP;
+                        if(s->extra < 0)
+                            t[1] = 1;
+                    }
+                }
+                goto BOLT;
+
+            case CAMERA1:
+
+                if( t[0] == 0 )
+                {
+                    t[1]+=8;
+                    if(camerashitable)
+                    {
+                        IFHIT
+                        {
+                            t[0] = 1; // static
+                            s->cstat = (short)32768;
+                            for(x=0;x<5;x++) RANDOMSCRAP;
+                            goto BOLT;
+                        }
+                    }
+
+                    if(s->hitag > 0)
+                    {
+                        if(t[1]<s->hitag)
+                            s->ang+=8;
+                        else if(t[1]<(s->hitag*3))
+                            s->ang-=8;
+                        else if(t[1] < (s->hitag<<2) )
+                            s->ang+=8;
+                        else
+                        {
+                            t[1]=8;
+                            s->ang+=16;
+                        }
+                    }
+                }
+                goto BOLT;
+        }
+
+
+// #ifndef VOLOMEONE
+        if( ud.multimode < 2 && badguy(s) )
+        {
+            if( actor_tog == 1)
+            {
+                s->cstat = (short)32768;
+                goto BOLT;
+            }
+            else if(actor_tog == 2) s->cstat = 257;
+        }
+// #endif
+
+        p = findplayer(s,&x);
+
+        execute(i,p,x);
+
+        BOLT:
+
+        i = nexti;
+    }
+
+}
+
+
+void moveexplosions(void)  // STATNUM 5
+{
+    short i, j, k, nexti, sect, p;
+    long l, x, *t;
+    spritetype *s;
+
+    i = headspritestat[5];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        t = &hittype[i].temp_data[0];
+        s = &sprite[i];
+        sect = s->sectnum;
+
+        if( sect < 0 || s->xrepeat == 0 ) KILLIT(i);
+
+        hittype[i].bposx = s->x;
+        hittype[i].bposy = s->y;
+        hittype[i].bposz = s->z;
+
+        switch(s->picnum)
+        {
+            case NEON1:
+            case NEON2:
+            case NEON3:
+            case NEON4:
+            case NEON5:
+            case NEON6:
+
+                if( (global_random/(s->lotag+1)&31) > 4) s->shade = -127;
+                else s->shade = 127;
+                goto BOLT;
+
+            case BLOODSPLAT1:
+            case BLOODSPLAT2:
+            case BLOODSPLAT3:
+            case BLOODSPLAT4:
+
+                if( t[0] == 7*26 ) goto BOLT;
+                s->z += 16+(TRAND&15);
+                t[0]++;
+                if( (t[0]%9) == 0 ) s->yrepeat++;
+                goto BOLT;
+
+            case NUKEBUTTON:
+            case NUKEBUTTON+1:
+            case NUKEBUTTON+2:
+            case NUKEBUTTON+3:
+
+                if(t[0])
+                {
+                    t[0]++;
+                    if(t[0] == 8) s->picnum = NUKEBUTTON+1;
+                    else if(t[0] == 16)
+                    {
+                        s->picnum = NUKEBUTTON+2;
+                        ps[sprite[s->owner].yvel].fist_incs = 1;
+                    }
+                    if( ps[sprite[s->owner].yvel].fist_incs == 26 )
+                        s->picnum = NUKEBUTTON+3;
+                }
+                goto BOLT;
+
+            case FORCESPHERE:
+
+                l = s->xrepeat;
+                if(t[1] > 0)
+                {
+                    t[1]--;
+                    if(t[1] == 0)
+                    {
+                        KILLIT(i);
+                    }
+                }
+                if(hittype[s->owner].temp_data[1] == 0)
+                {
+                    if(t[0] < 64)
+                    {
+                        t[0]++;
+                        l += 3;
+                    }
+                }
+                else
+                    if(t[0] > 64)
+                    {
+                        t[0]--;
+                        l -= 3;
+                    }
+
+                s->x = sprite[s->owner].x;
+                s->y = sprite[s->owner].y;
+                s->z = sprite[s->owner].z;
+                s->ang += hittype[s->owner].temp_data[0];
+
+                if(l > 64) l = 64;
+                else if(l < 1) l = 1;
+
+                s->xrepeat = l;
+                s->yrepeat = l;
+                s->shade = (l>>1)-48;
+
+                for(j=t[0];j > 0;j--)
+                    ssp(i,CLIPMASK0);
+                goto BOLT;
+            case WATERSPLASH2:
+
+                t[0]++;
+                if(t[0] == 1 )
+                {
+                    if(sector[sect].lotag != 1 && sector[sect].lotag != 2)
+                        KILLIT(i);
+/*                    else
+                    {
+                        l = getflorzofslope(sect,s->x,s->y)-s->z;
+                        if( l > (16<<8) ) KILLIT(i);
+                    }
+                    else */ if(Sound[ITEM_SPLASH].num == 0)
+                        spritesound(ITEM_SPLASH,i);
+                }
+                if(t[0] == 3)
+                {
+                    t[0] = 0;
+                    t[1]++;
+                }
+                if(t[1] == 5)
+                    deletesprite(i);
+                goto BOLT;
+
+            case FRAMEEFFECT1:
+			case FRAMEEFFECT1_13CON:
+                if(s->owner >= 0)
+                {
+                    t[0]++;
+
+                    if( t[0] > 7 )
+                    {
+                        KILLIT(i);
+                    }
+                    else if( t[0] > 4 )
+                        s->cstat |= 512+2;
+                    else if( t[0] > 2 )
+                        s->cstat |= 2;
+                    s->xoffset = sprite[s->owner].xoffset;
+                    s->yoffset = sprite[s->owner].yoffset;
+                }
+                goto BOLT;
+            case INNERJAW:
+            case INNERJAW+1:
+
+                p = findplayer(s,&x);
+                if(x < 512)
+                {
+                    ps[p].pals_time = 32;
+                    ps[p].pals[0] = 32;
+                    ps[p].pals[1] = 0;
+                    ps[p].pals[2] = 0;
+                    sprite[ps[p].i].extra -= 4;
+                }
+
+            case FIRELASER:
+                if(s->extra != 999)
+                    s->extra = 999;
+                else KILLIT(i);
+                break;
+            case TONGUE:
+                KILLIT(i);
+            case MONEY+1:
+            case MAIL+1:
+            case PAPER+1:
+                hittype[i].floorz = s->z = getflorzofslope(s->sectnum,s->x,s->y);
+                break;
+            case MONEY:
+            case MAIL:
+            case PAPER:
+
+                s->xvel = (TRAND&7)+(sintable[T1&2047]>>9);
+                T1 += (TRAND&63);
+                if( (T1&2047) > 512 && (T1&2047) < 1596)
+                {
+                    if(sector[sect].lotag == 2)
+                    {
+                        if(s->zvel < 64)
+                            s->zvel += (gc>>5)+(TRAND&7);
+                    }
+                    else
+                        if(s->zvel < 144)
+                            s->zvel += (gc>>5)+(TRAND&7);
+                }
+
+                ssp(i,CLIPMASK0);
+
+                if( (TRAND&3) == 0 )
+                    setsprite(i,s->x,s->y,s->z);
+
+                if(s->sectnum == -1) KILLIT(i);
+                l = getflorzofslope(s->sectnum,s->x,s->y);
+
+                if( s->z > l )
+                {
+                    s->z = l;
+
+                    insertspriteq(i);
+                    PN ++;
+
+                    j = headspritestat[5];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].picnum == BLOODPOOL)
+                            if(ldist(s,&sprite[j]) < 348)
+                        {
+                            s->pal = 2;
+                            break;
+                        }
+                        j = nextspritestat[j];
+                    }
+                }
+
+                break;
+
+            case JIBS1:
+            case JIBS2:
+            case JIBS3:
+            case JIBS4:
+            case JIBS5:
+            case JIBS6:
+            case HEADJIB1:
+            case ARMJIB1:
+            case LEGJIB1:
+            case LIZMANHEAD1:
+            case LIZMANARM1:
+            case LIZMANLEG1:
+            case DUKETORSO:
+            case DUKEGUN:
+            case DUKELEG:
+
+                if(s->xvel > 0) s->xvel--;
+                else s->xvel = 0;
+
+                if( t[5] < 30*10 )
+                    t[5]++;
+                else { KILLIT(i); }
+
+
+                if(s->zvel > 1024 && s->zvel < 1280)
+                {
+                    setsprite(i,s->x,s->y,s->z);
+                    sect = s->sectnum;
+                }
+
+                l = getflorzofslope(sect,s->x,s->y);
+                x = getceilzofslope(sect,s->x,s->y);
+                if(x == l || sect < 0 || sect >= MAXSECTORS) KILLIT(i);
+
+                if( s->z < l-(2<<8) )
+                {
+                    if(t[1] < 2) t[1]++;
+                    else if(sector[sect].lotag != 2)
+                    {
+                        t[1] = 0;
+                        if( s->picnum == DUKELEG || s->picnum == DUKETORSO || s->picnum == DUKEGUN )
+                        {
+                            if(t[0] > 6) t[0] = 0;
+                            else t[0]++;
+                        }
+                        else
+                        {
+                            if(t[0] > 2)
+                                t[0] = 0;
+                            else t[0]++;
+                        }
+                    }
+
+                    if(s->zvel < 6144)
+                    {
+                        if(sector[sect].lotag == 2)
+                        {
+                            if(s->zvel < 1024)
+                                s->zvel += 48;
+                            else s->zvel = 1024;
+                        }
+                        else s->zvel += gc-50;
+                    }
+
+                    s->x += (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    s->y += (s->xvel*sintable[s->ang&2047])>>14;
+                    s->z += s->zvel;
+
+                }
+                else
+                {
+                    if(t[2] == 0)
+                    {
+                        if( s->sectnum == -1) { KILLIT(i); }
+                        if( (sector[s->sectnum].floorstat&2) ) { KILLIT(i); }
+                        t[2]++;
+                    }
+                    l = getflorzofslope(s->sectnum,s->x,s->y);
+
+                    s->z = l-(2<<8);
+                    s->xvel = 0;
+
+                    if(s->picnum == JIBS6)
+                    {
+                        t[1]++;
+                        if( (t[1]&3) == 0 && t[0] < 7)
+                            t[0]++;
+                        if(t[1] > 20) KILLIT(i);
+                    }
+                    else { s->picnum = JIBS6; t[0] = 0; t[1] = 0; }
+                }
+                goto BOLT;
+
+            case BLOODPOOL:
+            case PUKE:
+
+                if(t[0] == 0)
+                {
+                    t[0] = 1;
+                    if(sector[sect].floorstat&2) { KILLIT(i); }
+                    else insertspriteq(i);
+                }
+
+                makeitfall(i);
+
+                p = findplayer(s,&x);
+
+                s->z = hittype[i].floorz-(FOURSLEIGHT);
+
+                if(t[2] < 32)
+                {
+                    t[2]++;
+                    if(hittype[i].picnum == TIRE)
+                    {
+                        if(s->xrepeat < 64 && s->yrepeat < 64)
+                        {
+                            s->xrepeat += TRAND&3;
+                            s->yrepeat += TRAND&3;
+                        }
+                    }
+                    else
+                    {
+                        if(s->xrepeat < 32 && s->yrepeat < 32)
+                        {
+                            s->xrepeat += TRAND&3;
+                            s->yrepeat += TRAND&3;
+                        }
+                    }
+                }
+
+                if(x < 844 && s->xrepeat > 6 && s->yrepeat > 6)
+                {
+                    if( s->pal == 0 && (TRAND&255) < 16 && s->picnum != PUKE)
+                    {
+                        if(ps[p].boot_amount > 0)
+                            ps[p].boot_amount--;
+                        else
+                        {
+                            if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                spritesound(DUKE_LONGTERM_PAIN,ps[p].i);
+                            sprite[ps[p].i].extra --;
+                            ps[p].pals_time = 32;
+                            ps[p].pals[0] = 16;
+                            ps[p].pals[1] = 0;
+                            ps[p].pals[2] = 0;
+                        }
+                    }
+
+                    if(t[1] == 1) goto BOLT;
+                    t[1] = 1;
+
+                    if(hittype[i].picnum == TIRE)
+                        ps[p].footprintcount = 10;
+                    else ps[p].footprintcount = 3;
+
+                    ps[p].footprintpal = s->pal;
+                    ps[p].footprintshade = s->shade;
+
+                    if(t[2] == 32)
+                    {
+                        s->xrepeat -= 6;
+                        s->yrepeat -= 6;
+                    }
+                }
+                else t[1] = 0;
+                goto BOLT;
+
+            case BURNING:
+            case BURNING2:
+            case FECES:
+            case WATERBUBBLE:
+            case SMALLSMOKE:
+            case EXPLOSION2:
+            case SHRINKEREXPLOSION:
+            case EXPLOSION2BOT:
+            case BLOOD:
+            case LASERSITE:
+            case FORCERIPPLE:
+            case TRANSPORTERSTAR:
+            case TRANSPORTERBEAM:
+                p = findplayer(s,&x);
+                execute(i,p,x);
+                goto BOLT;
+
+            case SHELL:
+            case SHOTGUNSHELL:
+
+                ssp(i,CLIPMASK0);
+
+                if(sect < 0 || ( sector[sect].floorz+(24<<8) ) < s->z ) KILLIT(i);
+
+                if(sector[sect].lotag == 2)
+                {
+                    t[1]++;
+                    if(t[1] > 8)
+                    {
+                        t[1] = 0;
+                        t[0]++;
+                        t[0] &= 3;
+                    }
+                    if(s->zvel < 128) s->zvel += (gc/13); // 8
+                    else s->zvel -= 64;
+                    if(s->xvel > 0)
+                        s->xvel -= 4;
+                    else s->xvel = 0;
+                }
+                else
+                {
+                    t[1]++;
+                    if(t[1] > 3)
+                    {
+                        t[1] = 0;
+                        t[0]++;
+                        t[0] &= 3;
+                    }
+                    if(s->zvel < 512) s->zvel += (gc/3); // 52;
+                    if(s->xvel > 0)
+                        s->xvel --;
+                    else KILLIT(i);
+                }
+
+                goto BOLT;
+
+            case GLASSPIECES:
+            case GLASSPIECES+1:
+            case GLASSPIECES+2:
+
+                makeitfall(i);
+
+                if(s->zvel > 4096) s->zvel = 4096;
+                if(sect < 0) KILLIT(i);
+
+                if( s->z == hittype[i].floorz-(FOURSLEIGHT) && t[0] < 3)
+                {
+                    s->zvel = -((3-t[0])<<8)-(TRAND&511);
+                    if(sector[sect].lotag == 2)
+                        s->zvel >>= 1;
+                    s->xrepeat >>= 1;
+                    s->yrepeat >>= 1;
+                    if( rnd(96) )
+                      setsprite(i,s->x,s->y,s->z);
+                    t[0]++;//Number of bounces
+                }
+                else if( t[0] == 3 ) KILLIT(i);
+
+                if(s->xvel > 0)
+                {
+                    s->xvel -= 2;
+                    s->cstat = ((s->xvel&3)<<2);
+                }
+                else s->xvel = 0;
+
+                ssp(i,CLIPMASK0);
+
+                goto BOLT;
+        }
+
+        IFWITHIN(SCRAP6,SCRAP5+3)
+        {
+                if(s->xvel > 0)
+                    s->xvel--;
+                else s->xvel = 0;
+
+                if(s->zvel > 1024 && s->zvel < 1280)
+                {
+                    setsprite(i,s->x,s->y,s->z);
+                    sect = s->sectnum;
+                }
+
+                if( s->z < sector[sect].floorz-(2<<8) )
+                {
+                    if(t[1] < 1) t[1]++;
+                    else
+                    {
+                        t[1] = 0;
+
+                        if(s->picnum < SCRAP6+8)
+                        {
+                            if(t[0] > 6)
+                                t[0] = 0;
+                            else t[0]++;
+                        }
+                        else
+                        {
+                            if(t[0] > 2)
+                                t[0] = 0;
+                            else t[0]++;
+                        }
+                    }
+                    if(s->zvel < 4096) s->zvel += gc-50;
+                    s->x += (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    s->y += (s->xvel*sintable[s->ang&2047])>>14;
+                    s->z += s->zvel;
+                }
+                else
+                {
+                    if(s->picnum == SCRAP1 && s->yvel > 0)
+                    {
+                        j = spawn(i,s->yvel);
+                        setsprite(j,s->x,s->y,s->z);
+                        getglobalz(j);
+                        sprite[j].hitag = sprite[j].lotag = 0;
+                    }
+                    KILLIT(i);
+                }
+                goto BOLT;
+        }
+
+        BOLT:
+        i = nexti;
+    }
+}
+
+void moveeffectors(void)   //STATNUM 3
+{
+    long q=0, l, m, x, st, j, *t;
+    short i, k, nexti, nextk, p, sh, nextj;
+    spritetype *s;
+    sectortype *sc;
+    walltype *wal;
+
+    fricxv = fricyv = 0;
+
+    i = headspritestat[3];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+        s = &sprite[i];
+
+        sc = &sector[s->sectnum];
+        st = s->lotag;
+        sh = s->hitag;
+
+        t = &hittype[i].temp_data[0];
+
+        switch(st)
+        {
+            case 0:
+            {
+                long zchange = 0;
+
+                zchange = 0;
+
+                j = s->owner;
+
+                if( sprite[j].lotag == (short) 65535 )
+                    KILLIT(i);
+
+                q = sc->extra>>3;
+                l = 0;
+
+                if(sc->lotag == 30)
+                {
+                    q >>= 2;
+
+                    if( sprite[i].extra == 1 )
+                    {
+                        if(hittype[i].tempang < 256)
+                        {
+                            hittype[i].tempang += 4;
+                            if(hittype[i].tempang >= 256)
+                                callsound(s->sectnum,i);
+                            if(s->clipdist) l = 1;
+                            else l = -1;
+                        }
+                        else hittype[i].tempang = 256;
+
+                        if( sc->floorz > s->z ) //z's are touching
+                        {
+                            sc->floorz -= 512;
+                            zchange = -512;
+                            if( sc->floorz < s->z )
+                                sc->floorz = s->z;
+                        }
+
+                        else if( sc->floorz < s->z ) //z's are touching
+                        {
+                            sc->floorz += 512;
+                            zchange = 512;
+                            if( sc->floorz > s->z )
+                                sc->floorz = s->z;
+                        }
+                    }
+                    else if(sprite[i].extra == 3)
+                    {
+                        if(hittype[i].tempang > 0)
+                        {
+                            hittype[i].tempang -= 4;
+                            if(hittype[i].tempang <= 0)
+                                callsound(s->sectnum,i);
+                            if( s->clipdist ) l = -1;
+                            else l = 1;
+                        }
+                        else hittype[i].tempang = 0;
+
+                        if( sc->floorz > T4 ) //z's are touching
+                        {
+                            sc->floorz -= 512;
+                            zchange = -512;
+                            if( sc->floorz < T4 )
+                                sc->floorz = T4;
+                        }
+
+                        else if( sc->floorz < T4 ) //z's are touching
+                        {
+                            sc->floorz += 512;
+                            zchange = 512;
+                            if( sc->floorz > T4 )
+                                sc->floorz = T4;
+                        }
+                    }
+
+                    s->ang += (l*q);
+                    t[2] += (l*q);
+                }
+                else
+                {
+                    if( hittype[j].temp_data[0] == 0 ) break;
+                    if( hittype[j].temp_data[0] == 2 ) KILLIT(i);
+
+                    if( sprite[j].ang > 1024 )
+                        l = -1;
+                    else l = 1;
+                    if( t[3] == 0 )
+                        t[3] = ldist(s,&sprite[j]);
+                    s->xvel = t[3];
+                    s->x = sprite[j].x;
+                    s->y = sprite[j].y;
+                    s->ang += (l*q);
+                    t[2] += (l*q);
+                }
+
+                if( l && (sc->floorstat&64) )
+                {
+                    for(p=connecthead;p>=0;p=connectpoint2[p])
+                    {
+                        if( ps[p].cursectnum == s->sectnum && ps[p].on_ground == 1)
+                        {
+
+                            ps[p].ang += (l*q);
+                            ps[p].ang &= 2047;
+
+                            ps[p].posz += zchange;
+
+                            rotatepoint( sprite[j].x,sprite[j].y,
+                                ps[p].posx,ps[p].posy,(q*l),
+                                &m,&x);
+
+                            ps[p].bobposx += m-ps[p].posx;
+                            ps[p].bobposy += x-ps[p].posy;
+
+                            ps[p].posx = m;
+                            ps[p].posy = x;
+
+                            if(sprite[ps[p].i].extra <= 0)
+                            {
+                                sprite[ps[p].i].x = m;
+                                sprite[ps[p].i].y = x;
+                            }
+                        }
+                    }
+
+                    p = headspritesect[s->sectnum];
+                    while(p >= 0)
+                    {
+                        if(sprite[p].statnum != 3 && sprite[p].statnum != 4)
+                            if( sprite[p].picnum != LASERLINE )
+                        {
+                            if(sprite[p].picnum == APLAYER && sprite[p].owner >= 0)
+                            {
+                                p = nextspritesect[p];
+                                continue;
+                            }
+
+                            sprite[p].ang += (l*q);
+                            sprite[p].ang &= 2047;
+
+                            sprite[p].z += zchange;
+
+                            rotatepoint(sprite[j].x,sprite[j].y,
+                                sprite[p].x,sprite[p].y,(q*l),
+                                &sprite[p].x,&sprite[p].y);
+
+                        }
+                        p = nextspritesect[p];
+                    }
+
+                }
+
+                ms(i);
+            }
+
+            break;
+            case 1: //Nothing for now used as the pivot
+                if(s->owner == -1) //Init
+                {
+                    s->owner = i;
+
+                    j = headspritestat[3];
+                    while(j >= 0)
+                    {
+                        if( sprite[j].lotag == 19 && sprite[j].hitag == sh )
+                        {
+                            t[0] = 0;
+                            break;
+                        }
+                        j = nextspritestat[j];
+                    }
+                }
+
+                break;
+            case 6:
+                k = sc->extra;
+
+                if(t[4] > 0)
+                {
+                    t[4]--;
+                    if( t[4] >= (k-(k>>3)) )
+                        s->xvel -= (k>>5);
+                    if( t[4] > ((k>>1)-1) && t[4] < (k-(k>>3)) )
+                        s->xvel = 0;
+                    if( t[4] < (k>>1) )
+                        s->xvel += (k>>5);
+                    if( t[4] < ((k>>1)-(k>>3)) )
+                    {
+                        t[4] = 0;
+                        s->xvel = k;
+                    }
+                }
+                else s->xvel = k;
+
+                j = headspritestat[3];
+                while( j >= 0)
+                {
+                    if( (sprite[j].lotag == 14) && (sh == sprite[j].hitag) && (hittype[j].temp_data[0] == t[0]) )
+                    {
+                        sprite[j].xvel = s->xvel;
+//                        if( t[4] == 1 )
+                        {
+                            if(hittype[j].temp_data[5] == 0)
+                                hittype[j].temp_data[5] = dist(&sprite[j],s);
+                            x = sgn( dist(&sprite[j],s)-hittype[j].temp_data[5] );
+                            if(sprite[j].extra)
+                                x = -x;
+                            s->xvel += x;
+                        }
+                        hittype[j].temp_data[4] = t[4];
+                    }
+                    j = nextspritestat[j];
+                }
+                x = 0;
+
+
+            case 14:
+                if(s->owner==-1)
+                    s->owner = LocateTheLocator((short)t[3],(short)t[0]);
+
+                if(s->owner == -1)
+                {
+                    sprintf(tempbuf,"Could not find any locators for SE# 6 and 14 with a hitag of %ld.\n",t[3]);
+                    gameexit(tempbuf);
+                }
+
+                j = ldist(&sprite[s->owner],s);
+
+                if( j < 1024L )
+                {
+                    if(st==6)
+                        if(sprite[s->owner].hitag&1)
+                            t[4]=sc->extra; //Slow it down
+                    t[3]++;
+                    s->owner = LocateTheLocator(t[3],t[0]);
+                    if(s->owner==-1)
+                    {
+                        t[3]=0;
+                        s->owner = LocateTheLocator(0,t[0]);
+                    }
+                }
+
+                if(s->xvel)
+                {
+                    x = getangle(sprite[s->owner].x-s->x,sprite[s->owner].y-s->y);
+                    q = getincangle(s->ang,x)>>3;
+
+                    t[2] += q;
+                    s->ang += q;
+
+                    if(s->xvel == sc->extra )
+                    {
+                        if( (sc->floorstat&1) == 0 && (sc->ceilingstat&1) == 0 )
+                        {
+                            if( Sound[hittype[i].lastvx].num == 0 )
+                                spritesound(hittype[i].lastvx,i);
+                        }
+                        else if( ud.monsters_off == 0 && sc->floorpal == 0 && (sc->floorstat&1) && rnd(8) )
+                        {
+                            p = findplayer(s,&x);
+                            if(x < 20480)
+                            {
+                                j = s->ang;
+                                s->ang = getangle(s->x-ps[p].posx,s->y-ps[p].posy);
+                                shoot(i,RPG);
+                                s->ang = j;
+                            }
+                        }
+                    }
+
+                    if(s->xvel <= 64 && (sc->floorstat&1) == 0 && (sc->ceilingstat&1) == 0 )
+                        stopsound(hittype[i].lastvx);
+
+                    if( (sc->floorz-sc->ceilingz) < (108<<8) )
+                    {
+                        if(ud.clipping == 0 && s->xvel >= 192)
+                            for(p=connecthead;p>=0;p=connectpoint2[p])
+                                if(sprite[ps[p].i].extra > 0)
+                        {
+                            k = ps[p].cursectnum;
+                            updatesector(ps[p].posx,ps[p].posy,&k);
+                            if( ( k == -1 && ud.clipping == 0 ) || ( k == s->sectnum && ps[p].cursectnum != s->sectnum ) )
+                            {
+                                ps[p].posx = s->x;
+                                ps[p].posy = s->y;
+                                ps[p].cursectnum = s->sectnum;
+
+                                setsprite(ps[p].i,s->x,s->y,s->z);
+                                quickkill(&ps[p]);
+                            }
+                        }
+                    }
+
+                    m = (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    x = (s->xvel*sintable[s->ang&2047])>>14;
+
+                    for(p = connecthead;p >= 0;p=connectpoint2[p])
+                       if(sector[ps[p].cursectnum].lotag != 2)
+                    {
+                        if(po[p].os == s->sectnum)
+                        {
+                            po[p].ox += m;
+                            po[p].oy += x;
+                        }
+
+                        if(s->sectnum == sprite[ps[p].i].sectnum)
+                        {
+                            rotatepoint(s->x,s->y,ps[p].posx,ps[p].posy,q,&ps[p].posx,&ps[p].posy);
+
+                            ps[p].posx += m;
+                            ps[p].posy += x;
+
+                            ps[p].bobposx += m;
+                            ps[p].bobposy += x;
+
+                            ps[p].ang += q;
+
+                            if(numplayers > 1)
+                            {
+                                ps[p].oposx = ps[p].posx;
+                                ps[p].oposy = ps[p].posy;
+                            }
+                            if( sprite[ps[p].i].extra <= 0 )
+                            {
+                                sprite[ps[p].i].x = ps[p].posx;
+                                sprite[ps[p].i].y = ps[p].posy;
+                            }
+                        }
+                    }
+                    j = headspritesect[s->sectnum];
+                    while(j >= 0)
+                    {
+                        if (sprite[j].statnum != 10 && sector[sprite[j].sectnum].lotag != 2 && sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS )
+                        {
+                            rotatepoint(s->x,s->y,
+                                sprite[j].x,sprite[j].y,q,
+                                &sprite[j].x,&sprite[j].y);
+
+                            sprite[j].x+= m;
+                            sprite[j].y+= x;
+
+                            sprite[j].ang+=q;
+
+                            if(numplayers > 1)
+                            {
+                                hittype[j].bposx = sprite[j].x;
+                                hittype[j].bposy = sprite[j].y;
+                            }
+                        }
+                        j = nextspritesect[j];
+                    }
+
+                    ms(i);
+                    setsprite(i,s->x,s->y,s->z);
+
+                    if( (sc->floorz-sc->ceilingz) < (108<<8) )
+                    {
+                        if(ud.clipping == 0 && s->xvel >= 192)
+                            for(p=connecthead;p>=0;p=connectpoint2[p])
+                                if(sprite[ps[p].i].extra > 0)
+                        {
+                            k = ps[p].cursectnum;
+                            updatesector(ps[p].posx,ps[p].posy,&k);
+                            if( ( k == -1 && ud.clipping == 0 ) || ( k == s->sectnum && ps[p].cursectnum != s->sectnum ) )
+                            {
+                                ps[p].oposx = ps[p].posx = s->x;
+                                ps[p].oposy = ps[p].posy = s->y;
+                                ps[p].cursectnum = s->sectnum;
+
+                                setsprite(ps[p].i,s->x,s->y,s->z);
+                                quickkill(&ps[p]);
+                            }
+                        }
+
+                        j = headspritesect[sprite[OW].sectnum];
+                        while(j >= 0)
+                        {
+                            l = nextspritesect[j];
+                            if (sprite[j].statnum == 1 && badguy(&sprite[j]) && sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS )
+                            {
+                                k = sprite[j].sectnum;
+                                updatesector(sprite[j].x,sprite[j].y,&k);
+                                if( sprite[j].extra >= 0 && k == s->sectnum )
+                                {
+                                    gutsdir(&sprite[j],JIBS6,72,myconnectindex);
+                                    spritesound(SQUISHED,i);
+                                    deletesprite(j);
+                                }
+                            }
+                            j = l;
+                        }
+                    }
+                }
+
+                break;
+
+            case 30:
+                if(s->owner == -1)
+                {
+                    t[3] = !t[3];
+                    s->owner = LocateTheLocator(t[3],t[0]);
+                }
+                else
+                {
+
+                    if(t[4] == 1) // Starting to go
+                    {
+                        if( ldist( &sprite[s->owner],s ) < (2048-128) )
+                            t[4] = 2;
+                        else
+                        {
+                            if(s->xvel == 0)
+                                operateactivators(s->hitag+(!t[3]),-1);
+                            if(s->xvel < 256)
+                                s->xvel += 16;
+                        }
+                    }
+                    if(t[4] == 2)
+                    {
+                        l = FindDistance2D(sprite[s->owner].x-s->x,sprite[s->owner].y-s->y);
+
+                        if(l <= 128)
+                            s->xvel = 0;
+
+                        if( s->xvel > 0 )
+                            s->xvel -= 16;
+                        else
+                        {
+                            s->xvel = 0;
+                            operateactivators(s->hitag+(short)t[3],-1);
+                            s->owner = -1;
+                            s->ang += 1024;
+                            t[4] = 0;
+                            operateforcefields(i,s->hitag);
+
+                            j = headspritesect[s->sectnum];
+                            while(j >= 0)
+                            {
+                                if(sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS )
+                                {
+                                    hittype[j].bposx = sprite[j].x;
+                                    hittype[j].bposy = sprite[j].y;
+                                }
+                                j = nextspritesect[j];
+                            }
+
+                        }
+                    }
+                }
+
+                if(s->xvel)
+                {
+                    l = (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    x = (s->xvel*sintable[s->ang&2047])>>14;
+
+                    if( (sc->floorz-sc->ceilingz) < (108<<8) )
+                        if(ud.clipping == 0)
+                            for(p=connecthead;p>=0;p=connectpoint2[p])
+                                if(sprite[ps[p].i].extra > 0)
+                    {
+                        k = ps[p].cursectnum;
+                        updatesector(ps[p].posx,ps[p].posy,&k);
+                        if( ( k == -1 && ud.clipping == 0 ) || ( k == s->sectnum && ps[p].cursectnum != s->sectnum ) )
+                        {
+                            ps[p].posx = s->x;
+                            ps[p].posy = s->y;
+                            ps[p].cursectnum = s->sectnum;
+
+                            setsprite(ps[p].i,s->x,s->y,s->z);
+                            quickkill(&ps[p]);
+                        }
+                    }
+
+                    for(p = connecthead;p >= 0;p = connectpoint2[p])
+                    {
+                        if( sprite[ps[p].i].sectnum == s->sectnum )
+                        {
+                            ps[p].posx += l;
+                            ps[p].posy += x;
+
+                            if(numplayers > 1)
+                            {
+                                ps[p].oposx = ps[p].posx;
+                                ps[p].oposy = ps[p].posy;
+                            }
+
+                            ps[p].bobposx += l;
+                            ps[p].bobposy += x;
+                        }
+
+                        if( po[p].os == s->sectnum )
+                        {
+                            po[p].ox += l;
+                            po[p].oy += x;
+                        }
+                    }
+
+                    j = headspritesect[s->sectnum];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS )
+                        {
+                            if(numplayers < 2)
+                            {
+                                hittype[j].bposx = sprite[j].x;
+                                hittype[j].bposy = sprite[j].y;
+                            }
+
+                            sprite[j].x += l;
+                            sprite[j].y += x;
+
+                            if(numplayers > 1)
+                            {
+                                hittype[j].bposx = sprite[j].x;
+                                hittype[j].bposy = sprite[j].y;
+                            }
+                        }
+                        j = nextspritesect[j];
+                    }
+
+                    ms(i);
+                    setsprite(i,s->x,s->y,s->z);
+
+                    if( (sc->floorz-sc->ceilingz) < (108<<8) )
+                    {
+                        if(ud.clipping == 0)
+                            for(p=connecthead;p>=0;p=connectpoint2[p])
+                                if(sprite[ps[p].i].extra > 0)
+                        {
+                            k = ps[p].cursectnum;
+                            updatesector(ps[p].posx,ps[p].posy,&k);
+                            if( ( k == -1 && ud.clipping == 0 ) || ( k == s->sectnum && ps[p].cursectnum != s->sectnum ) )
+                            {
+                                ps[p].posx = s->x;
+                                ps[p].posy = s->y;
+
+                                ps[p].oposx = ps[p].posx;
+                                ps[p].oposy = ps[p].posy;
+
+                                ps[p].cursectnum = s->sectnum;
+
+                                setsprite(ps[p].i,s->x,s->y,s->z);
+                                quickkill(&ps[p]);
+                            }
+                        }
+
+                        j = headspritesect[sprite[OW].sectnum];
+                        while(j >= 0)
+                        {
+                            l = nextspritesect[j];
+                            if (sprite[j].statnum == 1 && badguy(&sprite[j]) && sprite[j].picnum != SECTOREFFECTOR && sprite[j].picnum != LOCATORS )
+                            {
+            //                    if(sprite[j].sectnum != s->sectnum)
+                                {
+                                    k = sprite[j].sectnum;
+                                    updatesector(sprite[j].x,sprite[j].y,&k);
+                                    if( sprite[j].extra >= 0 && k == s->sectnum )
+                                    {
+                                        gutsdir(&sprite[j],JIBS6,24,myconnectindex);
+                                        spritesound(SQUISHED,j);
+                                        deletesprite(j);
+                                    }
+                                }
+
+                            }
+                            j = l;
+                        }
+                    }
+                }
+
+                break;
+
+
+            case 2://Quakes
+                if(t[4] > 0 && t[0] == 0 )
+                {
+                    if( t[4] < sh )
+                        t[4]++;
+                    else t[0] = 1;
+                }
+
+                if(t[0] > 0)
+                {
+                    t[0]++;
+
+                    s->xvel = 3;
+
+                    if(t[0] > 96)
+                    {
+                        t[0] = -1; //Stop the quake
+                        t[4] = -1;
+                        KILLIT(i);
+                    }
+                    else
+                    {
+                        if( (t[0]&31) ==  8 )
+                        {
+                            earthquaketime = 48;
+                            spritesound(EARTHQUAKE,ps[screenpeek].i);
+                        }
+
+                        if( klabs( sc->floorheinum-t[5] ) < 8 )
+                            sc->floorheinum = t[5];
+                        else sc->floorheinum += ( sgn(t[5]-sc->floorheinum)<<4 );
+                    }
+
+                    m = (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    x = (s->xvel*sintable[s->ang&2047])>>14;
+
+
+                    for(p=connecthead;p>=0;p=connectpoint2[p])
+                        if(ps[p].cursectnum == s->sectnum && ps[p].on_ground)
+                        {
+                            ps[p].posx += m;
+                            ps[p].posy += x;
+
+                            ps[p].bobposx += m;
+                            ps[p].bobposy += x;
+                        }
+
+                    j = headspritesect[s->sectnum];
+                    while(j >= 0)
+                    {
+                        nextj = nextspritesect[j];
+
+                        if (sprite[j].picnum != SECTOREFFECTOR)
+                        {
+                            sprite[j].x+=m;
+                            sprite[j].y+=x;
+                            setsprite(j,sprite[j].x,sprite[j].y,sprite[j].z);
+                        }
+                        j = nextj;
+                    }
+                    ms(i);
+                    setsprite(i,s->x,s->y,s->z);
+                }
+                break;
+
+            //Flashing sector lights after reactor EXPLOSION2
+
+            case 3:
+
+                if( t[4] == 0 ) break;
+                p = findplayer(s,&x);
+
+            //    if(t[5] > 0) { t[5]--; break; }
+
+                if( (global_random/(sh+1)&31) < 4 && !t[2])
+                {
+             //       t[5] = 4+(global_random&7);
+                    sc->ceilingpal = s->owner>>8;
+                    sc->floorpal = s->owner&0xff;
+                    t[0] = s->shade + (global_random&15);
+                }
+                else
+                {
+             //       t[5] = 4+(global_random&3);
+                    sc->ceilingpal = s->pal;
+                    sc->floorpal = s->pal;
+                    t[0] = t[3];
+                }
+
+                sc->ceilingshade = t[0];
+                sc->floorshade = t[0];
+
+                wal = &wall[sc->wallptr];
+
+                for(x=sc->wallnum;x > 0;x--,wal++)
+                {
+                    if( wal->hitag != 1 )
+                    {
+                        wal->shade = t[0];
+                        if((wal->cstat&2) && wal->nextwall >= 0)
+                        {
+                            wall[wal->nextwall].shade = wal->shade;
+                        }
+                    }
+                }
+
+                break;
+
+            case 4:
+
+                if((global_random/(sh+1)&31) < 4 )
+                {
+                    t[1] = s->shade + (global_random&15);//Got really bright
+                    t[0] = s->shade + (global_random&15);
+                    sc->ceilingpal = s->owner>>8;
+                    sc->floorpal = s->owner&0xff;
+                    j = 1;
+                }
+                else
+                {
+                    t[1] = t[2];
+                    t[0] = t[3];
+
+                    sc->ceilingpal = s->pal;
+                    sc->floorpal = s->pal;
+
+                    j = 0;
+                }
+
+                sc->floorshade = t[1];
+                sc->ceilingshade = t[1];
+
+                wal = &wall[sc->wallptr];
+
+                for(x=sc->wallnum;x > 0; x--,wal++)
+                {
+                    if(j) wal->pal = (s->owner&0xff);
+                    else wal->pal = s->pal;
+
+                    if( wal->hitag != 1 )
+                    {
+                        wal->shade = t[0];
+                        if((wal->cstat&2) && wal->nextwall >= 0)
+                            wall[wal->nextwall].shade = wal->shade;
+                    }
+                }
+
+                j = headspritesect[SECT];
+                while(j >= 0)
+                {
+                    if(sprite[j].cstat&16)
+                    {
+                        if (sc->ceilingstat&1)
+                            sprite[j].shade = sc->ceilingshade;
+                        else sprite[j].shade = sc->floorshade;
+                    }
+
+                    j = nextspritesect[j];
+                }
+
+                if(t[4]) KILLIT(i);
+
+                break;
+
+            //BOSS
+            case 5:
+                p = findplayer(s,&x);
+                if(x < 8192)
+                {
+                    j = s->ang;
+                    s->ang = getangle(s->x-ps[p].posx,s->y-ps[p].posy);
+                    shoot(i,FIRELASER);
+                    s->ang = j;
+                }
+
+                if(s->owner==-1) //Start search
+                {
+                    t[4]=0;
+                    l = 0x7fffffff;
+                    while(1) //Find the shortest dist
+                    {
+                        s->owner = LocateTheLocator((short)t[4],-1); //t[0] hold sectnum
+
+                        if(s->owner==-1) break;
+
+                        m = ldist(&sprite[ps[p].i],&sprite[s->owner]);
+
+                        if(l > m)
+                        {
+                            q = s->owner;
+                            l = m;
+                        }
+
+                        t[4]++;
+                    }
+
+                    s->owner = q;
+                    s->zvel = ksgn(sprite[q].z-s->z)<<4;
+                }
+
+                if(ldist(&sprite[s->owner],s) < 1024)
+                {
+                    short ta;
+                    ta = s->ang;
+                    s->ang = getangle(ps[p].posx-s->x,ps[p].posy-s->y);
+                    s->ang = ta;
+                    s->owner = -1;
+                    goto BOLT;
+
+                }
+                else s->xvel=256;
+
+                x = getangle(sprite[s->owner].x-s->x,sprite[s->owner].y-s->y);
+                q = getincangle(s->ang,x)>>3;
+                s->ang += q;
+                
+                if(rnd(32))
+                {
+                    t[2]+=q;
+                    sc->ceilingshade = 127;
+                }
+                else
+                {
+                    t[2] +=
+                        getincangle(t[2]+512,getangle(ps[p].posx-s->x,ps[p].posy-s->y))>>2;
+                    sc->ceilingshade = 0;
+                }
+                IFHIT
+                {
+                    t[3]++;
+                    if(t[3] == 5)
+                    {
+                        s->zvel += 1024;
+                        FTA(7,&ps[myconnectindex],0);
+                    }
+                }
+
+                s->z += s->zvel;
+                sc->ceilingz += s->zvel;
+                sector[t[0]].ceilingz += s->zvel;
+                ms(i);
+                setsprite(i,s->x,s->y,s->z);
+                break;
+
+            
+            case 8:
+            case 9:
+
+                // work only if its moving
+
+                j = -1;
+
+                if(hittype[i].temp_data[4])
+                {
+                    hittype[i].temp_data[4]++;
+                    if( hittype[i].temp_data[4] > 8 ) KILLIT(i);
+                    j = 1;
+                }
+                else j = getanimationgoal(&sc->ceilingz);
+
+                if( j >= 0 )
+                {
+                    short sn;
+
+                    if( (sc->lotag&0x8000) || hittype[i].temp_data[4] )
+                        x = -t[3];
+                    else
+                        x = t[3];
+
+                    if ( st == 9 ) x = -x;
+
+                    j = headspritestat[3];
+                    while(j >= 0)
+                    {
+                        if( ((sprite[j].lotag) == st ) && (sprite[j].hitag) == sh )
+                        {
+                            sn = sprite[j].sectnum;
+                            m = sprite[j].shade;
+
+                            wal = &wall[sector[sn].wallptr];
+
+                            for(l=sector[sn].wallnum;l>0;l--,wal++)
+                            {
+                                if( wal->hitag != 1 )
+                                {
+                                    wal->shade+=x;
+
+                                    if(wal->shade < m)
+                                        wal->shade = m;
+                                    else if(wal->shade > hittype[j].temp_data[2])
+                                        wal->shade = hittype[j].temp_data[2];
+
+                                    if(wal->nextwall >= 0)
+                                        if(wall[wal->nextwall].hitag != 1)
+                                            wall[wal->nextwall].shade = wal->shade;
+                                }
+                            }
+
+                            sector[sn].floorshade   += x;
+                            sector[sn].ceilingshade += x;
+
+                            if(sector[sn].floorshade < m)
+                                sector[sn].floorshade = m;
+                            else if(sector[sn].floorshade > hittype[j].temp_data[0])
+                                sector[sn].floorshade = hittype[j].temp_data[0];
+
+                            if(sector[sn].ceilingshade < m)
+                                sector[sn].ceilingshade = m;
+                            else if(sector[sn].ceilingshade > hittype[j].temp_data[1])
+                                sector[sn].ceilingshade = hittype[j].temp_data[1];
+
+                        }
+                        j = nextspritestat[j];
+                    }
+                }
+                break;
+            case 10:
+
+                if( (sc->lotag&0xff) == 27 || ( sc->floorz > sc->ceilingz && (sc->lotag&0xff) != 23 ) || sc->lotag == (short) 32791 )
+                {
+                    j = 1;
+
+                    if( (sc->lotag&0xff) != 27)
+                        for(p=connecthead;p>=0;p=connectpoint2[p])
+                            if( sc->lotag != 30 && sc->lotag != 31 && sc->lotag != 0 )
+                                if(s->sectnum == sprite[ps[p].i].sectnum)
+                                    j = 0;
+
+                    if(j == 1)
+                    {
+                        if(t[0] > sh )
+                            switch(sector[s->sectnum].lotag)
+                            {
+                                case 20:
+                                case 21:
+                                case 22:
+                                case 26:
+                                  if( getanimationgoal(&sector[s->sectnum].ceilingz) >= 0 )
+                                      break;
+                                default:
+                                  activatebysector(s->sectnum,i);
+                                  t[0] = 0;
+                                  break;
+                            }
+                        else t[0]++;
+                    }
+                }
+                else t[0]=0;
+                break;
+            case 11: //Swingdoor
+
+                if( t[5] > 0)
+                {
+                    t[5]--;
+                    break;
+                }
+
+                if( t[4] )
+                {
+                    short startwall,endwall;
+
+                    startwall = sc->wallptr;
+                    endwall = startwall+sc->wallnum;
+
+                    for(j=startwall;j<endwall;j++)
+                    {
+                        k = headspritestat[1];
+                        while(k >= 0)
+                        {
+                            if( sprite[k].extra > 0 && badguy(&sprite[k]) && clipinsidebox(sprite[k].x,sprite[k].y,j,256L) == 1 )
+                                goto BOLT;
+                            k = nextspritestat[k];
+                        }
+
+                        k = headspritestat[10];
+                        while(k >= 0)
+                        {
+                            if( sprite[k].owner >= 0 && clipinsidebox(sprite[k].x,sprite[k].y,j,144L) == 1 )
+                            {
+                                t[5] = 8; // Delay
+                                k = (SP>>3)*t[3];
+                                t[2]-=k;
+                                t[4]-=k;
+                                ms(i);
+                                setsprite(i,s->x,s->y,s->z);
+                                goto BOLT;
+                            }
+                            k = nextspritestat[k];
+                        }
+                    }
+
+                    k = (SP>>3)*t[3];
+                    t[2]+=k;
+                    t[4]+=k;
+                    ms(i);
+                    setsprite(i,s->x,s->y,s->z);
+
+                    if(t[4] <= -511 || t[4] >= 512)
+                    {
+                        t[4] = 0;
+                        t[2] &= 0xffffff00;
+                        ms(i);
+                        setsprite(i,s->x,s->y,s->z);
+                        break;
+                    }
+                }
+                break;
+            case 12:
+                if( t[0] == 3 || t[3] == 1 ) //Lights going off
+                {
+                    sc->floorpal = 0;
+                    sc->ceilingpal = 0;
+
+                    wal = &wall[sc->wallptr];
+                    for(j = sc->wallnum;j > 0; j--, wal++)
+                        if(wal->hitag != 1)
+                        {
+                            wal->shade = t[1];
+                            wal->pal = 0;
+                        }
+
+                    sc->floorshade = t[1];
+                    sc->ceilingshade = t[2];
+                    t[0]=0;
+
+                    j = headspritesect[SECT];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].cstat&16)
+                        {
+                            if (sc->ceilingstat&1)
+                                sprite[j].shade = sc->ceilingshade;
+                            else sprite[j].shade = sc->floorshade;
+                        }
+                        j = nextspritesect[j];
+
+                    }
+
+                    if(t[3] == 1) KILLIT(i);
+                }
+                if( t[0] == 1 ) //Lights flickering on
+                {
+                    if( sc->floorshade > s->shade )
+                    {
+                        sc->floorpal = s->pal;
+                        sc->ceilingpal = s->pal;
+
+                        sc->floorshade -= 2;
+                        sc->ceilingshade -= 2;
+
+                        wal = &wall[sc->wallptr];
+                        for(j=sc->wallnum;j>0;j--,wal++)
+                            if(wal->hitag != 1)
+                            {
+                                wal->pal = s->pal;
+                                wal->shade -= 2;
+                            }
+                    }
+                    else t[0] = 2;
+
+                    j = headspritesect[SECT];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].cstat&16)
+                        {
+                            if (sc->ceilingstat&1)
+                                sprite[j].shade = sc->ceilingshade;
+                            else sprite[j].shade = sc->floorshade;
+                        }
+                        j = nextspritesect[j];
+                    }
+                }
+                break;
+
+
+            case 13:
+                if( t[2] )
+                {
+                    j = (SP<<5)|1;
+
+                    if( s->ang == 512 )
+                    {
+                        if( s->owner )
+                        {
+                            if( klabs(t[0]-sc->ceilingz) >= j )
+                                sc->ceilingz += sgn(t[0]-sc->ceilingz)*j;
+                            else sc->ceilingz = t[0];
+                        }
+                        else
+                        {
+                            if( klabs(t[1]-sc->floorz) >= j )
+                                sc->floorz += sgn(t[1]-sc->floorz)*j;
+                            else sc->floorz = t[1];
+                        }
+                    }
+                    else
+                    {
+                        if( klabs(t[1]-sc->floorz) >= j )
+                            sc->floorz += sgn(t[1]-sc->floorz)*j;
+                        else sc->floorz = t[1];
+                        if( klabs(t[0]-sc->ceilingz) >= j )
+                            sc->ceilingz += sgn(t[0]-sc->ceilingz)*j;
+                        sc->ceilingz = t[0];
+                    }
+
+                    if( t[3] == 1 )
+                    {
+                        //Change the shades
+
+                        t[3]++;
+                        sc->ceilingstat ^= 1;
+
+                        if(s->ang == 512)
+                        {
+                            wal = &wall[sc->wallptr];
+                            for(j=sc->wallnum;j>0;j--,wal++)
+                                wal->shade = s->shade;
+
+                            sc->floorshade = s->shade;
+
+                            if(ps[0].one_parallax_sectnum >= 0)
+                            {
+                                sc->ceilingpicnum =
+                                    sector[ps[0].one_parallax_sectnum].ceilingpicnum;
+                                sc->ceilingshade  =
+                                    sector[ps[0].one_parallax_sectnum].ceilingshade;
+                            }
+                        }
+                    }
+                    t[2]++;
+                    if(t[2] > 256)
+                        KILLIT(i);
+                }
+
+
+                if( t[2] == 4 && s->ang != 512)
+                    for(x=0;x<7;x++) RANDOMSCRAP;
+                break;
+
+
+            case 15:
+
+                if(t[4])
+                {
+                    s->xvel = 16;
+
+                    if(t[4] == 1) //Opening
+                    {
+                        if( t[3] >= (SP>>3) )
+                        {
+                            t[4] = 0; //Turn off the sliders
+                            callsound(s->sectnum,i);
+                            break;
+                        }
+                        t[3]++;
+                    }
+                    else if(t[4] == 2)
+                    {
+                        if(t[3]<1)
+                        {
+                            t[4] = 0;
+                            callsound(s->sectnum,i);
+                            break;
+                        }
+                        t[3]--;
+                    }
+
+                    ms(i);
+                    setsprite(i,s->x,s->y,s->z);
+                }
+                break;
+
+            case 16: //Reactor
+
+                t[2]+=32;
+                if(sc->floorz<sc->ceilingz) s->shade=0;
+
+                else if( sc->ceilingz < t[3] )
+                {
+
+                    //The following code check to see if
+                    //there is any other sprites in the sector.
+                    //If there isn't, then kill this sectoreffector
+                    //itself.....
+
+                    j = headspritesect[s->sectnum];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].picnum == REACTOR || sprite[j].picnum == REACTOR2)
+                            break;
+                        j = nextspritesect[j];
+                    }
+                    if(j == -1) { KILLIT(i); }
+                    else s->shade=1;
+                }
+
+                if(s->shade) sc->ceilingz+=1024;
+                else sc->ceilingz-=512;
+
+                ms(i);
+                setsprite(i,s->x,s->y,s->z);
+
+                break;
+
+            case 17:
+
+                q = t[0]*(SP<<2);
+
+                sc->ceilingz += q;
+                sc->floorz += q;
+
+                j = headspritesect[s->sectnum];
+                while(j >= 0)
+                {
+                    if(sprite[j].statnum == 10 && sprite[j].owner >= 0)
+                    {
+                        p = sprite[j].yvel;
+                        if(numplayers < 2)
+                            ps[p].oposz = ps[p].posz;
+                        ps[p].posz += q;
+                        ps[p].truefz += q;
+                        ps[p].truecz += q;
+                        if(numplayers > 1)
+                            ps[p].oposz = ps[p].posz;
+                    }
+                    if( sprite[j].statnum != 3 )
+                    {
+                        hittype[j].bposz = sprite[j].z;
+                        sprite[j].z += q;
+                    }
+
+                    hittype[j].floorz = sc->floorz;
+                    hittype[j].ceilingz = sc->ceilingz;
+
+                    j = nextspritesect[j];
+                }
+
+                if( t[0] )                if(t[0]) //If in motion
+                {
+                    if( klabs(sc->floorz-t[2]) <= SP)
+                    {
+                        activatewarpelevators(i,0);
+                        break;
+                    }
+
+                    if(t[0]==-1)
+                    {
+                        if( sc->floorz > t[3] )
+                            break;
+                    }
+                    else if( sc->ceilingz < t[4] ) break;
+
+                    if( t[1] == 0 ) break;
+                    t[1] = 0;
+
+                    j = headspritestat[3];
+                    while(j >= 0)
+                    {
+                                if( i != j && (sprite[j].lotag) == 17)
+                                    if( (sc->hitag-t[0]) ==
+                                        (sector[sprite[j].sectnum].hitag)
+                                        && sh == (sprite[j].hitag))
+                                            break;
+                                j = nextspritestat[j];
+                    }
+
+                    if(j == -1) break;
+
+                    k = headspritesect[s->sectnum];
+                    while(k >= 0)
+                    {
+                        nextk = nextspritesect[k];
+
+                        if(sprite[k].statnum == 10 && sprite[k].owner >= 0)
+                        {
+                            p = sprite[k].yvel;
+
+                            ps[p].posx += sprite[j].x-s->x;
+                            ps[p].posy += sprite[j].y-s->y;
+                            ps[p].posz = sector[sprite[j].sectnum].floorz-(sc->floorz-ps[p].posz);
+
+                            hittype[k].floorz = sector[sprite[j].sectnum].floorz;
+                            hittype[k].ceilingz = sector[sprite[j].sectnum].ceilingz;
+
+                            ps[p].bobposx = ps[p].oposx = ps[p].posx;
+                            ps[p].bobposy = ps[p].oposy = ps[p].posy;
+                            ps[p].oposz = ps[p].posz;
+
+                            ps[p].truefz = hittype[k].floorz;
+                            ps[p].truecz = hittype[k].ceilingz;
+                            ps[p].bobcounter = 0;
+
+                            changespritesect(k,sprite[j].sectnum);
+                            ps[p].cursectnum = sprite[j].sectnum;
+                        }
+                        else if( sprite[k].statnum != 3 )
+                        {
+                            sprite[k].x +=
+                                sprite[j].x-s->x;
+                            sprite[k].y +=
+                                sprite[j].y-s->y;
+                            sprite[k].z = sector[sprite[j].sectnum].floorz-
+                                (sc->floorz-sprite[k].z);
+
+                            hittype[k].bposx = sprite[k].x;
+                            hittype[k].bposy = sprite[k].y;
+                            hittype[k].bposz = sprite[k].z;
+
+                            changespritesect(k,sprite[j].sectnum);
+                            setsprite(k,sprite[k].x,sprite[k].y,sprite[k].z);
+
+                            hittype[k].floorz = sector[sprite[j].sectnum].floorz;
+                            hittype[k].ceilingz = sector[sprite[j].sectnum].ceilingz;
+
+                        }
+                        k = nextk;
+                    }
+                }
+                break;
+
+            case 18:
+                if(t[0])
+                {
+                    if(s->pal)
+                    {
+                        if(s->ang == 512)
+                        {
+                            sc->ceilingz -= sc->extra;
+                            if(sc->ceilingz <= t[1])
+                            {
+                                sc->ceilingz = t[1];
+                                KILLIT(i);
+                            }
+                        }
+                        else
+                        {
+                            sc->floorz += sc->extra;
+                                j = headspritesect[s->sectnum];
+                                while(j >= 0)
+                                {
+                                    if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                        if( ps[sprite[j].yvel].on_ground == 1 )
+                                            ps[sprite[j].yvel].posz += sc->extra;
+                                    if( sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4)
+                                    {
+                                        hittype[j].bposz = sprite[j].z += sc->extra;
+                                        hittype[j].floorz = sc->floorz;
+                                    }
+                                    j = nextspritesect[j];
+                                }
+                            if(sc->floorz >= t[1])
+                            {
+                                sc->floorz = t[1];
+                                KILLIT(i);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        if(s->ang == 512)
+                        {
+                            sc->ceilingz += sc->extra;
+                            if(sc->ceilingz >= s->z)
+                            {
+                                sc->ceilingz = s->z;
+                                KILLIT(i);
+                            }
+                        }
+                        else
+                        {
+                            sc->floorz -= sc->extra;
+                                j = headspritesect[s->sectnum];
+                                while(j >= 0)
+                                {
+                                    if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                        if( ps[sprite[j].yvel].on_ground == 1 )
+                                            ps[sprite[j].yvel].posz -= sc->extra;
+                                    if( sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4)
+                                    {
+                                        hittype[j].bposz = sprite[j].z -= sc->extra;
+                                        hittype[j].floorz = sc->floorz;
+                                    }
+                                    j = nextspritesect[j];
+                                }
+                            if(sc->floorz <= s->z)
+                            {
+                                sc->floorz = s->z;
+                                KILLIT(i);
+                            }
+                        }
+                    }
+
+                    t[2]++;
+                    if(t[2] >= s->hitag)
+                    {
+                        t[2] = 0;
+                        t[0] = 0;
+                    }
+                }
+                break;
+
+            case 19: //Battlestar galactia shields
+
+                if(t[0])
+                {
+                    if(t[0] == 1)
+                    {
+                        t[0]++;
+                        x = sc->wallptr;
+                        q = x+sc->wallnum;
+                        for(j=x;j<q;j++)
+                            if(wall[j].overpicnum == BIGFORCE)
+                            {
+                                wall[j].cstat &= (128+32+8+4+2);
+                                wall[j].overpicnum = 0;
+                                if(wall[j].nextwall >= 0)
+                                {
+                                    wall[wall[j].nextwall].overpicnum = 0;
+                                    wall[wall[j].nextwall].cstat &= (128+32+8+4+2);
+                                }
+                            }
+                    }
+
+                    if(sc->ceilingz < sc->floorz)
+                        sc->ceilingz += SP;
+                    else
+                    {
+                        sc->ceilingz = sc->floorz;
+
+                        j = headspritestat[3];
+                        while(j >= 0)
+                        {
+                            if(sprite[j].lotag == 0 && sprite[j].hitag==sh)
+                            {
+                                q = sprite[sprite[j].owner].sectnum;
+                                sector[sprite[j].sectnum].floorpal = sector[sprite[j].sectnum].ceilingpal =
+                                        sector[q].floorpal;
+                                sector[sprite[j].sectnum].floorshade = sector[sprite[j].sectnum].ceilingshade =
+                                    sector[q].floorshade;
+
+                                hittype[sprite[j].owner].temp_data[0] = 2;
+                            }
+                            j = nextspritestat[j];
+                        }
+                        KILLIT(i);
+                    }
+                }
+                else //Not hit yet
+                {
+                    IFHITSECT
+                    {
+                        FTA(8,&ps[myconnectindex],0);
+
+                        l = headspritestat[3];
+                        while(l >= 0)
+                        {
+                            x = sprite[l].lotag&0x7fff;
+                            switch( x )
+                            {
+                                case 0:
+                                    if(sprite[l].hitag == sh)
+                                    {
+                                        q = sprite[l].sectnum;
+                                        sector[q].floorshade =
+                                            sector[q].ceilingshade =
+                                                sprite[sprite[l].owner].shade;
+                                        sector[q].floorpal =
+                                            sector[q].ceilingpal =
+                                                sprite[sprite[l].owner].pal;
+                                    }
+                                    break;
+
+                                case 1:
+                                case 12:
+//                                case 18:
+                                case 19:
+
+                                    if( sh == sprite[l].hitag )
+                                        if( hittype[l].temp_data[0] == 0 )
+                                        {
+                                            hittype[l].temp_data[0] = 1; //Shut them all on
+                                            sprite[l].owner = i;
+                                        }
+
+                                    break;
+                            }
+                            l = nextspritestat[l];
+                        }
+                    }
+                }
+
+                break;
+
+            case 20: //Extend-o-bridge
+
+                if( t[0] == 0 ) break;
+                if( t[0] == 1 ) s->xvel = 8;
+                else s->xvel = -8;
+
+                if( s->xvel ) //Moving
+                {
+                    x = (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                    l = (s->xvel*sintable[s->ang&2047])>>14;
+
+                    t[3] += s->xvel;
+
+                    s->x += x;
+                    s->y += l;
+
+                    if( t[3] <= 0 || (t[3]>>6) >= (SP>>6) )
+                    {
+                        s->x -= x;
+                        s->y -= l;
+                        t[0] = 0;
+                        callsound(s->sectnum,i);
+                        break;
+                    }
+
+                    j = headspritesect[s->sectnum];
+                    while(j >= 0)
+                    {
+                        nextj = nextspritesect[j];
+
+                        if( sprite[j].statnum != 3 && sprite[j].zvel == 0)
+                        {
+                            sprite[j].x += x;
+                            sprite[j].y += l;
+                            setsprite(j,sprite[j].x,sprite[j].y,sprite[j].z);
+                            if( sector[sprite[j].sectnum].floorstat&2 )
+                                if(sprite[j].statnum == 2)
+                                    makeitfall(j);
+                        }
+                        j = nextj;
+                    }
+
+                    dragpoint((short)t[1],wall[t[1]].x+x,wall[t[1]].y+l);
+                    dragpoint((short)t[2],wall[t[2]].x+x,wall[t[2]].y+l);
+
+                    for(p=connecthead;p>=0;p=connectpoint2[p])
+                        if(ps[p].cursectnum == s->sectnum && ps[p].on_ground)
+                        {
+                            ps[p].posx += x;
+                            ps[p].posy += l;
+
+                            ps[p].oposx = ps[p].posx;
+                            ps[p].oposy = ps[p].posy;
+
+                            setsprite(ps[p].i,ps[p].posx,ps[p].posy,ps[p].posz+PHEIGHT);
+                        }
+
+                    sc->floorxpanning-=x>>3;
+                    sc->floorypanning-=l>>3;
+
+                    sc->ceilingxpanning-=x>>3;
+                    sc->ceilingypanning-=l>>3;
+                }
+
+                break;
+
+            case 21: // Cascading effect
+
+                if( t[0] == 0 ) break;
+
+                if( s->ang == 1536 )
+                    l = (long) &sc->ceilingz;
+                else
+                    l = (long) &sc->floorz;
+
+                if( t[0] == 1 ) //Decide if the s->sectnum should go up or down
+                {
+                    s->zvel = ksgn(s->z-*(long *)l) * (SP<<4);
+                    t[0]++;
+                }
+
+                if( sc->extra == 0 )
+                {
+                    *(long *)l += s->zvel;
+
+                    if(klabs(*(long *)l-s->z) < 1024)
+                    {
+                        *(long *)l = s->z;
+                        KILLIT(i); //All done
+                    }
+                }
+                else sc->extra--;
+                break;
+
+            case 22:
+
+                if( t[1] )
+                {
+                    if(getanimationgoal(&sector[t[0]].ceilingz) >= 0)
+                        sc->ceilingz += sc->extra*9;
+                    else t[1] = 0;
+                }
+                break;
+
+            case 24:
+            case 34:
+
+                if(t[4]) break;
+
+                x = (SP*sintable[(s->ang+512)&2047])>>18;
+                l = (SP*sintable[s->ang&2047])>>18;
+
+                k = 0;
+
+                j = headspritesect[s->sectnum];
+                while(j >= 0)
+                {
+                    nextj = nextspritesect[j];
+                    if(sprite[j].zvel >= 0)
+                        switch(sprite[j].statnum)
+                    {
+                        case 5:
+                            switch(sprite[j].picnum)
+                            {
+                                case BLOODPOOL:
+                                case PUKE:
+                                case FOOTPRINTS:
+                                case FOOTPRINTS2:
+                                case FOOTPRINTS3:
+                                case FOOTPRINTS4:
+                                case BULLETHOLE:
+                                case BLOODSPLAT1:
+                                case BLOODSPLAT2:
+                                case BLOODSPLAT3:
+                                case BLOODSPLAT4:
+                                    sprite[j].xrepeat = sprite[j].yrepeat = 0;
+                                    j = nextj;
+                                    continue;
+                                case LASERLINE:
+                                    j = nextj;
+                                    continue;
+                            }
+                        case 6:
+                            if(sprite[j].picnum == TRIPBOMB) break;
+                        case 1:
+                        case 0:
+                            if(
+                                sprite[j].picnum == BOLT1 ||
+                                sprite[j].picnum == BOLT1+1 ||
+                                sprite[j].picnum == BOLT1+2 ||
+                                sprite[j].picnum == BOLT1+3 ||
+                                sprite[j].picnum == SIDEBOLT1 ||
+                                sprite[j].picnum == SIDEBOLT1+1 ||
+                                sprite[j].picnum == SIDEBOLT1+2 ||
+                                sprite[j].picnum == SIDEBOLT1+3 ||
+                                wallswitchcheck(j)
+                              )
+                              break;
+
+                            if( !(sprite[j].picnum >= CRANE && sprite[j].picnum <= (CRANE+3)))
+                            {
+                                if( sprite[j].z > (hittype[j].floorz-(16<<8)) )
+                                {
+                                    hittype[j].bposx = sprite[j].x;
+                                    hittype[j].bposy = sprite[j].y;
+
+                                    sprite[j].x += x>>2;
+                                    sprite[j].y += l>>2;
+
+                                    setsprite(j,sprite[j].x,sprite[j].y,sprite[j].z);
+
+                                    if( sector[sprite[j].sectnum].floorstat&2 )
+                                        if(sprite[j].statnum == 2)
+                                            makeitfall(j);
+                                }
+                            }
+                            break;
+                    }
+                    j = nextj;
+                }
+
+                p = myconnectindex;
+                if(ps[p].cursectnum == s->sectnum && ps[p].on_ground)
+                    if( klabs(ps[p].posz-ps[p].truefz) < PHEIGHT+(9<<8) )
+                {
+                    fricxv += x<<3;
+                    fricyv += l<<3;
+                }
+
+                sc->floorxpanning += SP>>7;
+
+            break;
+
+            case 35:
+                if(sc->ceilingz > s->z)
+                    for(j = 0;j < 8;j++)
+                {
+                    s->ang += TRAND&511;
+                    k = spawn(i,SMALLSMOKE);
+                    sprite[k].xvel = 96+(TRAND&127);
+                    ssp(k,CLIPMASK0);
+                    setsprite(k,sprite[k].x,sprite[k].y,sprite[k].z);
+                    if( rnd(16) )
+                        spawn(i,EXPLOSION2);
+                }
+
+                switch(t[0])
+                {
+                    case 0:
+                        sc->ceilingz += s->yvel;
+                        if(sc->ceilingz > sc->floorz)
+                            sc->floorz = sc->ceilingz;
+                        if(sc->ceilingz > s->z+(32<<8))
+                            t[0]++;
+                        break;
+                    case 1:
+                        sc->ceilingz-=(s->yvel<<2);
+                        if(sc->ceilingz < t[4])
+                        {
+                            sc->ceilingz = t[4];
+                            t[0] = 0;
+                        }
+                        break;
+                }
+                break;
+
+            case 25: //PISTONS
+
+                if( t[4] == 0 ) break;
+
+                if(sc->floorz <= sc->ceilingz)
+                    s->shade = 0;
+                else if( sc->ceilingz <= t[3])
+                    s->shade = 1;
+
+                if(s->shade)
+                {
+                    sc->ceilingz += SP<<4;
+                    if(sc->ceilingz > sc->floorz)
+                        sc->ceilingz = sc->floorz;
+                }
+                else
+                {
+                    sc->ceilingz   -= SP<<4;
+                    if(sc->ceilingz < t[3])
+                        sc->ceilingz = t[3];
+                }
+
+                break;
+
+            case 26:
+
+                s->xvel = 32;
+                l = (s->xvel*sintable[(s->ang+512)&2047])>>14;
+                x = (s->xvel*sintable[s->ang&2047])>>14;
+
+                s->shade++;
+                if( s->shade > 7 )
+                {
+                    s->x = t[3];
+                    s->y = t[4];
+                    sc->floorz -= ((s->zvel*s->shade)-s->zvel);
+                    s->shade = 0;
+                }
+                else
+                    sc->floorz += s->zvel;
+
+                j = headspritesect[s->sectnum];
+                while( j >= 0 )
+                {
+                    nextj = nextspritesect[j];
+                    if(sprite[j].statnum != 3 && sprite[j].statnum != 10)
+                    {
+                        hittype[j].bposx = sprite[j].x;
+                        hittype[j].bposy = sprite[j].y;
+
+                        sprite[j].x += l;
+                        sprite[j].y += x;
+
+                        sprite[j].z += s->zvel;
+                        setsprite(j,sprite[j].x,sprite[j].y,sprite[j].z);
+                    }
+                    j = nextj;
+                }
+
+                p = myconnectindex;
+                if(sprite[ps[p].i].sectnum == s->sectnum && ps[p].on_ground)
+                {
+                    fricxv += l<<5;
+                    fricyv += x<<5;
+                }
+
+                for(p = connecthead;p >= 0;p = connectpoint2[p])
+                    if(sprite[ps[p].i].sectnum == s->sectnum && ps[p].on_ground)
+                        ps[p].posz += s->zvel;
+
+                ms(i);
+                setsprite(i,s->x,s->y,s->z);
+
+                break;
+
+
+            case 27:
+
+                if(ud.recstat == 0) break;
+
+                hittype[i].tempang = s->ang;
+
+                p = findplayer(s,&x);
+
+				// FIX_00013: 3rd person camera view during demo playback can now be 
+				// turned off (no need to use hacked no-camera maps anymore)                
+                if( /*sprite[ps[p].i].extra > 0 && myconnectindex == screenpeek */ 0) // xduke remove camera view for the 1st player (notice myconnectindex is == 1 when replaying, dosent matter who recorded)
+                {
+                    if( t[0] < 0 )
+                    {
+                        ud.camerasprite = i;
+                        t[0]++;
+                    }
+                    else if(ud.recstat == 2 && ps[p].newowner == -1)
+                    {
+                        if(cansee(s->x,s->y,s->z,SECT,ps[p].posx,ps[p].posy,ps[p].posz,ps[p].cursectnum))
+                        {
+                            if(x < (unsigned)sh)
+                            {
+                                ud.camerasprite = i;
+                                t[0] = 999;
+                                s->ang += getincangle(s->ang,getangle(ps[p].posx-s->x,ps[p].posy-s->y))>>3;
+                                SP = 100+((s->z-ps[p].posz)/257);
+
+                            }
+                            else if(t[0] == 999)
+                            {
+                                if(ud.camerasprite == i)
+                                    t[0] = 0;
+                                else t[0] = -10;
+                                ud.camerasprite = i;
+
+                            }
+                        }
+                        else
+                        {
+                            s->ang = getangle(ps[p].posx-s->x,ps[p].posy-s->y);
+
+                            if(t[0] == 999)
+                            {
+                                if(ud.camerasprite == i)
+                                    t[0] = 0;
+                                else t[0] = -20;
+                                ud.camerasprite = i;
+                            }
+                        }
+                    }
+                }
+                break;
+            case 28:
+                if(t[5] > 0)
+                {
+                    t[5]--;
+                    break;
+                }
+
+                if(T1 == 0)
+                {
+                    p = findplayer(s,&x);
+                    if( x > 15500 )
+                        break;
+                    T1 = 1;
+                    T2 = 64 + (TRAND&511);
+                    T3 = 0;
+                }
+                else
+                {
+                    T3++;
+                    if(T3 > T2)
+                    {
+                        T1 = 0;
+                        ps[screenpeek].visibility = ud.const_visibility;
+                        break;
+                    }
+                    else if( T3 == (T2>>1) )
+                        spritesound(THUNDER,i);
+                    else if(T3 == (T2>>3) )
+                        spritesound(LIGHTNING_SLAP,i);
+                    else if( T3 == (T2>>2) )
+                    {
+                        j = headspritestat[0];
+                        while(j >= 0)
+                        {
+                            if( sprite[j].picnum == NATURALLIGHTNING && sprite[j].hitag == s->hitag)
+                                sprite[j].cstat |= 32768;
+                            j = nextspritestat[j];
+                        }
+                    }
+                    else if(T3 > (T2>>3) && T3 < (T2>>2) )
+                    {
+                        if( cansee(s->x,s->y,s->z,s->sectnum,ps[screenpeek].posx,ps[screenpeek].posy,ps[screenpeek].posz,ps[screenpeek].cursectnum ) )
+                            j = 1;
+                        else j = 0;
+
+                        if( rnd(192) && (T3&1) )
+                        {
+                            if(j)
+                                ps[screenpeek].visibility = 0;
+                        }
+                        else if(j)
+                            ps[screenpeek].visibility = ud.const_visibility;
+
+                        j = headspritestat[0];
+                        while(j >= 0)
+                        {
+                            if( sprite[j].picnum == NATURALLIGHTNING && sprite[j].hitag == s->hitag)
+                            {
+                                if ( rnd(32) && (T3&1) )
+                                {
+                                    sprite[j].cstat &= 32767;
+                                    spawn(j,SMALLSMOKE);
+
+                                    p = findplayer(s,&x);
+                                    x = ldist(&sprite[ps[p].i], &sprite[j]);
+                                    if( x < 768 )
+                                    {
+                                        if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                            spritesound(DUKE_LONGTERM_PAIN,ps[p].i);
+                                        spritesound(SHORT_CIRCUIT,ps[p].i);
+                                        sprite[ps[p].i].extra -= 8+(TRAND&7);
+                                        ps[p].pals_time = 32;
+                                        ps[p].pals[0] = 16;
+                                        ps[p].pals[1] = 0;
+                                        ps[p].pals[2] = 0;
+                                    }
+                                    break;
+                                }
+                                else sprite[j].cstat |= 32768;
+                            }
+
+                            j = nextspritestat[j];
+                        }
+                    }
+                }
+                break;
+            case 29:
+                s->hitag += 64;
+                l = mulscale12((long)s->yvel,sintable[s->hitag&2047]);
+                sc->floorz = s->z + l;
+                break;
+            case 31: // True Drop Floor
+                if(t[0] == 1)
+                {
+                    // Choose dir
+
+                    if(t[3] > 0)
+                    {
+                        t[3]--;
+                        break;
+                    }
+
+                    if(t[2] == 1) // Retract
+                    {
+                        if(SA != 1536)
+                        {
+                            if( klabs( sc->floorz - s->z ) < SP )
+                            {
+                                sc->floorz = s->z;
+                                t[2] = 0;
+                                t[0] = 0;
+                                t[3] = s->hitag;
+                                callsound(s->sectnum,i);
+                            }
+                            else
+                            {
+                                l = sgn(s->z-sc->floorz)*SP;
+                                sc->floorz += l;
+
+                                j = headspritesect[s->sectnum];
+                                while(j >= 0)
+                                {
+                                    if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                        if( ps[sprite[j].yvel].on_ground == 1 )
+                                            ps[sprite[j].yvel].posz += l;
+                                    if( sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4)
+                                    {
+                                        hittype[j].bposz = sprite[j].z += l;
+                                        hittype[j].floorz = sc->floorz;
+                                    }
+                                    j = nextspritesect[j];
+                                }
+                            }
+                        }
+                        else
+                        {
+                            if( klabs( sc->floorz - t[1] ) < SP )
+                            {
+                                sc->floorz = t[1];
+                                callsound(s->sectnum,i);
+                                t[2] = 0;
+                                t[0] = 0;
+                                t[3] = s->hitag;
+                            }
+                            else
+                            {
+                                l = sgn(t[1]-sc->floorz)*SP;
+                                sc->floorz += l;
+
+                                j = headspritesect[s->sectnum];
+                                while(j >= 0)
+                                {
+                                    if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                        if( ps[sprite[j].yvel].on_ground == 1 )
+                                            ps[sprite[j].yvel].posz += l;
+                                    if( sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4 )
+                                    {
+                                        hittype[j].bposz = sprite[j].z += l;
+                                        hittype[j].floorz = sc->floorz;
+                                    }
+                                    j = nextspritesect[j];
+                                }
+                            }
+                        }
+                        break;
+                    }
+
+                    if( (s->ang&2047) == 1536)
+                    {
+                        if( klabs( s->z-sc->floorz ) < SP )
+                        {
+                            callsound(s->sectnum,i);
+                            t[0] = 0;
+                            t[2] = 1;
+                            t[3] = s->hitag;
+                        }
+                        else
+                        {
+                            l = sgn(s->z-sc->floorz)*SP;
+                            sc->floorz += l;
+
+                            j = headspritesect[s->sectnum];
+                            while(j >= 0)
+                            {
+                                if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                    if( ps[sprite[j].yvel].on_ground == 1 )
+                                        ps[sprite[j].yvel].posz += l;
+                                if( sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4 )
+                                {
+                                    hittype[j].bposz = sprite[j].z += l;
+                                    hittype[j].floorz = sc->floorz;
+                                }
+                                j = nextspritesect[j];
+                            }
+                        }
+                    }
+                    else
+                    {
+                        if( klabs( sc->floorz-t[1] ) < SP )
+                        {
+                            t[0] = 0;
+                            callsound(s->sectnum,i);
+                            t[2] = 1;
+                            t[3] = s->hitag;
+                        }
+                        else
+                        {
+                            l = sgn(s->z-t[1])*SP;
+                            sc->floorz -= l;
+
+                            j = headspritesect[s->sectnum];
+                            while(j >= 0)
+                            {
+                                if(sprite[j].picnum == APLAYER && sprite[j].owner >= 0)
+                                    if( ps[sprite[j].yvel].on_ground == 1 )
+                                        ps[sprite[j].yvel].posz -= l;
+                                if(sprite[j].zvel == 0 && sprite[j].statnum != 3 && sprite[j].statnum != 4 )
+                                {
+                                    hittype[j].bposz = sprite[j].z -= l;
+                                    hittype[j].floorz = sc->floorz;
+                                }
+                                j = nextspritesect[j];
+                            }
+                        }
+                    }
+                }
+                break;
+
+           case 32: // True Drop Ceiling
+                if(t[0] == 1)
+                {
+                    // Choose dir
+
+                    if(t[2] == 1) // Retract
+                    {
+                        if(SA != 1536)
+                        {
+                            if( klabs( sc->ceilingz - s->z ) <
+                                (SP<<1) )
+                            {
+                                sc->ceilingz = s->z;
+                                callsound(s->sectnum,i);
+                                t[2] = 0;
+                                t[0] = 0;
+                            }
+                            else sc->ceilingz +=
+                                sgn(s->z-sc->ceilingz)*SP;
+                        }
+                        else
+                        {
+                            if( klabs( sc->ceilingz - t[1] ) <
+                                (SP<<1) )
+                            {
+                                sc->ceilingz = t[1];
+                                callsound(s->sectnum,i);
+                                t[2] = 0;
+                                t[0] = 0;
+                            }
+                            else sc->ceilingz +=
+                                sgn(t[1]-sc->ceilingz)*SP;
+                        }
+                        break;
+                    }
+
+                    if( (s->ang&2047) == 1536)
+                    {
+                        if( klabs(sc->ceilingz-s->z ) <
+                            (SP<<1) )
+                        {
+                            t[0] = 0;
+                            t[2] = !t[2];
+                            callsound(s->sectnum,i);
+                            sc->ceilingz = s->z;
+                        }
+                        else sc->ceilingz +=
+                            sgn(s->z-sc->ceilingz)*SP;
+                    }
+                    else
+                    {
+                        if( klabs(sc->ceilingz-t[1] ) < (SP<<1) )
+                        {
+                            t[0] = 0;
+                            t[2] = !t[2];
+                            callsound(s->sectnum,i);
+                        }
+                        else sc->ceilingz -= sgn(s->z-t[1])*SP;
+                    }
+                }
+                break;
+
+            case 33:
+                if( earthquaketime > 0 && (TRAND&7) == 0 )
+                    RANDOMSCRAP;
+                break;
+            case 36:
+
+                if( t[0] )
+                {
+                    if( t[0] == 1 )
+                        shoot(i,sc->extra);
+                    else if( t[0] == 26*5 )
+                        t[0] = 0;
+                    t[0]++;
+                }
+                break;
+
+            case 128: //SE to control glass breakage
+
+                wal = &wall[t[2]];
+
+                if(wal->cstat|32)
+                {
+                    wal->cstat &= (255-32);
+                    wal->cstat |= 16;
+                    if(wal->nextwall >= 0)
+                    {
+                        wall[wal->nextwall].cstat &= (255-32);
+                        wall[wal->nextwall].cstat |= 16;
+                    }
+                }
+                else break;
+
+                wal->overpicnum++;
+                if(wal->nextwall >= 0)
+                    wall[wal->nextwall].overpicnum++;
+
+                if(t[0] < t[1]) t[0]++;
+                else
+                {
+                    wal->cstat &= (128+32+8+4+2);
+                    if(wal->nextwall >= 0)
+                        wall[wal->nextwall].cstat &= (128+32+8+4+2);
+                    KILLIT(i);
+                }
+                break;
+
+            case 130:
+                if(t[0] > 80) { KILLIT(i); }
+                else t[0]++;
+
+                x = sc->floorz-sc->ceilingz;
+
+                if( rnd(64) )
+                {
+                    k = spawn(i,EXPLOSION2);
+                    sprite[k].xrepeat = sprite[k].yrepeat = 2+(TRAND&7);
+                    sprite[k].z = sc->floorz-(TRAND%x);
+                    sprite[k].ang += 256-(TRAND%511);
+                    sprite[k].xvel = TRAND&127;
+                    ssp(k,CLIPMASK0);
+                }
+                break;
+            case 131:
+                if(t[0] > 40) { KILLIT(i); }
+                else t[0]++;
+
+                x = sc->floorz-sc->ceilingz;
+
+                if( rnd(32) )
+                {
+                    k = spawn(i,EXPLOSION2);
+                    sprite[k].xrepeat = sprite[k].yrepeat = 2+(TRAND&3);
+                    sprite[k].z = sc->floorz-(TRAND%x);
+                    sprite[k].ang += 256-(TRAND%511);
+                    sprite[k].xvel = TRAND&127;
+                    ssp(k,CLIPMASK0);
+                }
+                break;
+        }
+        BOLT:
+        i = nexti;
+    }
+
+         //Sloped sin-wave floors!
+     for(i=headspritestat[3];i>=0;i=nextspritestat[i])
+     {
+          s = &sprite[i];
+          if (s->lotag != 29) continue;
+          sc = &sector[s->sectnum];
+          if (sc->wallnum != 4) continue;
+          wal = &wall[sc->wallptr+2];
+          alignflorslope(s->sectnum,wal->x,wal->y,sector[wal->nextsector].floorz);
+     }
+}
+
--- /dev/null
+++ b/Game/src/animlib.c
@@ -1,0 +1,340 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "types.h"
+#include "develop.h"
+#include "util_lib.h"
+//#include "_animlib.h"
+#include "animlib.h"
+
+//****************************************************************************
+//
+// GLOBALS
+//
+//****************************************************************************
+
+//****************************************************************************
+//
+// LOCALS
+//
+//****************************************************************************
+anim_t * anim=NULL;
+static boolean Anim_Started = false;
+
+//****************************************************************************
+//
+//      CheckAnimStarted ()
+//
+//****************************************************************************
+
+void CheckAnimStarted ( char * funcname )
+   {
+   if (!Anim_Started)
+      Error(EXIT_FAILURE, "ANIMLIB_%s: Anim has not been initialized\n",funcname);
+   }
+//****************************************************************************
+//
+//      findpage ()
+//              - given a frame number return the large page number it resides in
+//
+//****************************************************************************
+
+uint16 findpage (uint16 framenumber)
+   {
+   uint16 i;
+
+   CheckAnimStarted ( "findpage" );
+   for(i=0; i<anim->lpheader.nLps; i++)
+      {
+      if
+         (
+         anim->LpArray[i].baseRecord <= framenumber &&
+         anim->LpArray[i].baseRecord + anim->LpArray[i].nRecords > framenumber
+         )
+         return(i);
+      }
+   return(i);
+   }
+
+
+//****************************************************************************
+//
+//      loadpage ()
+//      - seek out and load in the large page specified
+//
+//****************************************************************************
+
+void loadpage (uint16 pagenumber, uint16 *pagepointer)
+   {
+   int32 size;
+   byte * buffer;
+
+   CheckAnimStarted ( "loadpage" );
+   buffer = anim->buffer;
+   if (anim->curlpnum != pagenumber)
+      {
+      anim->curlpnum = pagenumber;
+      buffer += 0xb00 + (pagenumber*0x10000);
+      size = sizeof(lp_descriptor);
+      memcpy(&anim->curlp,buffer,size);
+      buffer += size + sizeof(uint16);
+      memcpy(pagepointer,buffer,anim->curlp.nBytes+(anim->curlp.nRecords*2));
+      }
+   }
+
+
+//****************************************************************************
+//
+//      CPlayRunSkipDump ()
+//      - This version of the decompressor is here for portability to non PC's
+//
+//****************************************************************************
+
+void CPlayRunSkipDump (char *srcP, char *dstP)
+   {
+   signed char cnt;
+   uint16 wordCnt;
+   byte pixel;
+
+
+nextOp:
+   cnt = (signed char) *srcP++;
+   if (cnt > 0)
+      goto dump;
+   if (cnt == 0)
+      goto run;
+   cnt -= 0x80;
+   if (cnt == 0)
+      goto longOp;
+/* shortSkip */
+   dstP += cnt;                    /* adding 7-bit count to 32-bit pointer */
+   goto nextOp;
+dump:
+   do
+      {
+      *dstP++ = *srcP++;
+      } while (--cnt);
+   goto nextOp;
+run:
+   wordCnt = (byte)*srcP++;                /* 8-bit unsigned count */
+   pixel = *srcP++;
+   do
+      {
+      *dstP++ = pixel;
+      } while (--wordCnt);
+
+   goto nextOp;
+longOp:
+   wordCnt = *((uint16 *)srcP);
+   srcP += sizeof(uint16);
+   if ((int16)wordCnt <= 0)
+      goto notLongSkip;       /* Do SIGNED test. */
+
+/* longSkip. */
+   dstP += wordCnt;
+   goto nextOp;
+
+notLongSkip:
+   if (wordCnt == 0)
+      goto stop;
+   wordCnt -= 0x8000;              /* Remove sign bit. */
+   if (wordCnt >= 0x4000)
+      goto longRun;
+
+/* longDump. */
+   do
+      {
+      *dstP++ = *srcP++;
+      } while (--wordCnt);
+   goto nextOp;
+
+longRun:
+   wordCnt -= 0x4000;              /* Clear "longRun" bit. */
+   pixel = *srcP++;
+   do
+      {
+      *dstP++ = pixel;
+      } while (--wordCnt);
+   goto nextOp;
+
+stop:   /* all done */
+   ;
+   }
+
+
+
+//****************************************************************************
+//
+//      renderframe ()
+//      - draw the frame sepcified from the large page in the buffer pointed to
+//
+//****************************************************************************
+
+void renderframe (uint16 framenumber, uint16 *pagepointer)
+   {
+   uint16 offset=0;
+   uint16 i;
+   uint16 destframe;
+   byte *ppointer;
+
+   CheckAnimStarted ( "renderframe" );
+   destframe = framenumber - anim->curlp.baseRecord;
+
+   for(i = 0; i < destframe; i++)
+      {
+      offset += pagepointer[i];
+      }
+   ppointer = (byte *)pagepointer;
+
+   ppointer+=anim->curlp.nRecords*2+offset;
+   if(ppointer[1])
+      {
+      ppointer += (4 + (((uint16 *)ppointer)[1] + (((uint16 *)ppointer)[1] & 1)));
+      }
+   else
+      {
+      ppointer+=4;
+      }
+
+   CPlayRunSkipDump (ppointer, anim->imagebuffer);
+   }
+
+
+//****************************************************************************
+//
+//      drawframe ()
+//      - high level frame draw routine
+//
+//****************************************************************************
+
+void drawframe (uint16 framenumber)
+   {
+   CheckAnimStarted ( "drawframe" );
+   loadpage(findpage(framenumber), anim->thepage);
+   renderframe(framenumber, anim->thepage);
+   }
+
+
+//****************************************************************************
+//
+//      ANIM_LoadAnim ()
+//
+//****************************************************************************
+
+void ANIM_LoadAnim (char * buffer)
+   {
+   uint16 i;
+   int32 size;
+
+   if (!Anim_Started) Anim_Started = true;
+
+   anim->buffer = buffer;
+   anim->curlpnum = 0xffff;
+   anim->currentframe = -1;
+   size = sizeof(lpfileheader);
+   memcpy(&anim->lpheader, buffer, size );
+   buffer += size+128;
+   // load the color palette
+   for (i = 0; i < 768; i += 3)
+      {
+      anim->pal[i+2] = *buffer++;
+      anim->pal[i+1] = *buffer++;
+      anim->pal[i] = *buffer++;
+      buffer++;
+      }
+        // read in large page descriptors
+   size = sizeof(anim->LpArray);
+   memcpy(&anim->LpArray,buffer,size);
+   }
+
+//****************************************************************************
+//
+//      ANIM_FreeAnim ()
+//
+//****************************************************************************
+
+void ANIM_FreeAnim ( void )
+   {
+   if (Anim_Started)
+      {
+//      SafeFree(anim);
+      Anim_Started = false;
+      }
+   }
+
+//****************************************************************************
+//
+//      ANIM_NumFrames ()
+//
+//****************************************************************************
+
+int32 ANIM_NumFrames ( void )
+   {
+   CheckAnimStarted ( "NumFrames" );
+   return anim->lpheader.nRecords;
+   }
+
+//****************************************************************************
+//
+//      ANIM_DrawFrame ()
+//
+//****************************************************************************
+
+byte * ANIM_DrawFrame (int32 framenumber)
+   {
+   int32 cnt;
+
+   CheckAnimStarted ( "DrawFrame" );
+   if ((anim->currentframe != -1) && (anim->currentframe<=framenumber))
+      {
+      for (cnt = anim->currentframe; cnt < framenumber; cnt++)
+          drawframe (cnt);
+      }
+   else
+      {
+      for (cnt = 0; cnt < framenumber; cnt++)
+         drawframe (cnt);
+      }
+   anim->currentframe = framenumber;
+   return anim->imagebuffer;
+   }
+
+//****************************************************************************
+//
+//      ANIM_GetPalette ()
+//
+//****************************************************************************
+
+byte * ANIM_GetPalette ( void )
+   {
+   CheckAnimStarted ( "GetPalette" );
+   return anim->pal;
+   }
--- /dev/null
+++ b/Game/src/animlib.h
@@ -1,0 +1,155 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+/////////////////////////////////////////////////////////////////////////////
+//
+//      ANIMLIB.H
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _animlib_public_
+#define _animlib_public_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// structure declarations for deluxe animate large page files */
+
+typedef struct
+   {
+   uint32 id;                 // 4 character ID == "LPF " */
+   uint16 maxLps;                  // max # largePages allowed. 256 FOR NOW.   */
+   uint16 nLps;            // # largePages in this file. */
+   uint32 nRecords;        // # records in this file.  65534 is current limit plus */
+                        // one for last-to-first delta for looping the animation */
+   uint16 maxRecsPerLp; // # records permitted in an lp. 256 FOR NOW.   */
+   uint16 lpfTableOffset; // Absolute Seek position of lpfTable.  1280 FOR NOW.
+                                         // The lpf Table is an array of 256 large page structures
+                                         // that is used to facilitate finding records in an anim
+                                         // file without having to seek through all of the Large
+                                         // Pages to find which one a specific record lives in. */
+   uint32 contentType;  // 4 character ID == "ANIM" */
+   uint16 width;                   // Width of screen in pixels. */
+   uint16 height;                  // Height of screen in pixels. */
+   byte variant;              // 0==ANIM. */
+   byte version;              // 0==frame rate is multiple of 18 cycles/sec.
+                                        // 1==frame rate is multiple of 70 cycles/sec.  */
+   byte hasLastDelta;   // 1==Last record is a delta from last-to-first frame. */
+   byte lastDeltaValid; // 0==The last-to-first delta (if present) hasn't been
+                                  // updated to match the current first&last frames,    so it
+                                  // should be ignored. */
+   byte pixelType;         //   /* 0==256 color. */
+   byte CompressionType;//      /* 1==(RunSkipDump) Only one used FOR NOW. */
+   byte otherRecsPerFrm;//      /* 0 FOR NOW. */
+   byte bitmaptype;     //   /* 1==320x200, 256-color.  Only one implemented so far. */
+   byte recordTypes[32];//      /* Not yet implemented. */
+   uint32 nFrames;         //   /* In case future version adds other records at end of
+                                //      file, we still know how many actual frames.
+                                  //    NOTE: DOES include last-to-first delta when present. */
+   uint16 framesPerSecond;      // Number of frames to play per second. */
+   uint16 pad2[29];           // 58 bytes of filler to round up to 128 bytes total. */
+   } lpfileheader;
+
+// this is the format of a large page structure
+typedef struct
+   {
+   uint16 baseRecord;   // Number of first record in this large page.
+   uint16 nRecords;        // Number of records in lp.
+                                                      // bit 15 of "nRecords" == "has continuation from previous lp".
+                                              // bit 14 of "nRecords" == "final record continues on next lp".
+   uint16 nBytes;                  // Total number of bytes of contents, excluding header.
+   } lp_descriptor;
+
+typedef struct
+   {
+   uint16 framecount;          // current frame of anim
+   lpfileheader lpheader;           // file header will be loaded into this structure
+   lp_descriptor LpArray[256]; // arrays of large page structs used to find frames
+   uint16 curlpnum;               // initialize to an invalid Large page number
+   lp_descriptor curlp;        // header of large page currently in memory
+   uint16 thepage[0x8000];     // buffer where current large page is loaded
+   byte imagebuffer[0x10000]; // buffer where anim frame is decoded
+   byte * buffer;
+   byte pal[768];
+   int32  currentframe;
+  } anim_t;
+
+//****************************************************************************
+//
+//      ANIM_LoadAnim ()
+//
+// Setup internal anim data structure
+//
+//****************************************************************************
+
+void ANIM_LoadAnim (char * buffer);
+
+//****************************************************************************
+//
+//      ANIM_FreeAnim ()
+//
+// Free up internal anim data structure
+//
+//****************************************************************************
+
+void ANIM_FreeAnim ( void );
+
+//****************************************************************************
+//
+//      ANIM_NumFrames ()
+//
+// returns the number of frames in the current anim
+//
+//****************************************************************************
+
+int32 ANIM_NumFrames ( void );
+
+//****************************************************************************
+//
+//      ANIM_DrawFrame ()
+//
+// Draw the frame to a returned buffer
+//
+//****************************************************************************
+
+byte * ANIM_DrawFrame (int32 framenumber);
+
+//****************************************************************************
+//
+//      ANIM_GetPalette ()
+//
+// return the palette of the anim
+//****************************************************************************
+
+byte * ANIM_GetPalette ( void );
+
+extern anim_t * anim;
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/audiolib/Makefile
@@ -1,0 +1,22 @@
+CC=gcc
+AR=ar
+RANLIB=ranlib
+CFLAGS=-g -O2
+LDLIBS=
+
+CFLAGS += $(shell sdl-config --cflags)
+LDLIBS += $(shell sdl-config --libs)
+
+OBJ=fx_man.o dsl.o ll_man.o multivoc.o mv_mix.o mvreverb.o nodpmi.o \
+	pitch.o user.o usrhooks.o
+
+audiolib.a: $(OBJ)
+	rm -rf $@
+	$(AR) rc $@ $^
+	$(RANLIB) $@
+
+clean:
+	rm -rf audiolib.a *.o
+
+distclean: clean
+	rm -rf *~
--- /dev/null
+++ b/Game/src/audiolib/_al_midi.h
@@ -1,0 +1,174 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef ___AL_MIDI_H
+#define ___AL_MIDI_H
+
+#define NO_ADLIB_DETECTION    "NOAL"
+
+#define STEREO_DETUNE 5
+
+#define lobyte( num )   ( ( unsigned )*( ( char * )&( num ) ) )
+#define hibyte( num )   ( ( unsigned )*( ( ( char * )&( num ) ) + 1 ) )
+
+#define AL_VoiceNotFound -1
+
+#define  alFreqH     0xb0
+#define  alEffects   0xbd
+
+/* Number of slots for the voices on the chip */
+#define NumChipSlots 18
+
+#define NUM_VOICES      9
+#define NUM_CHANNELS    16
+
+#define NOTE_ON         0x2000  /* Used to turn note on or toggle note */
+#define NOTE_OFF        0x0000
+
+#define MAX_VELOCITY   0x7f
+#define MAX_OCTAVE     7
+#define MAX_NOTE       ( MAX_OCTAVE * 12 + 11 )
+#define FINETUNE_MAX   31
+#define FINETUNE_RANGE ( FINETUNE_MAX + 1 )
+
+#define PITCHBEND_CENTER 1638400
+
+#define note_off             0x80
+#define note_on              0x90
+#define poly_aftertouch      0xa0
+#define control_change       0xb0
+#define program_chng         0xc0
+#define channel_aftertouch   0xd0
+#define pitch_wheel          0xe0
+
+#define MIDI_VOLUME          7
+#define MIDI_PAN             10
+#define MIDI_DETUNE          94
+#define MIDI_ALL_NOTES_OFF   0x7B
+#define MIDI_RESET_ALL_CONTROLLERS 0x79
+#define MIDI_RPN_MSB               100
+#define MIDI_RPN_LSB               101
+#define MIDI_DATAENTRY_MSB         6
+#define MIDI_DATAENTRY_LSB         38
+#define MIDI_PITCHBEND_RPN         0
+
+enum cromatic_scale
+   {
+   C       = 0x157,
+   C_SHARP = 0x16B,
+   D_FLAT  = 0x16B,
+   D       = 0x181,
+   D_SHARP = 0x198,
+   E_FLAT  = 0x198,
+   E       = 0x1B0,
+   F_FLAT  = 0x1B0,
+   E_SHARP = 0x1CA,
+   F       = 0x1CA,
+   F_SHARP = 0x1E5,
+   G_FLAT  = 0x1E5,
+   G       = 0x202,
+   G_SHARP = 0x220,
+   A_FLAT  = 0x220,
+   A       = 0x241,
+   A_SHARP = 0x263,
+   B_FLAT  = 0x263,
+   B       = 0x287,
+   C_FLAT  = 0x287,
+   B_SHARP = 0x2AE,
+   };
+
+/* Definition of octave information to be ORed onto F-Number */
+
+enum octaves
+   {
+   OCTAVE_0 = 0x0000,
+   OCTAVE_1 = 0x0400,
+   OCTAVE_2 = 0x0800,
+   OCTAVE_3 = 0x0C00,
+   OCTAVE_4 = 0x1000,
+   OCTAVE_5 = 0x1400,
+   OCTAVE_6 = 0x1800,
+   OCTAVE_7 = 0x1C00
+   };
+
+typedef struct VOICE
+   {
+   struct VOICE *next;
+   struct VOICE *prev;
+
+   unsigned num;
+   unsigned key;
+   unsigned velocity;
+   unsigned channel;
+   unsigned pitchleft;
+   unsigned pitchright;
+   int      timbre;
+   int      port;
+   unsigned status;
+   } VOICE;
+
+typedef struct
+   {
+   VOICE *start;
+   VOICE *end;
+   } VOICELIST;
+
+typedef struct
+   {
+   VOICELIST Voices;
+   int       Timbre;
+   int       Pitchbend;
+   int       KeyOffset;
+   unsigned  KeyDetune;
+   unsigned  Volume;
+   unsigned  EffectiveVolume;
+   int       Pan;
+   int       Detune;
+   unsigned  RPN;
+   short     PitchBendRange;
+   short     PitchBendSemiTones;
+   short     PitchBendHundreds;
+   } CHANNEL;
+
+typedef struct
+   {
+   unsigned char SAVEK[ 2 ];
+   unsigned char Level[ 2 ];
+   unsigned char Env1[ 2 ];
+   unsigned char Env2[ 2 ];
+   unsigned char Wave[ 2 ];
+   unsigned char Feedback;
+   signed   char Transpose;
+   signed   char Velocity;
+   } TIMBRE;
+
+extern TIMBRE ADLIB_TimbreBank[ 256 ];
+
+static void AL_ResetVoices( void );
+static void AL_CalcPitchInfo( void );
+static void AL_SetVoiceTimbre( int voice );
+static void AL_SetVoiceVolume( int voice );
+static int  AL_AllocVoice( void );
+static int  AL_GetVoice( int channel, int key );
+static void AL_SetVoicePitch( int voice );
+static void AL_SetChannelVolume( int channel, int volume );
+static void AL_SetChannelPan( int channel, int pan );
+static void AL_SetChannelDetune( int channel, int detune );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_blaster.h
@@ -1,0 +1,133 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: _BLASTER.H
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Private header for for BLASTER.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___BLASTER_H
+#define ___BLASTER_H
+
+#define VALID   ( 1 == 1 )
+#define INVALID ( !VALID )
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+#define YES ( 1 == 1 )
+#define NO  ( !YES )
+
+#define lobyte( num )   ( ( int )*( ( char * )&( num ) ) )
+#define hibyte( num )   ( ( int )*( ( ( char * )&( num ) ) + 1 ) )
+
+#define BLASTER_MixerAddressPort  0x04
+#define BLASTER_MixerDataPort     0x05
+#define BLASTER_ResetPort         0x06
+#define BLASTER_ReadPort          0x0A
+#define BLASTER_WritePort         0x0C
+#define BLASTER_DataAvailablePort 0x0E
+#define BLASTER_Ready             0xAA
+#define BLASTER_16BitDMAAck       0x0F
+
+#define MIXER_DSP4xxISR_Ack       0x82
+#define MIXER_DSP4xxISR_Enable    0x83
+#define MIXER_MPU401_INT          0x4
+#define MIXER_16BITDMA_INT        0x2
+#define MIXER_8BITDMA_INT         0x1
+#define MIXER_DisableMPU401Interrupts 0xB
+#define MIXER_SBProOutputSetting  0x0E
+#define MIXER_SBProStereoFlag     0x02
+#define MIXER_SBProVoice          0x04
+#define MIXER_SBProMidi           0x26
+#define MIXER_SB16VoiceLeft       0x32
+#define MIXER_SB16VoiceRight      0x33
+#define MIXER_SB16MidiLeft        0x34
+#define MIXER_SB16MidiRight       0x35
+
+#define DSP_Version1xx            0x0100
+#define DSP_Version2xx            0x0200
+#define DSP_Version201            0x0201
+#define DSP_Version3xx            0x0300
+#define DSP_Version4xx            0x0400
+#define DSP_SB16Version           DSP_Version4xx
+
+#define DSP_MaxNormalRate         22000
+#define DSP_MaxHighSpeedRate      44000
+
+#define DSP_8BitAutoInitRecord        0x2c
+#define DSP_8BitHighSpeedAutoInitRecord 0x98
+#define DSP_Old8BitADC                0x24
+#define DSP_8BitAutoInitMode          0x1c
+#define DSP_8BitHighSpeedAutoInitMode 0x90
+#define DSP_SetBlockLength            0x48
+#define DSP_Old8BitDAC                0x14
+#define DSP_16BitDAC                  0xB6
+#define DSP_8BitDAC                   0xC6
+#define DSP_8BitADC                   0xCe
+#define DSP_SetTimeConstant           0x40
+#define DSP_Set_DA_Rate               0x41
+#define DSP_Set_AD_Rate               0x42
+#define DSP_Halt8bitTransfer          0xd0
+#define DSP_Continue8bitTransfer      0xd4
+#define DSP_Halt16bitTransfer         0xd5
+#define DSP_Continue16bitTransfer     0xd6
+#define DSP_SpeakerOn                 0xd1
+#define DSP_SpeakerOff                0xd3
+#define DSP_GetVersion                0xE1
+#define DSP_Reset                     0xFFFF
+
+#define DSP_SignedBit                 0x10
+#define DSP_StereoBit                 0x20
+
+#define DSP_UnsignedMonoData      0x00
+#define DSP_SignedMonoData        ( DSP_SignedBit )
+#define DSP_UnsignedStereoData    ( DSP_StereoBit )
+#define DSP_SignedStereoData      ( DSP_SignedBit | DSP_StereoBit )
+
+#define BlasterEnv_Address    'A'
+#define BlasterEnv_Interrupt  'I'
+#define BlasterEnv_8bitDma    'D'
+#define BlasterEnv_16bitDma   'H'
+#define BlasterEnv_Type       'T'
+#define BlasterEnv_Midi       'P'
+#define BlasterEnv_EmuAddress 'E'
+
+#define CalcTimeConstant( rate, samplesize ) \
+   ( ( 65536L - ( 256000000L / ( ( samplesize ) * ( rate ) ) ) ) >> 8 )
+
+#define CalcSamplingRate( tc ) \
+   ( 256000000L / ( 65536L - ( tc << 8 ) ) )
+
+typedef struct
+   {
+   int IsSupported;
+   int HasMixer;
+   int MaxMixMode;
+   int MinSamplingRate;
+   int MaxSamplingRate;
+   } CARD_CAPABILITY;
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_guswave.h
@@ -1,0 +1,164 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   _GUSWAVE.H
+
+   author: James R. Dose
+   date:   March 23, 1994
+
+   Private header for GUSWAVE.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___GUSWAVE_H
+#define ___GUSWAVE_H
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+#define LOADDS _loadds
+
+#define VOC_8BIT            0x0
+#define VOC_CT4_ADPCM       0x1
+#define VOC_CT3_ADPCM       0x2
+#define VOC_CT2_ADPCM       0x3
+#define VOC_16BIT           0x4
+#define VOC_ALAW            0x6
+#define VOC_MULAW           0x7
+#define VOC_CREATIVE_ADPCM  0x200
+
+#define MAX_BLOCK_LENGTH 0x8000
+
+#define GF1BSIZE   896L   /* size of buffer per wav on GUS */
+//#define GF1BSIZE   512L   /* size of buffer per wav on GUS */
+
+//#define VOICES     8      /* maximum amount of concurrent wav files */
+#define VOICES     2      /* maximum amount of concurrent wav files */
+#define MAX_VOICES 32     /* This should always be 32 */
+#define MAX_VOLUME 4095
+#define BUFFER     2048U  /* size of DMA buffer for patch loading */
+
+typedef enum
+   {
+   Raw,
+   VOC,
+   DemandFeed,
+   WAV
+   } wavedata;
+
+typedef enum
+   {
+   NoMoreData,
+   KeepPlaying,
+   SoundDone
+   } playbackstatus;
+
+
+typedef volatile struct VoiceNode
+   {
+   struct VoiceNode *next;
+   struct VoiceNode *prev;
+
+   wavedata      wavetype;
+   int           bits;
+   playbackstatus ( *GetSound )( struct VoiceNode *voice );
+
+   int num;
+
+   unsigned long  mem;           /* location in ultrasound memory */
+   int            Active;        /* this instance in use */
+   int            GF1voice;      /* handle to active voice */
+
+   char          *NextBlock;
+   char          *LoopStart;
+   char          *LoopEnd;
+   unsigned       LoopCount;
+   unsigned long  LoopSize;
+   unsigned long  BlockLength;
+
+   unsigned long  PitchScale;
+
+   unsigned char *sound;
+   unsigned long  length;
+   unsigned long  SamplingRate;
+   unsigned long  RateScale;
+   int            Playing;
+
+   int    handle;
+   int    priority;
+
+   void          ( *DemandFeed )( char **ptr, unsigned long *length );
+
+   unsigned long callbackval;
+
+   int    Volume;
+   int    Pan;
+   }
+VoiceNode;
+
+typedef struct
+   {
+   VoiceNode *start;
+   VoiceNode *end;
+   }
+voicelist;
+
+typedef volatile struct voicestatus
+   {
+   VoiceNode *Voice;
+   int playing;
+   }
+voicestatus;
+
+typedef struct
+   {
+   char          RIFF[ 4 ];
+   unsigned long file_size;
+   char          WAVE[ 4 ];
+   char          fmt[ 4 ];
+   unsigned long format_size;
+   } riff_header;
+
+typedef struct
+   {
+   unsigned short wFormatTag;
+   unsigned short nChannels;
+   unsigned long  nSamplesPerSec;
+   unsigned long  nAvgBytesPerSec;
+   unsigned short nBlockAlign;
+   unsigned short nBitsPerSample;
+   } format_header;
+
+typedef struct
+   {
+   unsigned char DATA[ 4 ];
+   unsigned long size;
+   } data_header;
+
+playbackstatus GUSWAVE_GetNextVOCBlock( VoiceNode *voice );
+VoiceNode *GUSWAVE_GetVoice( int handle );
+
+int GUSWAVE_Play( VoiceNode *voice, int angle, int volume, int channels );
+
+VoiceNode *GUSWAVE_AllocVoice( int priority );
+static int GUSWAVE_InitVoices( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_midi.h
@@ -1,0 +1,290 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: _MIDI.H
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Private header for MIDI.C.  Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___MIDI_H
+#define ___MIDI_H
+
+#define RELATIVE_BEAT( measure, beat, tick ) \
+   ( ( tick ) + ( ( beat ) << 9 ) + ( ( measure ) << 16 ) )
+
+//Bobby Prince thinks this may be 100
+//#define GENMIDI_DefaultVolume 100
+#define GENMIDI_DefaultVolume 90
+
+#define MAX_FORMAT            1
+
+#define NUM_MIDI_CHANNELS     16
+
+#define TIME_PRECISION        16
+
+#define MIDI_HEADER_SIGNATURE 0x6468544d    // "MThd"
+#define MIDI_TRACK_SIGNATURE  0x6b72544d    // "MTrk"
+
+#define MIDI_VOLUME                7
+#define MIDI_PAN                   10
+#define MIDI_DETUNE                94
+#define MIDI_RHYTHM_CHANNEL        9
+#define MIDI_RPN_MSB               100
+#define MIDI_RPN_LSB               101
+#define MIDI_DATAENTRY_MSB         6
+#define MIDI_DATAENTRY_LSB         38
+#define MIDI_PITCHBEND_MSB         0
+#define MIDI_PITCHBEND_LSB         0
+#define MIDI_RUNNING_STATUS        0x80
+#define MIDI_NOTE_OFF              0x8
+#define MIDI_NOTE_ON               0x9
+#define MIDI_POLY_AFTER_TCH        0xA
+#define MIDI_CONTROL_CHANGE        0xB
+#define MIDI_PROGRAM_CHANGE        0xC
+#define MIDI_AFTER_TOUCH           0xD
+#define MIDI_PITCH_BEND            0xE
+#define MIDI_SPECIAL               0xF
+#define MIDI_SYSEX                 0xF0
+#define MIDI_SYSEX_CONTINUE        0xF7
+#define MIDI_META_EVENT            0xFF
+#define MIDI_END_OF_TRACK          0x2F
+#define MIDI_TEMPO_CHANGE          0x51
+#define MIDI_TIME_SIGNATURE        0x58
+#define MIDI_RESET_ALL_CONTROLLERS 0x79
+#define MIDI_ALL_NOTES_OFF         0x7b
+#define MIDI_MONO_MODE_ON          0x7E
+#define MIDI_SYSTEM_RESET          0xFF
+
+#define GET_NEXT_EVENT( track, data ) \
+   ( data ) = *( track )->pos; \
+   ( track )->pos += 1
+
+#define GET_MIDI_CHANNEL( event )       ( ( event ) & 0xf )
+#define GET_MIDI_COMMAND( event )       ( ( event ) >> 4 )
+
+#define EMIDI_INFINITE          -1
+#define EMIDI_END_LOOP_VALUE    127
+#define EMIDI_ALL_CARDS         127
+#define EMIDI_INCLUDE_TRACK     110
+#define EMIDI_EXCLUDE_TRACK     111
+#define EMIDI_PROGRAM_CHANGE    112
+#define EMIDI_VOLUME_CHANGE     113
+#define EMIDI_CONTEXT_START     114
+#define EMIDI_CONTEXT_END       115
+#define EMIDI_LOOP_START        116
+#define EMIDI_LOOP_END          117
+#define EMIDI_SONG_LOOP_START   118
+#define EMIDI_SONG_LOOP_END     119
+
+#define EMIDI_GeneralMIDI       0
+#define EMIDI_SoundCanvas       1
+#define EMIDI_AWE32             2
+#define EMIDI_WaveBlaster       3
+#define EMIDI_SoundBlaster      4
+#define EMIDI_ProAudio          5
+#define EMIDI_SoundMan16        6
+#define EMIDI_Adlib             7
+#define EMIDI_Soundscape        8
+#define EMIDI_Ultrasound        9
+
+#define EMIDI_AffectsCurrentCard( c, type ) \
+   ( ( ( c ) == EMIDI_ALL_CARDS ) || ( ( c ) == ( type ) ) )
+
+
+#define EMIDI_NUM_CONTEXTS      7
+typedef struct
+   {
+   unsigned char *pos;
+   unsigned char *loopstart;
+   short          loopcount;
+   short          RunningStatus;
+   unsigned       time;
+   long           FPSecondsPerTick;
+   short          tick;
+   short          beat;
+   short          measure;
+   short          BeatsPerMeasure;
+   short          TicksPerBeat;
+   short          TimeBase;
+   long           delay;
+   short          active;
+   } songcontext;
+
+typedef struct
+   {
+   unsigned char *start;
+   unsigned char *pos;
+
+   long           delay;
+   short          active;
+   short          RunningStatus;
+
+   short          currentcontext;
+   songcontext    context[ EMIDI_NUM_CONTEXTS ];
+
+   char           EMIDI_IncludeTrack;
+   char           EMIDI_ProgramChange;
+   char           EMIDI_VolumeChange;
+   } track;
+
+static long _MIDI_ReadNumber( void *from, size_t size );
+static long _MIDI_ReadDelta( track *ptr );
+static void _MIDI_ResetTracks( void );
+static void _MIDI_AdvanceTick( void );
+static void _MIDI_MetaEvent( track *Track );
+static void _MIDI_SysEx( track *Track );
+static int  _MIDI_InterpretControllerInfo( track *Track, int TimeSet,
+   int channel, int c1, int c2 );
+//static
+   void _MIDI_ServiceRoutine( task *Task );
+static int  _MIDI_SendControlChange( int channel, int c1, int c2 );
+static void _MIDI_SetChannelVolume( int channel, int volume );
+static void _MIDI_SendChannelVolumes( void );
+static int  _MIDI_ProcessNextTick( void );
+static void _MIDI_InitEMIDI( void );
+
+/*
+               if ( c1 == EMIDI_LOOP_START )
+                  {
+                  if ( c2 == 0 )
+                     {
+                     Track->context[ 0 ].loopcount = EMIDI_INFINITE;
+                     }
+                  else
+                     {
+                     Track->context[ 0 ].loopcount = c2;
+                     }
+
+                  Track->context[ 0 ].pos              = Track->pos;
+                  Track->context[ 0 ].loopstart        = Track->pos;
+                  Track->context[ 0 ].RunningStatus    = Track->RunningStatus;
+                  Track->context[ 0 ].time             = _MIDI_Time;
+                  Track->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
+                  Track->context[ 0 ].tick             = _MIDI_Tick;
+                  Track->context[ 0 ].beat             = _MIDI_Beat;
+                  Track->context[ 0 ].measure          = _MIDI_Measure;
+                  Track->context[ 0 ].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
+                  Track->context[ 0 ].TicksPerBeat     = _MIDI_TicksPerBeat;
+                  Track->context[ 0 ].TimeBase         = _MIDI_TimeBase;
+                  break;
+                  }
+
+               if ( ( c1 == EMIDI_LOOP_END ) &&
+                  ( c2 == EMIDI_END_LOOP_VALUE ) )
+                  {
+                  if ( ( Track->context[ 0 ].loopstart != NULL ) &&
+                     ( Track->context[ 0 ].loopcount != 0 ) )
+                     {
+                     if ( Track->context[ 0 ].loopcount != EMIDI_INFINITE )
+                        {
+                        Track->context[ 0 ].loopcount--;
+                        }
+
+                     Track->pos           = Track->context[ 0 ].loopstart;
+                     Track->RunningStatus = Track->context[ 0 ].RunningStatus;
+
+                     if ( !TimeSet )
+                        {
+                        _MIDI_Time             = Track->context[ 0 ].time;
+                        _MIDI_FPSecondsPerTick = Track->context[ 0 ].FPSecondsPerTick;
+                        _MIDI_Tick             = Track->context[ 0 ].tick;
+                        _MIDI_Beat             = Track->context[ 0 ].beat;
+                        _MIDI_Measure          = Track->context[ 0 ].measure;
+                        _MIDI_BeatsPerMeasure  = Track->context[ 0 ].BeatsPerMeasure;
+                        _MIDI_TicksPerBeat     = Track->context[ 0 ].TicksPerBeat;
+                        _MIDI_TimeBase         = Track->context[ 0 ].TimeBase;
+                        TimeSet = TRUE;
+                        }
+                     }
+                  break;
+                  }
+
+               if ( c1 == MIDI_MONO_MODE_ON )
+                  {
+                  Track->pos++;
+                  }
+
+               if ( ( c1 == MIDI_VOLUME ) && ( !Track->EMIDI_VolumeChange ) )
+                  {
+                  _MIDI_SetChannelVolume( channel, c2 );
+                  break;
+                  }
+               else if ( ( c1 == EMIDI_VOLUME_CHANGE ) &&
+                  ( Track->EMIDI_VolumeChange ) )
+                  {
+                  _MIDI_SetChannelVolume( channel, c2 );
+                  break;
+                  }
+
+               if ( ( c1 == EMIDI_PROGRAM_CHANGE ) &&
+                  ( Track->EMIDI_ProgramChange ) )
+                  {
+                  _MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c2 & 0x7f ] );
+                  break;
+                  }
+
+               if ( c1 == EMIDI_CONTEXT_START )
+                  {
+                  break;
+                  }
+
+               if ( c1 == EMIDI_CONTEXT_END )
+                  {
+                  if ( ( Track->currentcontext != _MIDI_Context ) ||
+                     ( Track->context[ _MIDI_Context ].pos == NULL )
+                     {
+                     break;
+                     }
+
+                  Track->currentcontext = _MIDI_Context;
+                  Track->context[ 0 ].loopstart = Track->context[ _MIDI_Context ].loopstart;
+                  Track->context[ 0 ].loopcount = Track->context[ _MIDI_Context ].loopcount;
+                  Track->pos           = Track->context[ _MIDI_Context ].pos;
+                  Track->RunningStatus = Track->context[ _MIDI_Context ].RunningStatus;
+
+                  if ( TimeSet )
+                     {
+                     break;
+                     }
+
+                  _MIDI_Time             = Track->context[ _MIDI_Context ].time;
+                  _MIDI_FPSecondsPerTick = Track->context[ _MIDI_Context ].FPSecondsPerTick;
+                  _MIDI_Tick             = Track->context[ _MIDI_Context ].tick;
+                  _MIDI_Beat             = Track->context[ _MIDI_Context ].beat;
+                  _MIDI_Measure          = Track->context[ _MIDI_Context ].measure;
+                  _MIDI_BeatsPerMeasure  = Track->context[ _MIDI_Context ].BeatsPerMeasure;
+                  _MIDI_TicksPerBeat     = Track->context[ _MIDI_Context ].TicksPerBeat;
+                  _MIDI_TimeBase         = Track->context[ _MIDI_Context ].TimeBase;
+                  TimeSet = TRUE;
+                  break;
+                  }
+
+               if ( _MIDI_Funcs->ControlChange )
+                  {
+                  _MIDI_Funcs->ControlChange( channel, c1, c2 );
+                  }
+ */
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_multivc.h
@@ -1,0 +1,318 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   _MULTIVC.H
+
+   author: James R. Dose
+   date:   December 20, 1993
+
+   Private header for MULTIVOC.C
+
+   (c) Copyright 1993 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___MULTIVC_H
+#define ___MULTIVC_H
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+#define VOC_8BIT            0x0
+#define VOC_CT4_ADPCM       0x1
+#define VOC_CT3_ADPCM       0x2
+#define VOC_CT2_ADPCM       0x3
+#define VOC_16BIT           0x4
+#define VOC_ALAW            0x6
+#define VOC_MULAW           0x7
+#define VOC_CREATIVE_ADPCM  0x200
+
+#define T_SIXTEENBIT_STEREO 0
+#define T_8BITS       1
+#define T_MONO        2
+#define T_16BITSOURCE 4
+#define T_LEFTQUIET   8
+#define T_RIGHTQUIET  16
+#define T_DEFAULT     T_SIXTEENBIT_STEREO
+
+#define MV_MaxPanPosition  31
+#define MV_NumPanPositions ( MV_MaxPanPosition + 1 )
+#define MV_MaxTotalVolume  255
+//#define MV_MaxVolume       63
+//#define MV_NumVoices       8
+
+#define MIX_VOLUME( volume ) \
+   ( ( max( 0, min( ( volume ), 255 ) ) * ( MV_MaxVolume + 1 ) ) >> 8 )
+//   ( ( max( 0, min( ( volume ), 255 ) ) ) >> 2 )
+
+//#define SILENCE_16BIT     0x80008000
+#define SILENCE_16BIT     0
+#define SILENCE_8BIT      0x80808080
+//#define SILENCE_16BIT_PAS 0
+
+#define MixBufferSize     256
+
+#define NumberOfBuffers   16
+#define TotalBufferSize   ( MixBufferSize * NumberOfBuffers )
+
+#define PI                3.1415926536
+
+typedef enum
+   {
+   Raw,
+   VOC,
+   DemandFeed,
+   WAV
+   } wavedata;
+
+typedef enum
+   {
+   NoMoreData,
+   KeepPlaying
+   } playbackstatus;
+
+
+typedef struct VoiceNode
+   {
+   struct VoiceNode *next;
+   struct VoiceNode *prev;
+
+   wavedata      wavetype;
+   char          bits;
+
+   playbackstatus ( *GetSound )( struct VoiceNode *voice );
+
+   void ( *mix )( unsigned long position, unsigned long rate,
+      const char *start, unsigned long length );
+
+   char         *NextBlock;
+   char         *LoopStart;
+   char         *LoopEnd;
+   unsigned      LoopCount;
+   unsigned long LoopSize;
+   unsigned long BlockLength;
+
+   unsigned long PitchScale;
+   unsigned long FixedPointBufferSize;
+
+   char         *sound;
+   unsigned long length;
+   unsigned long SamplingRate;
+   unsigned long RateScale;
+   unsigned long position;
+   int           Playing;
+
+   int           handle;
+   int           priority;
+
+   void          ( *DemandFeed )( char **ptr, unsigned long *length );
+
+#if 0
+   short        *LeftVolume;
+   short        *RightVolume;
+#else
+   int           LeftVolume;
+   int           RightVolume;
+#endif
+
+   int           GLast;
+   int           GPos;
+   int           GVal[4];
+
+   unsigned long callbackval;
+
+   } VoiceNode;
+
+typedef struct
+   {
+   VoiceNode *start;
+   VoiceNode *end;
+   } VList;
+
+typedef struct
+   {
+   unsigned char left;
+   unsigned char right;
+   } Pan;
+
+typedef signed short MONO16;
+typedef signed char  MONO8;
+
+typedef struct
+   {
+   MONO16 left;
+   MONO16 right;
+//   unsigned short left;
+//   unsigned short right;
+   } STEREO16;
+
+typedef struct
+   {
+   MONO16 left;
+   MONO16 right;
+   } SIGNEDSTEREO16;
+
+typedef struct
+   {
+//   MONO8 left;
+//   MONO8 right;
+   char left;
+   char right;
+   } STEREO8;
+
+typedef struct
+   {
+   char          RIFF[ 4 ];
+   unsigned long file_size;
+   char          WAVE[ 4 ];
+   char          fmt[ 4 ];
+   unsigned long format_size;
+   } riff_header;
+
+typedef struct
+   {
+   unsigned short wFormatTag;
+   unsigned short nChannels;
+   unsigned long  nSamplesPerSec;
+   unsigned long  nAvgBytesPerSec;
+   unsigned short nBlockAlign;
+   unsigned short nBitsPerSample;
+   } format_header;
+
+typedef struct
+   {
+   unsigned char DATA[ 4 ];
+   unsigned long size;
+   } data_header;
+
+typedef MONO8  VOLUME8[ 256 ];
+typedef MONO16 VOLUME16[ 256 ];
+
+// typedef char HARSH_CLIP_TABLE_8[ MV_NumVoices * 256 ];
+
+static void MV_Mix( VoiceNode *voice, int buffer );
+static void MV_PlayVoice( VoiceNode *voice );
+static void MV_StopVoice( VoiceNode *voice );
+static void MV_ServiceVoc( void );
+
+static playbackstatus MV_GetNextVOCBlock( VoiceNode *voice );
+static playbackstatus MV_GetNextDemandFeedBlock( VoiceNode *voice );
+static playbackstatus MV_GetNextRawBlock( VoiceNode *voice );
+static playbackstatus MV_GetNextWAVBlock( VoiceNode *voice );
+
+static void       MV_ServiceRecord( void );
+static VoiceNode *MV_GetVoice( int handle );
+static VoiceNode *MV_AllocVoice( int priority );
+
+#if 0
+static short     *MV_GetVolumeTable( int vol );
+#else
+static int        MV_GetVolumeTable( int vol );
+#endif
+
+static void       MV_SetVoiceMixMode( VoiceNode *voice );
+
+static void       MV_SetVoicePitch( VoiceNode *voice, unsigned long rate, int pitchoffset );
+static void       MV_CalcVolume( int MaxLevel );
+static void       MV_CalcPanTable( void );
+
+#ifdef PLAT_DOS
+#define ATR_INDEX               0x3c0
+#define STATUS_REGISTER_1       0x3da
+
+#define SetBorderColor(color) \
+   { \
+   inp  (STATUS_REGISTER_1); \
+   outp (ATR_INDEX,0x31);    \
+   outp (ATR_INDEX,color);   \
+   }
+#endif
+
+void ClearBuffer_DW( void *ptr, unsigned data, int length );
+
+#ifdef PLAT_DOS
+#pragma aux ClearBuffer_DW = \
+   "cld",                    \
+   "push   es",              \
+   "push   ds",              \
+   "pop    es",              \
+   "rep    stosd",           \
+   "pop    es",              \
+parm [ edi ] [ eax ] [ ecx ] modify exact [ ecx edi ];
+#endif
+
+/*
+void MV_Mix8BitMono( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_Mix8BitStereo( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+
+void MV_Mix16BitMono( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+
+void MV_Mix16BitStereo( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+
+void MV_Mix16BitMono16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+
+void MV_Mix8BitMono16( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_Mix8BitStereo16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+
+void MV_Mix16BitStereo16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length );
+*/
+
+void MV_MixFPMono8( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_MixFPMono16( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_MixFPStereo8( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_MixFPStereo16( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length );
+
+void MV_FPReverb(int volume);
+
+void MV_FPReverbFree(void);
+
+void MV_16BitDownmix(char *dest, int count);
+
+void MV_8BitDownmix(char *dest, int count);
+
+/*
+void MV_16BitReverbFast( const char *src, char *dest, int count, int shift );
+
+void MV_8BitReverbFast( const signed char *src, signed char *dest, int count, int shift );
+*/
+#ifdef PLAT_DOS
+#pragma aux MV_16BitReverb parm [eax] [edx] [ebx] [ecx] modify exact [eax ebx ecx edx esi edi]
+#pragma aux MV_8BitReverb parm [eax] [edx] [ebx] [ecx] modify exact [eax ebx ecx edx esi edi]
+#pragma aux MV_16BitReverbFast parm [eax] [edx] [ebx] [ecx] modify exact [eax ebx ecx edx esi edi]
+#pragma aux MV_8BitReverbFast parm [eax] [edx] [ebx] [ecx] modify exact [eax ebx ecx edx esi edi]
+#endif
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_pas16.h
@@ -1,0 +1,250 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: _PAS16.H
+
+   author: James R. Dose
+   date:   March 27, 1994
+
+   Private header for for PAS16.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___PAS16_H
+#define ___PAS16_H
+
+#define TRUE    ( 1 == 1 )
+#define FALSE   ( !TRUE )
+
+#define VALID   ( 1 == 1 )
+#define INVALID ( !VALID )
+
+#define lobyte( num )   ( ( int )*( ( char * )&( num ) ) )
+#define hibyte( num )   ( ( int )*( ( ( char * )&( num ) ) + 1 ) )
+
+#define STEREO      1
+#define SIXTEEN_BIT 2
+
+#define MONO_8BIT    0
+#define STEREO_8BIT  ( STEREO )
+#define MONO_16BIT   ( SIXTEEN_BIT )
+#define STEREO_16BIT ( STEREO | SIXTEEN_BIT )
+
+#define PAS_MaxMixMode        STEREO_16BIT
+
+#define MONO_8BIT_SAMPLE_SIZE    1
+#define MONO_16BIT_SAMPLE_SIZE   2
+#define STEREO_8BIT_SAMPLE_SIZE  ( 2 * MONO_8BIT_SAMPLE_SIZE )
+#define STEREO_16BIT_SAMPLE_SIZE ( 2 * MONO_16BIT_SAMPLE_SIZE )
+
+#define PAS_RevisionBits        0xe0
+
+#define AudioFilterControl      0xb8a
+#define InterruptControl        0xb8b
+#define InterruptStatus         0xb89
+#define PCMDataRegister         0xf88
+#define CrossChannelControl     0xf8a
+#define SampleRateTimer         0x1388
+#define SampleBufferCount       0x1389
+#define LocalSpeakerTimerCount  0x138a
+#define LocalTimerControl       0x138b
+#define SampleSizeConfiguration 0x8389
+
+#define AudioMuteFlag             0x20
+#define SampleRateTimerGateFlag   0x40
+#define SampleBufferCountGateFlag 0x80
+
+#define SampleRateInterruptFlag   0x04
+#define SampleBufferInterruptFlag 0x08
+
+#define PAS_SampleSizeMask     0xf3
+#define PAS_SignedSampleMask   0xe3
+#define PAS_16BitSampleFlag    0x04
+#define PAS_UnsignedSampleFlag 0x10
+//bSC2msbinv   equ   00010000b   ;; invert MSB from standard method
+
+#define PAS_OverSamplingMask 0xfc
+
+#define PAS_1xOverSampling  0x00
+#define PAS_2xOverSampling  0x01
+#define PAS_4xOverSampling  0x03
+
+#define PAS_StereoFlag      0x20
+
+#define PAS_AudioMuteFlag  0x20
+
+#define DEFAULT_BASE ( 0x0388 ^ 0x388 ) /* default base I/O address */
+#define ALT_BASE_1   ( 0x0384 ^ 0x388 ) /* first alternate address  */
+#define ALT_BASE_2   ( 0x038C ^ 0x388 ) /* second alternate address */
+#define ALT_BASE_3   ( 0x0288 ^ 0x388 ) /* third alternate address  */
+
+#define PAS_DMAEnable          0x80
+#define PAS_ChannelConnectMask 0x0f
+#define PAS_PCMStartDAC        0xD0
+#define PAS_PCMStartADC        0xC0
+#define PAS_PCMStopMask        0x3f
+
+#define RECORD   0
+#define PLAYBACK 1
+
+#define SelectSampleRateTimer   0x36   // 00110110b
+#define SelectSampleBufferCount 0x74   // 01110100b
+
+#define CalcTimeInterval( rate ) \
+   ( 1193180UL / ( rate ) )
+
+#define CalcSamplingRate( interval ) \
+   ( 1193180UL / ( interval ) )
+
+#define MV_Signature                 0x4d56
+#define MV_SoundInt                  0x2f
+#define MV_CheckForDriver            0xbc00
+#define MV_GetVersion                0xbc01
+#define MV_GetPointerToStateTable    0xbc02
+#define MV_GetPointerToFunctionTable 0xbc03
+#define MV_GetDmaIrqInt              0xbc04
+#define MV_SendCommandStructure      0xbc05
+#define MV_GetDriverMessage          0xbc06
+#define MV_SetHotkeyScanCodes        0xbc0a
+#define MV_GetPathToDriver           0xbc0b
+
+#define OUTPUTMIXER     0x00         /* output mixer H/W select */
+#define INPUTMIXER      0x40         /* input mixer select      */
+#define DEFMIXER        -1           /* use last mixer selected   */
+
+/* left channel values */
+
+#define L_FM            0x01
+#define L_IMIXER        0x02
+#define L_EXT           0x03
+#define L_INT           0x04
+#define L_MIC           0x05
+#define L_PCM           0x06
+#define L_SPEAKER       0x07
+#define L_FREE          0x00
+#define L_SBDAC         0x00
+
+/* right channel values */
+
+#define R_FM            0x08
+#define R_IMIXER        0x09
+#define R_EXT           0x0A
+#define R_INT           0x0B
+#define R_MIC           0x0C
+#define R_PCM           0x0D
+#define R_SPEAKER       0x0E
+#define R_FREE          0x0F
+#define R_SBDAC         0x0F
+
+typedef struct
+   {
+   unsigned char  sysspkrtmr;   /*   42 System Speaker Timer Address */
+   unsigned char  systmrctlr;   /*   43 System Timer Control         */
+   unsigned char  sysspkrreg;   /*   61 System Speaker Register      */
+   unsigned char  joystick;     /*  201 Joystick Register            */
+   unsigned char  lfmaddr;      /*  388 Left  FM Synth Address       */
+   unsigned char  lfmdata;      /*  389 Left  FM Synth Data          */
+   unsigned char  rfmaddr;      /*  38A Right FM Synth Address       */
+   unsigned char  rfmdata;      /*  38B Right FM Synth Data          */
+   unsigned char  dfmaddr;      /*  788 Dual  FM Synth Address       */
+   unsigned char  dfmdata;      /*  789 Dual  FM Synth Data          */
+   unsigned char  RESRVD1[1];   /*      reserved                     */
+   unsigned char  paudiomixr;   /*  78B Paralllel Audio Mixer Control*/
+   unsigned char  audiomixr;    /*  B88 Audio Mixer Control          */
+   unsigned char  intrctlrst;   /*  B89 Interrupt Status             */
+   unsigned char  audiofilt;    /*  B8A Audio Filter Control         */
+   unsigned char  intrctlr;     /*  B8B Interrupt Control            */
+   unsigned char  pcmdata;      /*  F88 PCM Data I/O Register        */
+   unsigned char  RESRVD2;      /*      reserved                     */
+   unsigned char  crosschannel; /*  F8A Cross Channel                */
+   unsigned char  RESRVD3;      /*      reserved                     */
+   unsigned short samplerate;   /* 1388 Sample Rate Timer            */
+   unsigned short samplecnt;    /* 1389 Sample Count Register        */
+   unsigned short spkrtmr;      /* 138A Shadow Speaker Timer Count   */
+   unsigned char  tmrctlr;      /* 138B Shadow Speaker Timer Control */
+   unsigned char  mdirqvect;    /* 1788 MIDI IRQ Vector Register     */
+   unsigned char  mdsysctlr;    /* 1789 MIDI System Control Register */
+   unsigned char  mdsysstat;    /* 178A MIDI IRQ Status Register     */
+   unsigned char  mdirqclr;     /* 178B MIDI IRQ Clear Register      */
+   unsigned char  mdgroup1;     /* 1B88 MIDI Group #1 Register       */
+   unsigned char  mdgroup2;     /* 1B89 MIDI Group #2 Register       */
+   unsigned char  mdgroup3;     /* 1B8A MIDI Group #3 Register       */
+   unsigned char  mdgroup4;     /* 1B8B MIDI Group #4 Register       */
+   } MVState;
+
+typedef struct
+   {
+   unsigned long SetMixer;
+   unsigned long SetVolume;
+   unsigned long SetFilter;
+   unsigned long SetCrossChannel;
+   unsigned long GetMixer;
+   unsigned long GetVolume;
+   unsigned long GetFilter;
+   unsigned long GetCrossChannel;
+   unsigned long ReadSound;
+   unsigned long FMSplit;
+   } MVFunc;
+
+int     PAS_CheckForDriver( void );
+MVState *PAS_GetStateTable( void );
+MVFunc  *PAS_GetFunctionTable( void );
+int     PAS_GetCardSettings( void );
+void    PAS_EnableInterrupt( void );
+void    PAS_DisableInterrupt( void );
+void    interrupt far PAS_ServiceInterrupt( void );
+//void    interrupt PAS_ServiceInterrupt( void );
+void    PAS_Write( int Register, int Data );
+int     PAS_Read( int Register );
+void    PAS_SetSampleRateTimer( void );
+void    PAS_SetSampleBufferCount( void );
+int     PAS_SetupDMABuffer( char *BufferPtr, int BufferSize, int mode );
+int     PAS_GetFilterSetting( int rate );
+void    PAS_BeginTransfer( int mode );
+int     PAS_TestAddress( int address );
+int     PAS_FindCard( void );
+int     PAS_CallMVFunction( unsigned long function, int ebx, int ecx, int edx );
+void    PAS_SaveState( void );
+void    PAS_RestoreState( void );
+
+
+#pragma aux PAS_TestAddress = \
+   "mov   dx, 0b8bh", \
+   "xor    dx, ax", \
+   "in    al, dx", \
+   "cmp   al, 0ffh", \
+   "je    TestExit", \
+   "mov   ah, al", \
+   "xor   al, 0e0h", \
+   "out   dx, al", \
+   "jmp   TestDelay1", \
+   "TestDelay1:", \
+   "jmp   TestDelay2", \
+   "TestDelay2:", \
+   "in    al, dx", \
+   "xchg  al, ah", \
+   "out   dx, al", \
+   "sub   al, ah", \
+   "TestExit:", \
+   "and   eax, 0ffh" \
+   parm [ eax ] modify exact [ eax dx ];
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/_sndscap.h
@@ -1,0 +1,136 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: _SNDSCAP.H
+
+   author: James R. Dose
+   date:   October 26, 1994
+
+   Private header for SNDSCAPE.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef ___SNDSCAP_H
+#define ___SNDSCAP_H
+
+#define VALID   ( 1 == 1 )
+#define INVALID ( !VALID )
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+#define lobyte( num )   ( ( int )*( ( char * )&( num ) ) )
+#define hibyte( num )   ( ( int )*( ( ( char * )&( num ) ) + 1 ) )
+
+#define STEREO      1
+#define SIXTEEN_BIT 2
+
+#define MONO_8BIT    0
+#define STEREO_8BIT  ( STEREO )
+#define MONO_16BIT   ( SIXTEEN_BIT )
+#define STEREO_16BIT ( STEREO | SIXTEEN_BIT )
+
+#define SOUNDSCAPE_MaxMixMode        STEREO_16BIT
+
+#define MONO_8BIT_SAMPLE_SIZE    1
+#define MONO_16BIT_SAMPLE_SIZE   2
+#define STEREO_8BIT_SAMPLE_SIZE  ( 2 * MONO_8BIT_SAMPLE_SIZE )
+#define STEREO_16BIT_SAMPLE_SIZE ( 2 * MONO_16BIT_SAMPLE_SIZE )
+
+#define SOUNDSCAPE_DefaultSampleRate 11000
+#define SOUNDSCAPE_DefaultMixMode    MONO_8BIT
+#define SOUNDSCAPE_MaxIrq            15
+
+/* Ensoniq gate-array chip defines ... */
+#define ODIE            0       /* ODIE gate array */
+#define OPUS            1       /* OPUS gate array */
+#define MMIC            2       /* MiMIC gate array */
+
+/* relevant direct register defines - offsets from base address */
+#define GA_HOSTCSTAT    2       /* host port ctrl/stat reg */
+#define GA_HOSTDATA     3       /* host port data reg */
+#define GA_REGADDR      4       /* indirect address reg */
+#define GA_REGDATA      5       /* indirect data reg */
+#define SB_IACK      0x22e /* SoundBlaster IACK register */
+
+/* relevant indirect register defines */
+#define GA_DMACHB       3       /* DMA chan B assign reg */
+#define GA_INTCFG       4       /* interrupt configuration reg */
+#define GA_DMACFG       5       /* DMA config reg */
+#define GA_CDCFG        6       /* CD-ROM (AD-1848) config reg */
+#define GA_HMCTL  9  /* host master control reg */
+#define GA_CHIPSEL      10      /* programmable external chip select */
+
+/* AD-1848 chip defines ... */
+/* relevant direct register defines */
+#define AD_REGADDR      0       /* indirect address reg */
+#define AD_REGDATA      1       /* indirect data reg */
+#define AD_STATUS       2       /* status register */
+#define AD_OFFSET       8       /* for some boards, a fixed BasePort offset */
+
+/* relevant indirect register defines */
+#define AD_LEFTOUT      6       /* left DAC output control reg */
+#define AD_RIGHTOUT     7       /* right DAC output control reg */
+#define AD_FORMAT       8       /* clock and data format reg */
+#define AD_CONFIG       9       /* interface config register */
+#define AD_PINCTRL      10      /* external pin control reg */
+#define AD_UCOUNT       14      /* upper count reg */
+#define AD_LCOUNT       15      /* lower count reg */
+
+/* some firmware command and communication defines */
+#define SET_CTL      0x88  /* set a control value */
+#define GET_CTL      0x89  /* get a control value */
+#define SET_REV      0xb0  /* set synth reverb */
+#define SYNTH_VOL 0x04  /* Synth Vol control number */
+#define RXRDY     0x01  /* Receive-Ready bit mask */
+#define TXRDY     0x02  /* Transmit-Ready bit mask */
+
+/* some miscellaneous defines ... soundscape reg values, sytem int regs, ... */
+#define INTCONT1        0x20    /* Interrupt Controller 1 control register */
+#define INTCONT2        0xa0    /* Interrupt Controller 2 control register */
+#define INTMASK1        0x21    /* Interrupt Controller 1 mask register */
+#define INTMASK2        0xa1    /* Interrupt Controller 2 mask register */
+#define VECTBASE1       0x08    /* vector base for XT interrupts */
+#define VECTBASE2       0x70    /* vector base for AT extended interrupts */
+#define EOI             0x20    /* End Of Interrupt command */
+#define AUTO_OUT        0x58    /* DMA controller mode */
+
+static void SOUNDSCAPE_EnableInterrupt( void );
+static void SOUNDSCAPE_DisableInterrupt( void );
+static void __interrupt __far SOUNDSCAPE_ServiceInterrupt( void );
+static int  ga_read( int rnum );
+static void ga_write( int rnum, int value );
+static int  ad_read( int rnum );
+static void ad_write( int rnum, int value );
+static void tdelay( void );
+static void pcm_format( void );
+static int  SOUNDSCAPE_SetupDMABuffer( char *BufferPtr, int BufferSize, int mode );
+static int  SOUNDSCAPE_BeginPlayback( int length );
+static void SOUNDSCAPE_LockEnd( void );
+static void SOUNDSCAPE_UnlockMemory( void );
+static int  SOUNDSCAPE_LockMemory( void );
+static unsigned short allocateTimerStack( unsigned short size );
+static void deallocateTimerStack( unsigned short selector );
+static int  parse( char *val, char *str, FILE *p1 );
+static int  SOUNDSCAPE_FindCard( void );
+static int  SOUNDSCAPE_Setup( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/adlibfx.c
@@ -1,0 +1,552 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: ADLIBFX.C
+
+   author: James R. Dose
+   date:   April 1, 1994
+
+   Low level routines to support Adlib sound effects created by Muse.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <stdlib.h>
+#include <conio.h>
+#include "dpmi.h"
+#include "task_man.h"
+#include "interrup.h"
+#include "al_midi.h"
+#include "adlibfx.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+static void ADLIBFX_SendOutput( int reg, int data );
+static void ADLIBFX_Service( task *Task );
+
+static long     ADLIBFX_LengthLeft;
+static int      ADLIBFX_Block;
+static ALSound *ADLIBFX_Sound = NULL;
+static char    *ADLIBFX_SoundPtr = NULL;
+static int      ADLIBFX_Priority;
+static unsigned long ADLIBFX_CallBackVal;
+static void     ( *ADLIBFX_CallBackFunc )( unsigned long ) = NULL;
+static int      ADLIBFX_SoundVolume;
+static int      ADLIBFX_TotalVolume = ADLIBFX_MaxVolume;
+static task    *ADLIBFX_ServiceTask = NULL;
+static int      ADLIBFX_VoiceHandle = ADLIBFX_MinVoiceHandle;
+
+int ADLIBFX_Installed = FALSE;
+
+int ADLIBFX_ErrorCode = ADLIBFX_Ok;
+
+#define ADLIBFX_SetErrorCode( status ) \
+   ADLIBFX_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *ADLIBFX_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case ADLIBFX_Warning :
+      case ADLIBFX_Error :
+         ErrorString = ADLIBFX_ErrorString( ADLIBFX_ErrorCode );
+         break;
+
+      case ADLIBFX_Ok :
+         ErrorString = "Adlib FX ok.";
+         break;
+
+      case ADLIBFX_NoVoices :
+         ErrorString = "No free voices available in Adlib FX.";
+         break;
+
+      case ADLIBFX_VoiceNotFound :
+         ErrorString = "No voice with matching handle found.";
+         break;
+
+      case ADLIBFX_DPMI_Error :
+         ErrorString = "DPMI Error in AdlibFX.";
+         break;
+
+      default :
+         ErrorString = "Unknown Adlib FX error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define ADLIBFX_LockStart ADLIBFX_SendOutput
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_SendOutput
+
+   Writes a byte of data to the specified register on the Adlib card.
+---------------------------------------------------------------------*/
+
+static void ADLIBFX_SendOutput
+   (
+   int reg,
+   int data
+   )
+
+   {
+   int i;
+   int adlib_port = 0x388;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   outp( adlib_port, reg );
+
+   for( i = 6; i ; i-- )
+      {
+      inp( adlib_port );
+      }
+
+   outp( adlib_port + 1, data );
+
+   for( i = 35; i ; i-- )
+      {
+      inp( adlib_port );
+      }
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_Stop
+
+   Halts playback of the currently playing sound effect.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_Stop
+   (
+   int handle
+   )
+
+   {
+   unsigned flags;
+
+   if ( ( handle != ADLIBFX_VoiceHandle ) || ( ADLIBFX_Sound == NULL ) )
+      {
+      ADLIBFX_SetErrorCode( ADLIBFX_VoiceNotFound );
+      return( ADLIBFX_Warning );
+      }
+
+   flags = DisableInterrupts();
+
+   ADLIBFX_SendOutput( 0xb0, 0 );
+
+   ADLIBFX_Sound      = NULL;
+   ADLIBFX_SoundPtr   = NULL;
+   ADLIBFX_LengthLeft = 0;
+   ADLIBFX_Priority   = 0;
+
+   RestoreInterrupts( flags );
+
+   if ( ADLIBFX_CallBackFunc )
+      {
+      ADLIBFX_CallBackFunc( ADLIBFX_CallBackVal );
+      }
+
+   return( ADLIBFX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_Service
+
+   Task Manager routine to perform the playback of a sound effect.
+---------------------------------------------------------------------*/
+
+static void ADLIBFX_Service
+   (
+   task *Task
+   )
+
+   {
+   int value;
+
+   if ( ADLIBFX_SoundPtr )
+      {
+      value = *ADLIBFX_SoundPtr++;
+      if ( value != 0 )
+         {
+         ADLIBFX_SendOutput( 0xa0, value );
+         ADLIBFX_SendOutput( 0xb0, ADLIBFX_Block );
+         }
+      else
+         {
+         ADLIBFX_SendOutput( 0xb0, 0 );
+         }
+
+      ADLIBFX_LengthLeft--;
+      if ( ADLIBFX_LengthLeft <= 0 )
+         {
+         ADLIBFX_Stop( ADLIBFX_VoiceHandle );
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_SetVolume
+
+   Sets the volume of the currently playing sound effect.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_SetVolume
+   (
+   int handle,
+   int volume
+   )
+
+   {
+   unsigned flags;
+   int      carrierlevel;
+
+   flags = DisableInterrupts();
+   if ( ( handle != ADLIBFX_VoiceHandle ) || ( ADLIBFX_Sound == NULL ) )
+      {
+      RestoreInterrupts( flags );
+      ADLIBFX_SetErrorCode( ADLIBFX_VoiceNotFound );
+      return( ADLIBFX_Warning );
+      }
+
+   volume  = min( volume, ADLIBFX_MaxVolume );
+   volume  = max( volume, 0 );
+   ADLIBFX_SoundVolume = volume;
+
+   volume *= ADLIBFX_TotalVolume;
+   volume /= ADLIBFX_MaxVolume;
+
+   carrierlevel  = ADLIBFX_Sound->cScale & 0x3f;
+   carrierlevel ^= 0x3f;
+   carrierlevel *= ( volume / 2 ) + 0x80;
+   carrierlevel /= ADLIBFX_MaxVolume;
+   carrierlevel ^= 0x3f;
+   carrierlevel |= ADLIBFX_Sound->cScale & 0xc0;
+
+   ADLIBFX_SendOutput( 0x43, carrierlevel );
+
+   RestoreInterrupts( flags );
+   return( ADLIBFX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_SetTotalVolume
+
+   Sets the total volume of the sound effect.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_SetTotalVolume
+   (
+   int volume
+   )
+
+   {
+   volume = max( volume, 0 );
+   volume = min( volume, ADLIBFX_MaxVolume );
+
+   ADLIBFX_TotalVolume = volume;
+   ADLIBFX_SetVolume( ADLIBFX_VoiceHandle, ADLIBFX_SoundVolume );
+
+   return( ADLIBFX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_GetTotalVolume
+
+   Returns the total volume of the sound effect.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_GetTotalVolume
+   (
+   void
+   )
+
+   {
+   return( ADLIBFX_TotalVolume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_VoiceAvailable
+
+   Checks if a voice can be play at the specified priority.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_VoiceAvailable
+   (
+   int priority
+   )
+
+   {
+   if ( priority < ADLIBFX_Priority )
+      {
+      return( FALSE );
+      }
+
+   return( TRUE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_Play
+
+   Starts playback of a Muse sound effect.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_Play
+   (
+   ALSound *sound,
+   int volume,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   unsigned flags;
+   int      carrierlevel;
+
+   if ( priority < ADLIBFX_Priority )
+      {
+      ADLIBFX_SetErrorCode( ADLIBFX_NoVoices );
+      return( ADLIBFX_Warning );
+      }
+
+   ADLIBFX_Stop( ADLIBFX_VoiceHandle );
+
+   ADLIBFX_VoiceHandle++;
+   if ( ADLIBFX_VoiceHandle < ADLIBFX_MinVoiceHandle )
+      {
+      ADLIBFX_VoiceHandle = ADLIBFX_MinVoiceHandle;
+      }
+
+   flags = DisableInterrupts();
+
+   ADLIBFX_LengthLeft  = sound->length;
+   ADLIBFX_Priority    = priority;
+   ADLIBFX_Sound       = sound;
+   ADLIBFX_SoundPtr    = &sound->data;
+   ADLIBFX_CallBackVal = callbackval;
+
+   ADLIBFX_Block = ( ( sound->block & 7 ) << 2 ) | 0x20;
+
+   volume = min( volume, ADLIBFX_MaxVolume );
+   volume = max( volume, 0 );
+   ADLIBFX_SoundVolume = volume;
+
+   volume *= ADLIBFX_TotalVolume;
+   volume /= ADLIBFX_MaxVolume;
+
+   carrierlevel  = sound->cScale & 0x3f;
+   carrierlevel ^= 0x3f;
+   carrierlevel *= ( volume / 2 ) + 0x80;
+   carrierlevel /= ADLIBFX_MaxVolume;
+   carrierlevel ^= 0x3f;
+   carrierlevel |= sound->cScale & 0xc0;
+
+   ADLIBFX_SendOutput( 0x20, sound->mChar );
+   ADLIBFX_SendOutput( 0x40, sound->mScale );
+   ADLIBFX_SendOutput( 0x60, sound->mAttack );
+   ADLIBFX_SendOutput( 0x80, sound->mSus );
+   ADLIBFX_SendOutput( 0xe0, sound->mWave );
+
+   ADLIBFX_SendOutput( 0x23, sound->cChar );
+   ADLIBFX_SendOutput( 0x43, carrierlevel );
+   ADLIBFX_SendOutput( 0x63, sound->cAttack );
+   ADLIBFX_SendOutput( 0x83, sound->cSus );
+   ADLIBFX_SendOutput( 0xe3, sound->cWave );
+
+   ADLIBFX_SendOutput( 0xc0, 0 );
+
+   RestoreInterrupts( flags );
+
+   return( ADLIBFX_VoiceHandle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_SoundPlaying
+
+   Checks if a sound effect is currently playing.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_SoundPlaying
+   (
+   int handle
+   )
+
+   {
+   int status;
+
+   status = FALSE;
+   if ( ( handle == ADLIBFX_VoiceHandle ) && ( ADLIBFX_LengthLeft > 0 ) )
+      {
+      status = TRUE;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void ADLIBFX_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_SetCallBack
+
+   Set the function to call when a voice stops.
+---------------------------------------------------------------------*/
+
+void ADLIBFX_SetCallBack
+   (
+   void ( *function )( unsigned long )
+   )
+
+   {
+   ADLIBFX_CallBackFunc = function;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_Init
+
+   Initializes the sound effect engine.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_Init
+   (
+   void
+   )
+
+   {
+   int status;
+
+   if ( ADLIBFX_Installed )
+      {
+      ADLIBFX_Shutdown();
+      }
+
+   status  = DPMI_LockMemoryRegion( ADLIBFX_LockStart, ADLIBFX_LockEnd );
+   status |= DPMI_Lock( ADLIBFX_VoiceHandle );
+   status |= DPMI_Lock( ADLIBFX_Sound );
+   status |= DPMI_Lock( ADLIBFX_ErrorCode );
+   status |= DPMI_Lock( ADLIBFX_SoundPtr );
+   status |= DPMI_Lock( ADLIBFX_LengthLeft );
+   status |= DPMI_Lock( ADLIBFX_Priority );
+   status |= DPMI_Lock( ADLIBFX_CallBackFunc );
+   status |= DPMI_Lock( ADLIBFX_Block );
+
+   if ( status != DPMI_Ok )
+      {
+      ADLIBFX_SetErrorCode( ADLIBFX_DPMI_Error );
+      return( ADLIBFX_Error );
+      }
+
+//JIM
+//   AL_ReserveVoice( 0 );
+   ADLIBFX_Stop( ADLIBFX_VoiceHandle );
+   ADLIBFX_ServiceTask = TS_ScheduleTask( &ADLIBFX_Service, 140, 2, NULL );
+   TS_Dispatch();
+   ADLIBFX_Installed = TRUE;
+   ADLIBFX_CallBackFunc = NULL;
+
+   ADLIBFX_SetErrorCode( ADLIBFX_Ok );
+   return( ADLIBFX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ADLIBFX_Shutdown
+
+   Ends the use of the sound effect engine.
+---------------------------------------------------------------------*/
+
+int ADLIBFX_Shutdown
+   (
+   void
+   )
+
+   {
+   if ( ADLIBFX_Installed )
+      {
+      ADLIBFX_Stop( ADLIBFX_VoiceHandle );
+      TS_Terminate( ADLIBFX_ServiceTask );
+      ADLIBFX_ServiceTask = NULL;
+//JIM
+//      AL_ReleaseVoice( 0 );
+      ADLIBFX_Installed = FALSE;
+
+      DPMI_UnlockMemoryRegion( ADLIBFX_LockStart, ADLIBFX_LockEnd );
+      DPMI_Unlock( ADLIBFX_VoiceHandle );
+      DPMI_Unlock( ADLIBFX_Sound );
+      DPMI_Unlock( ADLIBFX_ErrorCode );
+      DPMI_Unlock( ADLIBFX_SoundPtr );
+      DPMI_Unlock( ADLIBFX_LengthLeft );
+      DPMI_Unlock( ADLIBFX_Priority );
+      DPMI_Unlock( ADLIBFX_CallBackFunc );
+      DPMI_Unlock( ADLIBFX_Block );
+      }
+
+   ADLIBFX_SetErrorCode( ADLIBFX_Ok );
+   return( ADLIBFX_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/adlibfx.h
@@ -1,0 +1,80 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: ADLIBFX.H
+
+   author: James R. Dose
+   date:   April 1, 1994
+
+   Public header for ADLIBFX.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __ADLIBFX_H
+#define __ADLIBFX_H
+
+enum ADLIBFX_Errors
+   {
+   ADLIBFX_Warning = -2,
+   ADLIBFX_Error   = -1,
+   ADLIBFX_Ok      = 0,
+   ADLIBFX_NoVoices,
+   ADLIBFX_VoiceNotFound,
+   ADLIBFX_DPMI_Error
+   };
+
+typedef	struct
+   {
+   unsigned long  length;
+   short int      priority;
+   char           mChar, cChar;
+   char           mScale, cScale;
+   char           mAttack, cAttack;
+   char           mSus, cSus;
+   char           mWave, cWave;
+   char           nConn;
+   char           voice;
+   char           mode;
+   char           unused[ 3 ];
+   char           block;
+   char           data[];
+   } ALSound;
+
+#define ADLIBFX_MaxVolume      255
+#define ADLIBFX_MinVoiceHandle 1
+
+char *ADLIBFX_ErrorString( int ErrorNumber );
+int   ADLIBFX_Stop( int handle );
+int   ADLIBFX_SetVolume( int handle, int volume );
+int   ADLIBFX_SetTotalVolume( int volume );
+int   ADLIBFX_GetTotalVolume( void );
+int   ADLIBFX_VoiceAvailable( int priority );
+int   ADLIBFX_Play( ALSound *sound, int volume, int priority, unsigned long callbackval );
+int   ADLIBFX_SoundPlaying( int handle );
+void  ADLIBFX_SetCallBack( void ( *function )( unsigned long ) );
+int   ADLIBFX_Init( void );
+int   ADLIBFX_Shutdown( void );
+   #pragma aux ADLIBFX_Shutdown frame;
+void  PCFX_UnlockMemory( void );
+   #pragma aux ADLIBFX_UnlockMemory frame;
+int   PCFX_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/al_midi.c
@@ -1,0 +1,1510 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: AL_MIDI.C
+
+   author: James R. Dose
+   date:   April 1, 1994
+
+   Low level routines to support General MIDI music on Adlib compatible
+   cards.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <conio.h>
+#include <dos.h>
+#include <stddef.h>
+#include <stdlib.h>
+//#include <math.h>
+#include "dpmi.h"
+#include "interrup.h"
+#include "sndcards.h"
+#include "blaster.h"
+#include "user.h"
+#include "al_midi.h"
+#include "_al_midi.h"
+#include "ll_man.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+static unsigned OctavePitch[ MAX_OCTAVE + 1 ] =
+   {
+   OCTAVE_0, OCTAVE_1, OCTAVE_2, OCTAVE_3,
+   OCTAVE_4, OCTAVE_5, OCTAVE_6, OCTAVE_7,
+   };
+
+static unsigned NoteMod12[ MAX_NOTE + 1 ];
+static unsigned NoteDiv12[ MAX_NOTE + 1 ];
+
+// Pitch table
+
+//static unsigned NotePitch[ FINETUNE_MAX + 1 ][ 12 ] =
+//   {
+//      { C, C_SHARP, D, D_SHARP, E, F, F_SHARP, G, G_SHARP, A, A_SHARP, B },
+//   };
+
+static unsigned NotePitch[ FINETUNE_MAX + 1 ][ 12 ] =
+   {
+      { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287 },
+      { 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x242, 0x264, 0x288 },
+      { 0x158, 0x16c, 0x182, 0x199, 0x1b1, 0x1cb, 0x1e6, 0x203, 0x221, 0x243, 0x265, 0x289 },
+      { 0x158, 0x16c, 0x183, 0x19a, 0x1b2, 0x1cc, 0x1e7, 0x204, 0x222, 0x244, 0x266, 0x28a },
+      { 0x159, 0x16d, 0x183, 0x19a, 0x1b3, 0x1cd, 0x1e8, 0x205, 0x223, 0x245, 0x267, 0x28b },
+      { 0x15a, 0x16e, 0x184, 0x19b, 0x1b3, 0x1ce, 0x1e9, 0x206, 0x224, 0x246, 0x268, 0x28c },
+      { 0x15a, 0x16e, 0x185, 0x19c, 0x1b4, 0x1ce, 0x1ea, 0x207, 0x225, 0x247, 0x269, 0x28e },
+      { 0x15b, 0x16f, 0x185, 0x19d, 0x1b5, 0x1cf, 0x1eb, 0x208, 0x226, 0x248, 0x26a, 0x28f },
+      { 0x15b, 0x170, 0x186, 0x19d, 0x1b6, 0x1d0, 0x1ec, 0x209, 0x227, 0x249, 0x26b, 0x290 },
+      { 0x15c, 0x170, 0x187, 0x19e, 0x1b7, 0x1d1, 0x1ec, 0x20a, 0x228, 0x24a, 0x26d, 0x291 },
+      { 0x15d, 0x171, 0x188, 0x19f, 0x1b7, 0x1d2, 0x1ed, 0x20b, 0x229, 0x24b, 0x26e, 0x292 },
+      { 0x15d, 0x172, 0x188, 0x1a0, 0x1b8, 0x1d3, 0x1ee, 0x20c, 0x22a, 0x24c, 0x26f, 0x293 },
+      { 0x15e, 0x172, 0x189, 0x1a0, 0x1b9, 0x1d4, 0x1ef, 0x20d, 0x22b, 0x24d, 0x270, 0x295 },
+      { 0x15f, 0x173, 0x18a, 0x1a1, 0x1ba, 0x1d4, 0x1f0, 0x20e, 0x22c, 0x24e, 0x271, 0x296 },
+      { 0x15f, 0x174, 0x18a, 0x1a2, 0x1bb, 0x1d5, 0x1f1, 0x20f, 0x22d, 0x24f, 0x272, 0x297 },
+      { 0x160, 0x174, 0x18b, 0x1a3, 0x1bb, 0x1d6, 0x1f2, 0x210, 0x22e, 0x250, 0x273, 0x298 },
+      { 0x161, 0x175, 0x18c, 0x1a3, 0x1bc, 0x1d7, 0x1f3, 0x211, 0x22f, 0x251, 0x274, 0x299 },
+      { 0x161, 0x176, 0x18c, 0x1a4, 0x1bd, 0x1d8, 0x1f4, 0x212, 0x230, 0x252, 0x276, 0x29b },
+      { 0x162, 0x176, 0x18d, 0x1a5, 0x1be, 0x1d9, 0x1f5, 0x212, 0x231, 0x254, 0x277, 0x29c },
+      { 0x162, 0x177, 0x18e, 0x1a6, 0x1bf, 0x1d9, 0x1f5, 0x213, 0x232, 0x255, 0x278, 0x29d },
+      { 0x163, 0x178, 0x18f, 0x1a6, 0x1bf, 0x1da, 0x1f6, 0x214, 0x233, 0x256, 0x279, 0x29e },
+      { 0x164, 0x179, 0x18f, 0x1a7, 0x1c0, 0x1db, 0x1f7, 0x215, 0x235, 0x257, 0x27a, 0x29f },
+      { 0x164, 0x179, 0x190, 0x1a8, 0x1c1, 0x1dc, 0x1f8, 0x216, 0x236, 0x258, 0x27b, 0x2a1 },
+      { 0x165, 0x17a, 0x191, 0x1a9, 0x1c2, 0x1dd, 0x1f9, 0x217, 0x237, 0x259, 0x27c, 0x2a2 },
+      { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1de, 0x1fa, 0x218, 0x238, 0x25a, 0x27e, 0x2a3 },
+      { 0x166, 0x17b, 0x192, 0x1aa, 0x1c3, 0x1df, 0x1fb, 0x219, 0x239, 0x25b, 0x27f, 0x2a4 },
+      { 0x167, 0x17c, 0x193, 0x1ab, 0x1c4, 0x1e0, 0x1fc, 0x21a, 0x23a, 0x25c, 0x280, 0x2a6 },
+      { 0x168, 0x17d, 0x194, 0x1ac, 0x1c5, 0x1e0, 0x1fd, 0x21b, 0x23b, 0x25d, 0x281, 0x2a7 },
+      { 0x168, 0x17d, 0x194, 0x1ad, 0x1c6, 0x1e1, 0x1fe, 0x21c, 0x23c, 0x25e, 0x282, 0x2a8 },
+      { 0x169, 0x17e, 0x195, 0x1ad, 0x1c7, 0x1e2, 0x1ff, 0x21d, 0x23d, 0x260, 0x283, 0x2a9 },
+      { 0x16a, 0x17f, 0x196, 0x1ae, 0x1c8, 0x1e3, 0x1ff, 0x21e, 0x23e, 0x261, 0x284, 0x2ab },
+      { 0x16a, 0x17f, 0x197, 0x1af, 0x1c8, 0x1e4, 0x200, 0x21f, 0x23f, 0x262, 0x286, 0x2ac }
+   };
+
+// Slot numbers as a function of the voice and the operator.
+// ( melodic only)
+
+static int slotVoice[ NUM_VOICES ][ 2 ] =
+   {
+      { 0, 3 },    // voice 0
+      { 1, 4 },    // 1
+      { 2, 5 },    // 2
+      { 6, 9 },    // 3
+      { 7, 10 },   // 4
+      { 8, 11 },   // 5
+      { 12, 15 },  // 6
+      { 13, 16 },  // 7
+      { 14, 17 },  // 8
+   };
+
+static int VoiceLevel[ NumChipSlots ][ 2 ];
+static int VoiceKsl[ NumChipSlots ][ 2 ];
+
+// This table gives the offset of each slot within the chip.
+// offset = fn( slot)
+
+static char offsetSlot[ NumChipSlots ] =
+   {
+    0,  1,  2,  3,  4,  5,
+    8,  9, 10, 11, 12, 13,
+   16, 17, 18, 19, 20, 21
+   };
+
+static int VoiceReserved[ NUM_VOICES * 2 ] =
+   {
+   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
+   };
+
+static VOICE     Voice[ NUM_VOICES * 2 ];
+static VOICELIST Voice_Pool;
+
+static CHANNEL   Channel[ NUM_CHANNELS ];
+
+static int AL_LeftPort   = 0x388;
+static int AL_RightPort  = 0x388;
+static int AL_Stereo     = FALSE;
+static int AL_SendStereo = FALSE;
+static int AL_OPL3       = FALSE;
+static int AL_MaxMidiChannel = 16;
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define AL_LockStart AL_SendOutputToPort
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SendOutputToPort
+
+   Sends data to the Adlib using a specified port.
+---------------------------------------------------------------------*/
+
+void AL_SendOutputToPort
+   (
+   int  port,
+   int  reg,
+   int  data
+   )
+
+   {
+   int delay;
+
+   outp( port, reg );
+
+   for( delay = 6; delay > 0 ; delay-- )
+//   for( delay = 2; delay > 0 ; delay-- )
+      {
+      inp( port );
+      }
+
+   outp( port + 1, data );
+
+//   for( delay = 35; delay > 0 ; delay-- )
+   for( delay = 27; delay > 0 ; delay-- )
+//   for( delay = 2; delay > 0 ; delay-- )
+      {
+      inp( port );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SendOutput
+
+   Sends data to the Adlib.
+---------------------------------------------------------------------*/
+
+void AL_SendOutput
+   (
+   int  voice,
+   int  reg,
+   int  data
+   )
+
+   {
+   int port;
+
+   if ( AL_SendStereo )
+      {
+      AL_SendOutputToPort( AL_LeftPort, reg, data );
+      AL_SendOutputToPort( AL_RightPort, reg, data );
+      }
+   else
+      {
+      port = ( voice == 0 ) ? AL_RightPort : AL_LeftPort;
+      AL_SendOutputToPort( port, reg, data );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetVoiceTimbre
+
+   Programs the specified voice's timbre.
+---------------------------------------------------------------------*/
+
+static void AL_SetVoiceTimbre
+   (
+   int voice
+   )
+
+   {
+   int    off;
+   int    slot;
+   int    port;
+   int    voc;
+   int    patch;
+   int    channel;
+   TIMBRE *timbre;
+
+   channel = Voice[ voice ].channel;
+
+   if ( channel == 9 )
+      {
+      patch = Voice[ voice ].key + 128;
+      }
+   else
+      {
+      patch = Channel[ channel ].Timbre;
+      }
+
+   if ( Voice[ voice ].timbre == patch )
+      {
+      return;
+      }
+
+   Voice[ voice ].timbre = patch;
+   timbre = &ADLIB_TimbreBank[ patch ];
+
+   port = Voice[ voice ].port;
+   voc  = ( voice >= NUM_VOICES ) ? voice - NUM_VOICES : voice;
+   slot = slotVoice[ voc ][ 0 ];
+   off  = offsetSlot[ slot ];
+
+   VoiceLevel[ slot ][ port ] = 63 - ( timbre->Level[ 0 ] & 0x3f );
+   VoiceKsl[ slot ][ port ]   = timbre->Level[ 0 ] & 0xc0;
+
+   AL_SendOutput( port, 0xA0 + voc, 0 );
+   AL_SendOutput( port, 0xB0 + voc, 0 );
+
+   // Let voice clear the release
+   AL_SendOutput( port, 0x80 + off, 0xff );
+
+   AL_SendOutput( port, 0x60 + off, timbre->Env1[ 0 ] );
+   AL_SendOutput( port, 0x80 + off, timbre->Env2[ 0 ] );
+   AL_SendOutput( port, 0x20 + off, timbre->SAVEK[ 0 ] );
+   AL_SendOutput( port, 0xE0 + off, timbre->Wave[ 0 ] );
+
+   AL_SendOutput( port, 0x40 + off, timbre->Level[ 0 ] );
+   slot = slotVoice[ voc ][ 1 ];
+
+   if ( AL_SendStereo )
+      {
+      AL_SendOutputToPort( AL_LeftPort, 0xC0 + voice,
+         ( timbre->Feedback & 0x0f ) | 0x20 );
+      AL_SendOutputToPort( AL_RightPort, 0xC0 + voice,
+         ( timbre->Feedback & 0x0f ) | 0x10 );
+      }
+   else
+      {
+      if ( AL_OPL3 )
+         {
+         AL_SendOutput( port, 0xC0 + voc, ( timbre->Feedback & 0x0f ) |
+            0x30 );
+         }
+      else
+         {
+         AL_SendOutputToPort( ADLIB_PORT, 0xC0 + voice, timbre->Feedback );
+         }
+      }
+
+   off = offsetSlot[ slot ];
+
+   VoiceLevel[ slot ][ port ] = 63 - ( timbre->Level[ 1 ] & 0x3f );
+   VoiceKsl[ slot ][ port ]   = timbre->Level[ 1 ] & 0xc0;
+   AL_SendOutput( port, 0x40 + off, 63 );
+
+   // Let voice clear the release
+   AL_SendOutput( port, 0x80 + off, 0xff );
+
+   AL_SendOutput( port, 0x60 + off, timbre->Env1[ 1 ] );
+   AL_SendOutput( port, 0x80 + off, timbre->Env2[ 1 ] );
+   AL_SendOutput( port, 0x20 + off, timbre->SAVEK[ 1 ] );
+   AL_SendOutput( port, 0xE0 + off, timbre->Wave[ 1 ] );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetVoiceVolume
+
+   Sets the volume of the specified voice.
+---------------------------------------------------------------------*/
+
+static void AL_SetVoiceVolume
+   (
+   int voice
+   )
+
+   {
+   int channel;
+   int velocity;
+   int slot;
+   int port;
+   int voc;
+   unsigned long t1;
+   unsigned long t2;
+   unsigned long volume;
+   TIMBRE *timbre;
+
+   channel = Voice[ voice ].channel;
+
+   timbre = &ADLIB_TimbreBank[ Voice[ voice ].timbre ];
+
+   velocity = Voice[ voice ].velocity + timbre->Velocity;
+   velocity = min( velocity, MAX_VELOCITY );
+
+   voc  = ( voice >= NUM_VOICES ) ? voice - NUM_VOICES : voice;
+   slot = slotVoice[ voc ][ 1 ];
+   port = Voice[ voice ].port;
+
+   // amplitude
+   t1  = ( unsigned )VoiceLevel[ slot ][ port ];
+   t1 *= ( velocity + 0x80 );
+   t1  = ( Channel[ channel ].Volume * t1 ) >> 15;
+
+   if ( !AL_SendStereo )
+      {
+      volume  = t1 ^ 63;
+      volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+      AL_SendOutput( port, 0x40 + offsetSlot[ slot ], volume );
+
+      // Check if this timbre is Additive
+      if ( timbre->Feedback & 0x01 )
+         {
+         slot = slotVoice[ voc ][ 0 ];
+
+         // amplitude
+         t2  = ( unsigned )VoiceLevel[ slot ][ port ];
+         t2 *= ( velocity + 0x80 );
+         t2  = ( Channel[ channel ].Volume * t1 ) >> 15;
+
+         volume  = t2 ^ 63;
+         volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+         AL_SendOutput( port, 0x40 + offsetSlot[ slot ], volume );
+         }
+      }
+   else
+      {
+      // Set left channel volume
+      volume = t1;
+      if ( Channel[ channel ].Pan < 64 )
+         {
+         volume *= Channel[ channel ].Pan;
+         volume >>= 6;
+         }
+
+      volume ^= 63;
+      volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+      AL_SendOutputToPort( AL_LeftPort, 0x40 + offsetSlot[ slot ], volume );
+
+      // Set right channel volume
+      volume = t1;
+      if ( Channel[ channel ].Pan > 64 )
+         {
+         volume *= 127 - Channel[ channel ].Pan;
+         volume >>= 6;
+         }
+
+      volume ^= 63;
+      volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+      AL_SendOutputToPort( AL_RightPort, 0x40 + offsetSlot[ slot ], volume );
+
+      // Check if this timbre is Additive
+      if ( timbre->Feedback & 0x01 )
+         {
+         // amplitude
+         t2  = ( unsigned )VoiceLevel[ slot ][ port ];
+         t2 *= ( velocity + 0x80 );
+         t2  = ( Channel[ channel ].Volume * t1 ) >> 15;
+
+         slot = slotVoice[ voc ][ 0 ];
+
+         // Set left channel volume
+         volume = t2;
+         if ( Channel[ channel ].Pan < 64 )
+            {
+            volume *= Channel[ channel ].Pan;
+            volume >>= 6;
+            }
+
+         volume ^= 63;
+         volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+         AL_SendOutputToPort( AL_LeftPort, 0x40 + offsetSlot[ slot ], volume );
+
+         // Set right channel volume
+         volume = t2;
+         if ( Channel[ channel ].Pan > 64 )
+            {
+            volume *= 127 - Channel[ channel ].Pan;
+            volume >>= 6;
+            }
+
+         volume ^= 63;
+         volume |= ( unsigned )VoiceKsl[ slot ][ port ];
+
+         AL_SendOutputToPort( AL_RightPort, 0x40 + offsetSlot[ slot ], volume );
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_AllocVoice
+
+   Retrieves a free voice from the voice pool.
+---------------------------------------------------------------------*/
+
+static int AL_AllocVoice
+   (
+   void
+   )
+
+   {
+   int voice;
+
+   if ( Voice_Pool.start )
+      {
+      voice = Voice_Pool.start->num;
+      LL_Remove( VOICE, &Voice_Pool, &Voice[ voice ] );
+      return( voice );
+      }
+
+   return( AL_VoiceNotFound );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_GetVoice
+
+   Determines which voice is associated with a specified note and
+   MIDI channel.
+---------------------------------------------------------------------*/
+
+static int AL_GetVoice
+   (
+   int channel,
+   int key
+   )
+
+   {
+   VOICE *voice;
+
+   voice = Channel[ channel ].Voices.start;
+
+   while( voice != NULL )
+      {
+      if ( voice->key == key )
+         {
+         return( voice->num );
+         }
+      voice = voice->next;
+      }
+
+   return( AL_VoiceNotFound );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetVoicePitch
+
+   Programs the pitch of the specified voice.
+---------------------------------------------------------------------*/
+
+static void AL_SetVoicePitch
+   (
+   int voice
+   )
+
+   {
+   int note;
+   int channel;
+   int patch;
+   int detune;
+   int ScaleNote;
+   int Octave;
+   int pitch;
+   int port;
+   int voc;
+
+   port = Voice[ voice ].port;
+   voc  = ( voice >= NUM_VOICES ) ? voice - NUM_VOICES : voice;
+   channel = Voice[ voice ].channel;
+
+   if ( channel == 9 )
+      {
+      patch = Voice[ voice ].key + 128;
+      note  = ADLIB_TimbreBank[ patch ].Transpose;
+      }
+   else
+      {
+      patch = Channel[ channel ].Timbre;
+      note  = Voice[ voice ].key + ADLIB_TimbreBank[ patch ].Transpose;
+      }
+
+   note += Channel[ channel ].KeyOffset - 12;
+   if ( note > MAX_NOTE )
+      {
+      note = MAX_NOTE;
+      }
+   if ( note < 0 )
+      {
+      note = 0;
+      }
+
+   detune = Channel[ channel ].KeyDetune;
+
+   ScaleNote = NoteMod12[ note ];
+   Octave    = NoteDiv12[ note ];
+
+   pitch = OctavePitch[ Octave ] | NotePitch[ detune ][ ScaleNote ];
+
+   Voice[ voice ].pitchleft = pitch;
+
+   pitch |= Voice[ voice ].status;
+
+   if ( !AL_SendStereo )
+      {
+      AL_SendOutput( port, 0xA0 + voc, pitch );
+      AL_SendOutput( port, 0xB0 + voc, pitch >> 8 );
+      }
+   else
+      {
+      AL_SendOutputToPort( AL_LeftPort, 0xA0 + voice, pitch );
+      AL_SendOutputToPort( AL_LeftPort, 0xB0 + voice, pitch >> 8 );
+
+      if ( channel != 9 )
+         {
+         detune += STEREO_DETUNE;
+         }
+
+      if ( detune > FINETUNE_MAX )
+         {
+         detune -= FINETUNE_RANGE;
+         if ( note < MAX_NOTE )
+            {
+            note++;
+            ScaleNote = NoteMod12[ note ];
+            Octave    = NoteDiv12[ note ];
+            }
+         }
+
+      pitch = OctavePitch[ Octave ] | NotePitch[ detune ][ ScaleNote ];
+
+      Voice[ voice ].pitchright = pitch;
+
+      pitch |= Voice[ voice ].status;
+
+      AL_SendOutputToPort( AL_RightPort, 0xA0 + voice, pitch );
+      AL_SendOutputToPort( AL_RightPort, 0xB0 + voice, pitch >> 8 );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetChannelVolume
+
+   Sets the volume of the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+static void AL_SetChannelVolume
+   (
+   int channel,
+   int volume
+   )
+
+   {
+   VOICE *voice;
+
+   volume = max( 0, volume );
+   volume = min( volume, AL_MaxVolume );
+   Channel[ channel ].Volume = volume;
+
+   voice = Channel[ channel ].Voices.start;
+   while( voice != NULL )
+      {
+      AL_SetVoiceVolume( voice->num );
+      voice = voice->next;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetChannelPan
+
+   Sets the pan position of the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+static void AL_SetChannelPan
+   (
+   int channel,
+   int pan
+   )
+
+   {
+   // Don't pan drum sounds
+   if ( channel != 9 )
+      {
+      Channel[ channel ].Pan = pan;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetChannelDetune
+
+   Sets the stereo voice detune of the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+static void AL_SetChannelDetune
+   (
+   int channel,
+   int detune
+   )
+
+   {
+   Channel[ channel ].Detune = detune;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_ResetVoices
+
+   Sets all voice info to the default state.
+---------------------------------------------------------------------*/
+
+static void AL_ResetVoices
+   (
+   void
+   )
+
+   {
+   int index;
+   int numvoices;
+
+   Voice_Pool.start = NULL;
+   Voice_Pool.end = NULL;
+
+   numvoices = NUM_VOICES;
+   if ( ( AL_OPL3 ) && ( !AL_Stereo ) )
+      {
+      numvoices = NUM_VOICES * 2;
+      }
+   for( index = 0; index < numvoices; index++ )
+      {
+      if ( VoiceReserved[ index ] == FALSE )
+         {
+         Voice[ index ].num = index;
+         Voice[ index ].key = 0;
+         Voice[ index ].velocity = 0;
+         Voice[ index ].channel = -1;
+         Voice[ index ].timbre = -1;
+         Voice[ index ].port = ( index < NUM_VOICES ) ? 0 : 1;
+         Voice[ index ].status = NOTE_OFF;
+         LL_AddToTail( VOICE, &Voice_Pool, &Voice[ index ] );
+         }
+      }
+
+   for( index = 0; index < NUM_CHANNELS; index++ )
+      {
+      Channel[ index ].Voices.start    = NULL;
+      Channel[ index ].Voices.end      = NULL;
+      Channel[ index ].Timbre          = 0;
+      Channel[ index ].Pitchbend       = 0;
+      Channel[ index ].KeyOffset       = 0;
+      Channel[ index ].KeyDetune       = 0;
+      Channel[ index ].Volume          = AL_DefaultChannelVolume;
+      Channel[ index ].Pan             = 64;
+      Channel[ index ].RPN             = 0;
+      Channel[ index ].PitchBendRange     = AL_DefaultPitchBendRange;
+      Channel[ index ].PitchBendSemiTones = AL_DefaultPitchBendRange / 100;
+      Channel[ index ].PitchBendHundreds  = AL_DefaultPitchBendRange % 100;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_CalcPitchInfo
+
+   Calculates the pitch table.
+---------------------------------------------------------------------*/
+
+static void AL_CalcPitchInfo
+   (
+   void
+   )
+
+   {
+   int    note;
+//   int    finetune;
+//   double detune;
+
+   for( note = 0; note <= MAX_NOTE; note++ )
+      {
+      NoteMod12[ note ] = note % 12;
+      NoteDiv12[ note ] = note / 12;
+      }
+
+//   for( finetune = 1; finetune <= FINETUNE_MAX; finetune++ )
+//      {
+//      detune = pow( 2, ( double )finetune / ( 12.0 * FINETUNE_RANGE ) );
+//      for( note = 0; note < 12; note++ )
+//         {
+//         NotePitch[ finetune ][ note ] = ( ( double )NotePitch[ 0 ][ note ] * detune );
+//         }
+//      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_FlushCard
+
+   Sets all voices to a known (quiet) state.
+---------------------------------------------------------------------*/
+
+void AL_FlushCard
+   (
+   int port
+   )
+
+   {
+   int i;
+   unsigned slot1;
+   unsigned slot2;
+
+   for( i = 0 ; i < NUM_VOICES; i++ )
+      {
+      if ( VoiceReserved[ i ] == FALSE )
+         {
+         slot1 = offsetSlot[ slotVoice[ i ][ 0 ] ];
+         slot2 = offsetSlot[ slotVoice[ i ][ 1 ] ];
+
+         AL_SendOutputToPort( port, 0xA0 + i, 0 );
+         AL_SendOutputToPort( port, 0xB0 + i, 0 );
+
+         AL_SendOutputToPort( port, 0xE0 + slot1, 0 );
+         AL_SendOutputToPort( port, 0xE0 + slot2, 0 );
+
+         // Set the envelope to be fast and quiet
+         AL_SendOutputToPort( port, 0x60 + slot1, 0xff );
+         AL_SendOutputToPort( port, 0x60 + slot2, 0xff );
+         AL_SendOutputToPort( port, 0x80 + slot1, 0xff );
+         AL_SendOutputToPort( port, 0x80 + slot2, 0xff );
+
+         // Maximum attenuation
+         AL_SendOutputToPort( port, 0x40 + slot1, 0xff );
+         AL_SendOutputToPort( port, 0x40 + slot2, 0xff );
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_StereoOn
+
+   Sets the card send info in stereo.
+---------------------------------------------------------------------*/
+
+void AL_StereoOn
+   (
+   void
+   )
+
+   {
+   if ( ( AL_Stereo ) && ( !AL_SendStereo ) )
+      {
+      AL_SendStereo = TRUE;
+      if ( AL_OPL3 )
+         {
+         // Set card to OPL3 operation
+         AL_SendOutputToPort( AL_RightPort, 0x5, 1 );
+         }
+      }
+   else if ( AL_OPL3 )
+      {
+      // Set card to OPL3 operation
+      AL_SendOutputToPort( AL_RightPort, 0x5, 1 );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_StereoOff
+
+   Sets the card send info in mono.
+---------------------------------------------------------------------*/
+
+void AL_StereoOff
+   (
+   void
+   )
+
+   {
+   if ( ( AL_Stereo ) && ( AL_SendStereo ) )
+      {
+      AL_SendStereo = FALSE;
+      if ( AL_OPL3 )
+         {
+         // Set card back to OPL2 operation
+         AL_SendOutputToPort( AL_RightPort, 0x5, 0 );
+         }
+      }
+   else if ( AL_OPL3 )
+      {
+      // Set card back to OPL2 operation
+      AL_SendOutputToPort( AL_RightPort, 0x5, 0 );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_Reset
+
+   Sets the card to a known (quiet) state.
+---------------------------------------------------------------------*/
+
+void AL_Reset
+   (
+   void
+   )
+
+   {
+   AL_SendOutputToPort( ADLIB_PORT, 1, 0x20 );
+   AL_SendOutputToPort( ADLIB_PORT, 0x08, 0 );
+
+   // Set the values: AM Depth, VIB depth & Rhythm
+   AL_SendOutputToPort( ADLIB_PORT, 0xBD, 0 );
+
+   AL_StereoOn();
+
+   if ( ( AL_SendStereo ) || ( AL_OPL3 ) )
+      {
+      AL_FlushCard( AL_LeftPort );
+      AL_FlushCard( AL_RightPort );
+      }
+   else
+      {
+      AL_FlushCard( ADLIB_PORT );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_ReserveVoice
+
+   Marks a voice as being not available for use.  This allows the
+   driver to use the rest of the card while another driver uses the
+   reserved voice.
+---------------------------------------------------------------------*/
+
+int AL_ReserveVoice
+   (
+   int voice
+   )
+
+   {
+   unsigned flags;
+
+   if ( ( voice < 0 ) || ( voice >= NUM_VOICES ) )
+      {
+      return( AL_Error );
+      }
+
+   if ( VoiceReserved[ voice ] )
+      {
+      return( AL_Warning );
+      }
+
+   flags = DisableInterrupts();
+
+   if ( Voice[ voice ].status == NOTE_ON )
+      {
+      AL_NoteOff( Voice[ voice ].channel, Voice[ voice ].key, 0 );
+      }
+
+   VoiceReserved[ voice ] = TRUE;
+   LL_Remove( VOICE, &Voice_Pool, &Voice[ voice ] );
+
+   RestoreInterrupts( flags );
+   return( AL_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_ReleaseVoice
+
+   Marks a previously reserved voice as being free to use.
+---------------------------------------------------------------------*/
+
+int AL_ReleaseVoice
+   (
+   int voice
+   )
+
+   {
+   unsigned flags;
+
+   if ( ( voice < 0 ) || ( voice >= NUM_VOICES ) )
+      {
+      return( AL_Error );
+      }
+
+   if ( !VoiceReserved[ voice ] )
+      {
+      return( AL_Warning );
+      }
+
+   flags = DisableInterrupts();
+
+   VoiceReserved[ voice ] = FALSE;
+   LL_AddToTail( VOICE, &Voice_Pool, &Voice[ voice ] );
+
+   RestoreInterrupts( flags );
+   return( AL_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_NoteOff
+
+   Turns off a note on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void AL_NoteOff
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   int voice;
+   int port;
+   int voc;
+
+   // We only play channels 1 through 10
+   if ( channel > AL_MaxMidiChannel )
+      {
+      return;
+      }
+
+   voice = AL_GetVoice( channel, key );
+
+   if ( voice == AL_VoiceNotFound )
+      {
+      return;
+      }
+
+   Voice[ voice ].status = NOTE_OFF;
+
+   port = Voice[ voice ].port;
+   voc  = ( voice >= NUM_VOICES ) ? voice - NUM_VOICES : voice;
+
+   if ( AL_SendStereo )
+      {
+      AL_SendOutputToPort( AL_LeftPort, 0xB0 + voice,
+         hibyte( Voice[ voice ].pitchleft ) );
+      AL_SendOutputToPort( AL_RightPort, 0xB0 + voice,
+         hibyte( Voice[ voice ].pitchright ) );
+      }
+   else
+      {
+      AL_SendOutput( port, 0xB0 + voc, hibyte( Voice[ voice ].pitchleft ) );
+      }
+
+   LL_Remove( VOICE, &Channel[ channel ].Voices, &Voice[ voice ] );
+   LL_AddToTail( VOICE, &Voice_Pool, &Voice[ voice ] );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_NoteOn
+
+   Plays a note on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void AL_NoteOn
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   int voice;
+
+   // We only play channels 1 through 10
+   if ( channel > AL_MaxMidiChannel )
+      {
+      return;
+      }
+
+   if ( velocity == 0 )
+      {
+      AL_NoteOff( channel, key, velocity );
+      return;
+      }
+
+   voice = AL_AllocVoice();
+
+   if ( voice == AL_VoiceNotFound )
+      {
+      if ( Channel[ 9 ].Voices.start )
+         {
+         AL_NoteOff( 9, Channel[ 9 ].Voices.start->key, 0 );
+         voice = AL_AllocVoice();
+         }
+      if ( voice == AL_VoiceNotFound )
+         {
+         return;
+         }
+      }
+
+   Voice[ voice ].key      = key;
+   Voice[ voice ].channel  = channel;
+   Voice[ voice ].velocity = velocity;
+   Voice[ voice ].status   = NOTE_ON;
+
+   LL_AddToTail( VOICE, &Channel[ channel ].Voices, &Voice[ voice ] );
+
+   AL_SetVoiceTimbre( voice );
+   AL_SetVoiceVolume( voice );
+   AL_SetVoicePitch( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_AllNotesOff
+
+   Turns off all currently playing voices.
+---------------------------------------------------------------------*/
+
+void AL_AllNotesOff
+   (
+   int channel
+   )
+
+   {
+   while( Channel[ channel ].Voices.start != NULL )
+      {
+      AL_NoteOff( channel, Channel[ channel ].Voices.start->key, 0 );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_ControlChange
+
+   Sets the value of a controller on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void AL_ControlChange
+   (
+   int channel,
+   int type,
+   int data
+   )
+
+   {
+   // We only play channels 1 through 10
+   if ( channel > AL_MaxMidiChannel )
+      {
+      return;
+      }
+
+   switch( type )
+      {
+      case MIDI_VOLUME :
+         AL_SetChannelVolume( channel, data );
+         break;
+
+      case MIDI_PAN :
+         AL_SetChannelPan( channel, data );
+         break;
+
+      case MIDI_DETUNE :
+         AL_SetChannelDetune( channel, data );
+         break;
+
+      case MIDI_ALL_NOTES_OFF :
+         AL_AllNotesOff( channel );
+         break;
+
+      case MIDI_RESET_ALL_CONTROLLERS :
+         AL_ResetVoices();
+         AL_SetChannelVolume( channel, AL_DefaultChannelVolume );
+         AL_SetChannelPan( channel, 64 );
+         AL_SetChannelDetune( channel, 0 );
+         break;
+
+      case MIDI_RPN_MSB :
+         Channel[ channel ].RPN &= 0x00FF;
+         Channel[ channel ].RPN |= ( data & 0xFF ) << 8;
+         break;
+
+      case MIDI_RPN_LSB :
+         Channel[ channel ].RPN &= 0xFF00;
+         Channel[ channel ].RPN |= data & 0xFF;
+         break;
+
+      case MIDI_DATAENTRY_MSB :
+         if ( Channel[ channel ].RPN == MIDI_PITCHBEND_RPN )
+            {
+            Channel[ channel ].PitchBendSemiTones = data;
+            Channel[ channel ].PitchBendRange     =
+               Channel[ channel ].PitchBendSemiTones * 100 +
+               Channel[ channel ].PitchBendHundreds;
+            }
+         break;
+
+      case MIDI_DATAENTRY_LSB :
+         if ( Channel[ channel ].RPN == MIDI_PITCHBEND_RPN )
+            {
+            Channel[ channel ].PitchBendHundreds = data;
+            Channel[ channel ].PitchBendRange    =
+               Channel[ channel ].PitchBendSemiTones * 100 +
+               Channel[ channel ].PitchBendHundreds;
+            }
+         break;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_ProgramChange
+
+   Selects the instrument to use on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void AL_ProgramChange
+   (
+   int channel,
+   int patch
+   )
+
+   {
+   // We only play channels 1 through 10
+   if ( channel > AL_MaxMidiChannel )
+      {
+      return;
+      }
+
+   Channel[ channel ].Timbre  = patch;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetPitchBend
+
+   Sets the pitch bend amount on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void AL_SetPitchBend
+   (
+   int channel,
+   int lsb,
+   int msb
+   )
+
+   {
+   int            pitchbend;
+   unsigned long  TotalBend;
+   VOICE         *voice;
+
+   // We only play channels 1 through 10
+   if ( channel > AL_MaxMidiChannel )
+      {
+      return;
+      }
+
+   pitchbend = lsb + ( msb << 8 );
+   Channel[ channel ].Pitchbend = pitchbend;
+
+   TotalBend  = pitchbend * Channel[ channel ].PitchBendRange;
+   TotalBend /= ( PITCHBEND_CENTER / FINETUNE_RANGE );
+
+   Channel[ channel ].KeyOffset  = ( int )( TotalBend / FINETUNE_RANGE );
+   Channel[ channel ].KeyOffset -= Channel[ channel ].PitchBendSemiTones;
+
+   Channel[ channel ].KeyDetune = ( unsigned )( TotalBend % FINETUNE_RANGE );
+
+   voice = Channel[ channel ].Voices.start;
+   while( voice != NULL )
+      {
+      AL_SetVoicePitch( voice->num );
+      voice = voice->next;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_DetectFM
+
+   Determines if an Adlib compatible card is installed in the machine.
+---------------------------------------------------------------------*/
+
+int AL_DetectFM
+   (
+   void
+   )
+
+   {
+   int status1;
+   int status2;
+   int i;
+
+   if ( USER_CheckParameter( NO_ADLIB_DETECTION ) )
+      {
+      return( FALSE );
+      }
+
+   AL_SendOutputToPort( ADLIB_PORT, 4, 0x60 );   // Reset T1 & T2
+   AL_SendOutputToPort( ADLIB_PORT, 4, 0x80 );   // Reset IRQ
+
+   status1 = inp( ADLIB_PORT );
+
+   AL_SendOutputToPort( ADLIB_PORT, 2, 0xff );   // Set timer 1
+   AL_SendOutputToPort( ADLIB_PORT, 4, 0x21 );   // Start timer 1
+
+   for( i = 100; i > 0; i-- )
+      {
+      inp( ADLIB_PORT );
+      }
+
+   status2 = inp( ADLIB_PORT );
+
+   AL_SendOutputToPort( ADLIB_PORT, 4, 0x60 );
+   AL_SendOutputToPort( ADLIB_PORT, 4, 0x80 );
+
+   return( ( ( status1 & 0xe0 ) == 0x00 ) && ( ( status2 & 0xe0 ) == 0xc0 ) );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void AL_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_Shutdown
+
+   Ends use of the sound card and resets it to a quiet state.
+---------------------------------------------------------------------*/
+
+void AL_Shutdown
+   (
+   void
+   )
+
+   {
+   AL_StereoOff();
+
+   AL_OPL3 = FALSE;
+   AL_ResetVoices();
+   AL_Reset();
+
+   DPMI_UnlockMemoryRegion( AL_LockStart, AL_LockEnd );
+   DPMI_Unlock( slotVoice );
+   DPMI_Unlock( VoiceLevel );
+   DPMI_Unlock( VoiceKsl );
+   DPMI_Unlock( offsetSlot );
+   DPMI_Unlock( NotePitch );
+   DPMI_Unlock( OctavePitch );
+   DPMI_Unlock( NoteMod12 );
+   DPMI_Unlock( NoteDiv12 );
+   DPMI_Unlock( VoiceReserved );
+   DPMI_Unlock( Voice );
+   DPMI_Unlock( Voice_Pool );
+   DPMI_Unlock( Channel );
+   DPMI_Unlock( AL_LeftPort );
+   DPMI_Unlock( AL_RightPort );
+   DPMI_Unlock( AL_Stereo );
+   DPMI_Unlock( AL_SendStereo );
+   DPMI_Unlock( AL_OPL3 );
+   DPMI_Unlock( AL_MaxMidiChannel );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_SetMaxMidiChannel
+
+   Sets the maximum MIDI channel that FM cards respond to.
+---------------------------------------------------------------------*/
+
+void AL_SetMaxMidiChannel
+   (
+   int channel
+   )
+
+   {
+   AL_MaxMidiChannel = channel - 1;
+   }
+
+/*---------------------------------------------------------------------
+   Function: AL_Init
+
+   Begins use of the sound card.
+---------------------------------------------------------------------*/
+
+int AL_Init
+   (
+   int soundcard
+   )
+
+   {
+   BLASTER_CONFIG Blaster;
+   int status;
+
+   status  = DPMI_LockMemoryRegion( AL_LockStart, AL_LockEnd );
+   status |= DPMI_Lock( slotVoice );
+   status |= DPMI_Lock( VoiceLevel );
+   status |= DPMI_Lock( VoiceKsl );
+   status |= DPMI_Lock( offsetSlot );
+   status |= DPMI_Lock( NotePitch );
+   status |= DPMI_Lock( OctavePitch );
+   status |= DPMI_Lock( NoteMod12 );
+   status |= DPMI_Lock( NoteDiv12 );
+   status |= DPMI_Lock( VoiceReserved );
+   status |= DPMI_Lock( Voice );
+   status |= DPMI_Lock( Voice_Pool );
+   status |= DPMI_Lock( Channel );
+   status |= DPMI_Lock( AL_LeftPort );
+   status |= DPMI_Lock( AL_RightPort );
+   status |= DPMI_Lock( AL_Stereo );
+   status |= DPMI_Lock( AL_SendStereo );
+   status |= DPMI_Lock( AL_OPL3 );
+   status |= DPMI_Lock( AL_MaxMidiChannel );
+
+   if ( status != DPMI_Ok )
+      {
+      return( AL_Error );
+      }
+
+   AL_Stereo = FALSE;
+   AL_OPL3   = FALSE;
+   AL_LeftPort = 0x388;
+   AL_RightPort = 0x388;
+
+   switch( soundcard )
+      {
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         AL_OPL3 = TRUE;
+         AL_LeftPort = 0x388;
+         AL_RightPort = 0x38A;
+         break;
+
+      case SoundBlaster :
+         status = BLASTER_GetCardSettings( &Blaster );
+         if ( status != BLASTER_Ok )
+            {
+            status = BLASTER_GetEnv( &Blaster );
+            if ( status != BLASTER_Ok )
+               {
+               break;
+               }
+            }
+
+         switch( Blaster.Type )
+            {
+            case SBPro2 :
+            case SB16 :
+               AL_OPL3 = TRUE;
+               AL_LeftPort  = Blaster.Address;
+               AL_RightPort = Blaster.Address + 2;
+               break;
+            }
+         break;
+      }
+// Temporarally commented out for ROTT.
+// Stereo FM seems to take too long on some computers and
+// causes the mouse driver to miss interrupts.
+
+/*
+   switch( soundcard )
+      {
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         AL_OPL3 = TRUE;
+         AL_Stereo = TRUE;
+         AL_LeftPort  = 0x388;
+         AL_RightPort = 0x38A;
+         break;
+
+      case SoundBlaster :
+         status = BLASTER_GetCardSettings( &Blaster );
+         if ( status != BLASTER_Ok )
+            {
+            status = BLASTER_GetEnv( &Blaster );
+            if ( status != BLASTER_Ok )
+               {
+               break;
+               }
+            }
+
+         switch( Blaster.Type )
+            {
+            case SBPro2 :
+            case SB16 :
+               AL_OPL3 = TRUE;
+               AL_Stereo = TRUE;
+               AL_LeftPort  = Blaster.Address;
+               AL_RightPort = Blaster.Address + 2;
+               break;
+
+            case SBPro :
+               AL_Stereo = TRUE;
+               AL_LeftPort  = Blaster.Address;
+               AL_RightPort = Blaster.Address + 2;
+               break;
+            }
+         break;
+      }
+*/
+
+   AL_CalcPitchInfo();
+   AL_Reset();
+   AL_ResetVoices();
+
+   return( AL_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AL_RegisterTimbreBank
+
+   Copies user supplied timbres over the default timbre bank.
+---------------------------------------------------------------------*/
+
+void AL_RegisterTimbreBank
+   (
+   unsigned char *timbres
+   )
+
+   {
+   int i;
+
+   for( i = 0; i < 256; i++ )
+      {
+      ADLIB_TimbreBank[ i ].SAVEK[ 0 ] = *( timbres++ );
+      ADLIB_TimbreBank[ i ].SAVEK[ 1 ] = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Level[ 0 ] = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Level[ 1 ] = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Env1[ 0 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Env1[ 1 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Env2[ 0 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Env2[ 1 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Wave[ 0 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Wave[ 1 ]  = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Feedback   = *( timbres++ );
+      ADLIB_TimbreBank[ i ].Transpose  = *( signed char * )( timbres++ );
+      ADLIB_TimbreBank[ i ].Velocity   = *( signed char * )( timbres++ );
+      }
+   }
--- /dev/null
+++ b/Game/src/audiolib/al_midi.h
@@ -1,0 +1,58 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __AL_MIDI_H
+#define __AL_MIDI_H
+
+enum AL_Errors
+   {
+   AL_Warning  = -2,
+   AL_Error    = -1,
+   AL_Ok       = 0,
+   };
+
+#define AL_MaxVolume             127
+#define AL_DefaultChannelVolume  90
+//#define AL_DefaultPitchBendRange 2
+#define AL_DefaultPitchBendRange 200
+
+#define ADLIB_PORT 0x388
+
+void AL_SendOutputToPort( int port, int reg, int data );
+void AL_SendOutput( int  voice, int reg, int data );
+void AL_StereoOn( void );
+void AL_StereoOff( void );
+int  AL_ReserveVoice( int voice );
+int  AL_ReleaseVoice( int voice );
+void AL_Shutdown( void );
+int  AL_Init( int soundcard );
+void AL_SetMaxMidiChannel( int channel );
+void AL_Reset( void );
+void AL_NoteOff( int channel, int key, int velocity );
+void AL_NoteOn( int channel, int key, int vel );
+//Turned off to test if it works with Watcom 10a
+//   #pragma aux AL_NoteOn frame;
+void AL_AllNotesOff( int channel );
+void AL_ControlChange( int channel, int type, int data );
+void AL_ProgramChange( int channel, int patch );
+void AL_SetPitchBend( int channel, int lsb, int msb );
+int  AL_DetectFM( void );
+void AL_RegisterTimbreBank( unsigned char *timbres );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/assert.h
@@ -1,0 +1,45 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef    __ASSERT_H
+
+    #define    __ASSERT_H
+
+    #ifdef NDEBUG
+
+        #define ASSERT(f)
+
+    #else
+
+        #pragma aux _Assert aborts;          /* _Assert will not return */
+        extern void _Assert( char *strFile, unsigned  uLine ); /*prototype */
+
+        #define ASSERT(f)          \
+            if (f)                 \
+                ;                  \
+            else                   \
+                _Assert( __FILE__, __LINE__ )
+
+    #endif
+
+#else
+
+    #error Multiple definition of ASSERT()
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/awe32.c
@@ -1,0 +1,540 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: AWE32.C
+
+   author: James R. Dose
+   date:   August 23, 1994
+
+   Cover functions for calling the AWE32 low-level library.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <conio.h>
+#include <string.h>
+#include "dpmi.h"
+#include "blaster.h"
+#include "ctaweapi.h"
+#include "awe32.h"
+
+#define _inp    inp
+#define _outp   outp
+
+/*  DSP defines  */
+#define MPU_ACK_OK          0xfe
+#define MPU_RESET_CMD       0xff
+#define MPU_ENTER_UART      0x3f
+
+static WORD wSBCBaseAddx;            /* Sound Blaster base address */
+static WORD wEMUBaseAddx;            /* EMU8000 subsystem base address */
+static WORD wMpuBaseAddx;            /* MPU401 base address */
+
+static unsigned short NoteFlags[ 128 ];
+
+/*  macros  */
+#define SBCPort( x )  ( ( x ) + wSBCBaseAddx )
+#define MPUPort( x )  ( ( x ) + wMpuBaseAddx )
+
+static SOUND_PACKET spSound =
+   {
+   0
+   };
+
+static LONG lBankSizes[ MAXBANKS ] =
+   {
+   0
+   };
+
+unsigned SetES( void );
+#pragma aux SetES = \
+        "xor eax, eax" \
+        "mov ax, es" \
+        "mov bx, ds" \
+        "mov es, bx" \
+        modify [ eax ebx ];
+
+void RestoreES( unsigned num );
+#pragma aux RestoreES = \
+        "mov  es, ax" \
+        parm [ eax ];
+
+int AWE32_ErrorCode = AWE32_Ok;
+
+#define AWE32_SetErrorCode( status ) \
+   AWE32_ErrorCode = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: AWE32_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *AWE32_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case AWE32_Warning :
+      case AWE32_Error :
+         ErrorString = AWE32_ErrorString( AWE32_ErrorCode );
+         break;
+
+      case AWE32_Ok :
+         ErrorString = "AWE32 ok.";
+         break;
+
+      case AWE32_SoundBlasterError :
+         ErrorString = BLASTER_ErrorString( BLASTER_Error );
+         break;
+
+      case AWE32_NotDetected :
+         ErrorString = "Could not detect AWE32.";
+         break;
+
+      case AWE32_UnableToInitialize :
+         ErrorString = "Unable to initialize AWE32.";
+
+      case AWE32_MPU401Error :
+         ErrorString = "MPU-401 initialization failed in AWE32.";
+         break;
+
+      case AWE32_DPMI_Error :
+         ErrorString = "DPMI Error in AWE32.";
+         break;
+
+      default :
+         ErrorString = "Unknown AWE32 error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define AWE32_LockStart AWE32_NoteOff
+
+
+void AWE32_NoteOff
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32NoteOff( channel, key, velocity );
+   RestoreES( temp );
+   NoteFlags[ key ] ^= ( 1 << channel );
+   }
+
+void AWE32_NoteOn
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32NoteOn( channel, key, velocity );
+   RestoreES( temp );
+   NoteFlags[ key ] |= ( 1 << channel );
+   }
+
+void AWE32_PolyAftertouch
+   (
+   int channel,
+   int key,
+   int pressure
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32PolyKeyPressure( channel, key, pressure );
+   RestoreES( temp );
+   }
+
+void AWE32_ChannelAftertouch
+   (
+   int channel,
+   int pressure
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32ChannelPressure( channel, pressure );
+   RestoreES( temp );
+   }
+
+void AWE32_ControlChange
+   (
+   int channel,
+   int number,
+   int value
+   )
+
+   {
+   unsigned temp;
+   int i;
+   unsigned channelmask;
+
+   temp = SetES();
+
+   if ( number == 0x7b )
+      {
+      channelmask = 1 << channel;
+      for( i = 0; i < 128; i++ )
+         {
+         if ( NoteFlags[ i ] & channelmask )
+            {
+            awe32NoteOff( channel, i, 0 );
+            NoteFlags[ i ] ^= channelmask;
+            }
+         }
+      }
+   else
+      {
+      awe32Controller( channel, number, value );
+      }
+   RestoreES( temp );
+   }
+
+void AWE32_ProgramChange
+   (
+   int channel,
+   int program
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32ProgramChange( channel, program );
+   RestoreES( temp );
+   }
+
+void AWE32_PitchBend
+   (
+   int channel,
+   int lsb,
+   int msb
+   )
+
+   {
+   unsigned temp;
+
+   temp = SetES();
+   awe32PitchBend( channel, lsb, msb );
+   RestoreES( temp );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: AWE32_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void AWE32_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*
+static int InitMPU
+   (
+   void
+   )
+
+   {
+   volatile DWORD dwCount;
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
+   _outp(MPUPort(1), MPU_RESET_CMD);
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
+   _inp(MPUPort(0));
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
+   _outp(MPUPort(1), MPU_RESET_CMD);
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
+   _inp(MPUPort(0));
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
+   _outp(MPUPort(1), MPU_ENTER_UART);
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x80) --dwCount;
+   if (!dwCount) return TRUE;
+   if (_inp(MPUPort(0)) != MPU_ACK_OK) return TRUE;
+
+   // mask MPU-401 interrupt
+   _outp(SBCPort(0x4), 0x83);
+   _outp(SBCPort(0x5), _inp(SBCPort(0x5)) & ~0x04);
+
+   return FALSE;
+   }
+*/
+
+/*������������������������������������������������������������������������͸*/
+/*� ShutdownMPU                                                    �*/
+/*� Cleans up Sound Blaster to normal state.                               �*/
+/*������������������������������������������������������������������������;*/
+
+static void ShutdownMPU
+   (
+   void
+   )
+
+   {
+   volatile DWORD dwCount;
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
+   _outp(MPUPort(1), MPU_RESET_CMD);
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   _inp(MPUPort(0));
+
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   dwCount = 0x2000;
+   while (dwCount && _inp(MPUPort(1)) & 0x40) --dwCount;
+   _outp(MPUPort(1), MPU_RESET_CMD);
+   for (dwCount=0; dwCount<0x2000; dwCount++) ;
+   _inp(MPUPort(0));
+   }
+
+
+/*������������������������������������������������������������������������͸*/
+/*� LoadSBK                                                                �*/
+/*������������������������������������������������������������������������;*/
+
+static void LoadSBK
+   (
+   void
+   )
+
+   {
+   /* use embeded preset objects */
+   spSound.bank_no = 0;            /* load as Bank 0 */
+   spSound.total_banks = 1;        /* use 1 bank first */
+   lBankSizes[ 0 ] = 0;            /* ram is not needed */
+
+   spSound.banksizes = lBankSizes;
+   awe32DefineBankSizes( &spSound );
+   awe32SoundPad.SPad1 = awe32SPad1Obj;
+   awe32SoundPad.SPad2 = awe32SPad2Obj;
+   awe32SoundPad.SPad3 = awe32SPad3Obj;
+   awe32SoundPad.SPad4 = awe32SPad4Obj;
+   awe32SoundPad.SPad5 = awe32SPad5Obj;
+   awe32SoundPad.SPad6 = awe32SPad6Obj;
+   awe32SoundPad.SPad7 = awe32SPad7Obj;
+   }
+
+
+int AWE32_Init
+   (
+   void
+   )
+
+   {
+   int status;
+   BLASTER_CONFIG Blaster;
+
+   wSBCBaseAddx = 0x220;
+   wEMUBaseAddx = 0x620;
+   wMpuBaseAddx = 0x330;
+
+   status = BLASTER_GetCardSettings( &Blaster );
+   if ( status != BLASTER_Ok )
+      {
+      status = BLASTER_GetEnv( &Blaster );
+      if ( status != BLASTER_Ok )
+         {
+         AWE32_SetErrorCode( AWE32_SoundBlasterError );
+         return( AWE32_Error );
+         }
+      }
+
+   wSBCBaseAddx = Blaster.Address;
+   if ( wSBCBaseAddx == UNDEFINED )
+      {
+      wSBCBaseAddx = 0x220;
+      }
+
+   wMpuBaseAddx = Blaster.Midi;
+   if ( wMpuBaseAddx == UNDEFINED )
+      {
+      wMpuBaseAddx = 0x330;
+      }
+
+   wEMUBaseAddx = Blaster.Emu;
+   if ( wEMUBaseAddx <= 0 )
+      {
+      wEMUBaseAddx = wSBCBaseAddx + 0x400;
+      }
+
+   status = awe32Detect( wEMUBaseAddx );
+   if ( status )
+      {
+      AWE32_SetErrorCode( AWE32_NotDetected );
+      return( AWE32_Error );
+      }
+
+   status = awe32InitHardware();
+   if ( status )
+      {
+      AWE32_SetErrorCode( AWE32_UnableToInitialize );
+      return( AWE32_Error );
+      }
+
+
+   status = awe32InitMIDI();
+   if ( status )
+      {
+      AWE32_Shutdown();
+      AWE32_SetErrorCode( AWE32_MPU401Error )
+      return( AWE32_Error );
+      }
+
+/*
+    status = InitMPU();
+   if ( status )
+      {
+      ShutdownMPU();
+      status = InitMPU();
+      if ( status )
+         {
+         ShutdownMPU();
+         status = InitMPU();
+         if ( status )
+            {
+            AWE32_Shutdown();
+            AWE32_SetErrorCode( AWE32_MPU401Error )
+            return( AWE32_Error );
+            }
+         }
+      }
+*/
+   status  = DPMI_LockMemoryRegion( AWE32_LockStart, AWE32_LockEnd );
+   status |= DPMI_Lock( wSBCBaseAddx );
+   status |= DPMI_Lock( wEMUBaseAddx );
+   status |= DPMI_Lock( wMpuBaseAddx );
+   status |= DPMI_Lock( spSound );
+   status |= DPMI_Lock( lBankSizes );
+   status |= DPMI_LockMemory( NoteFlags, sizeof( NoteFlags ) );
+
+   // Lock awe32 library
+   status  = DPMI_LockMemoryRegion( __midieng_code, __midieng_ecode );
+   status  = DPMI_LockMemoryRegion( __midieng_code(), __midieng_ecode() );
+   status  = DPMI_LockMemoryRegion( __nrpn_code, __nrpn_ecode );
+   status  = DPMI_LockMemoryRegion( __nrpn_code(), __nrpn_ecode() );
+   status  = DPMI_LockMemoryRegion( &__midivar_data, &__midivar_edata );
+   status  = DPMI_LockMemoryRegion( &__nrpnvar_data, &__nrpnvar_edata );
+   status  = DPMI_LockMemoryRegion( &__embed_data, &__embed_edata );
+
+   if ( status != DPMI_Ok )
+      {
+      ShutdownMPU();
+      awe32Terminate();
+      AWE32_SetErrorCode( AWE32_DPMI_Error );
+      return( AWE32_Error );
+      }
+
+   // Set the number of voices to use to 32
+   awe32NumG = 32;
+
+   awe32TotalPatchRam(&spSound);
+
+   LoadSBK();
+   awe32InitMIDI();
+   awe32InitNRPN();
+
+   memset( NoteFlags, 0, sizeof( NoteFlags ) );
+
+   return( AWE32_Ok );
+   }
+
+void AWE32_Shutdown
+   (
+   void
+   )
+
+   {
+   ShutdownMPU();
+   awe32Terminate();
+
+   DPMI_UnlockMemoryRegion( AWE32_LockStart, AWE32_LockEnd );
+   DPMI_Unlock( wSBCBaseAddx );
+   DPMI_Unlock( wEMUBaseAddx );
+   DPMI_Unlock( wMpuBaseAddx );
+   DPMI_Unlock( spSound );
+   DPMI_Unlock( lBankSizes );
+   DPMI_UnlockMemory( NoteFlags, sizeof( NoteFlags ) );
+
+   // Unlock awe32 library
+   DPMI_UnlockMemoryRegion( __midieng_code, __midieng_ecode );
+   DPMI_UnlockMemoryRegion( __midieng_code(), __midieng_ecode() );
+   DPMI_UnlockMemoryRegion( __nrpn_code, __nrpn_ecode );
+   DPMI_UnlockMemoryRegion( __nrpn_code(), __nrpn_ecode() );
+   DPMI_UnlockMemoryRegion( &__midivar_data, &__midivar_edata );
+   DPMI_UnlockMemoryRegion( &__nrpnvar_data, &__nrpnvar_edata );
+   DPMI_UnlockMemoryRegion( &__embed_data, &__embed_edata );
+   }
--- /dev/null
+++ b/Game/src/audiolib/awe32.h
@@ -1,0 +1,58 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: AWE32.H
+
+   author: James R. Dose
+   date:   August 23, 1994
+
+   Public header for AWE32.C  Cover functions for calling the
+   AWE32 low-level library.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __AWE32_H
+#define __AWE32_H
+
+enum AWE32_ERRORS
+   {
+   AWE32_Warning = -2,
+   AWE32_Error = -1,
+   AWE32_Ok = 0,
+   AWE32_SoundBlasterError,
+   AWE32_NotDetected,
+   AWE32_UnableToInitialize,
+   AWE32_MPU401Error,
+   AWE32_DPMI_Error
+   };
+
+char *AWE32_ErrorString( int ErrorNumber );
+int  AWE32_Init( void );
+void AWE32_Shutdown( void );
+void AWE32_NoteOff( int channel, int key, int velocity );
+void AWE32_NoteOn( int channel, int key, int velocity );
+void AWE32_PolyAftertouch( int channel, int key, int pressure );
+void AWE32_ChannelAftertouch( int channel, int pressure );
+void AWE32_ControlChange( int channel, int number, int value );
+void AWE32_ProgramChange( int channel, int program );
+void AWE32_PitchBend( int channel, int lsb, int msb );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/blaster.c
@@ -1,0 +1,2330 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: BLASTER.C
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Low level routines to support Sound Blaster, Sound Blaster Pro,
+   Sound Blaster 16, and compatible sound cards.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "dpmi.h"
+#include "dma.h"
+#include "irq.h"
+#include "blaster.h"
+#include "_blaster.h"
+
+#define USESTACK
+
+const int BLASTER_Interrupts[ BLASTER_MaxIrq + 1 ]  =
+   {
+   INVALID, INVALID,     0xa,     0xb,
+   INVALID,     0xd, INVALID,     0xf,
+   INVALID, INVALID,    0x72,    0x73,
+      0x74, INVALID, INVALID,    0x77
+   };
+
+const int BLASTER_SampleSize[ BLASTER_MaxMixMode + 1 ] =
+   {
+   MONO_8BIT_SAMPLE_SIZE,  STEREO_8BIT_SAMPLE_SIZE,
+   MONO_16BIT_SAMPLE_SIZE, STEREO_16BIT_SAMPLE_SIZE
+   };
+
+const CARD_CAPABILITY BLASTER_CardConfig[ BLASTER_MaxCardType + 1 ] =
+   {
+      { FALSE, INVALID,      INVALID, INVALID, INVALID }, // Unsupported
+      {  TRUE,      NO,    MONO_8BIT,    4000,   23000 }, // SB 1.0
+      {  TRUE,     YES,  STEREO_8BIT,    4000,   44100 }, // SBPro
+      {  TRUE,      NO,    MONO_8BIT,    4000,   23000 }, // SB 2.xx
+      {  TRUE,     YES,  STEREO_8BIT,    4000,   44100 }, // SBPro 2
+      { FALSE, INVALID,      INVALID, INVALID, INVALID }, // Unsupported
+      {  TRUE,     YES, STEREO_16BIT,    5000,   44100 }, // SB16
+   };
+
+CARD_CAPABILITY BLASTER_Card;
+
+static void    ( __interrupt __far *BLASTER_OldInt )( void );
+
+BLASTER_CONFIG BLASTER_Config =
+   {
+   UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED
+   };
+
+static int BLASTER_Installed = FALSE;
+
+int BLASTER_Version;
+
+static char   *BLASTER_DMABuffer;
+static char   *BLASTER_DMABufferEnd;
+static char   *BLASTER_CurrentDMABuffer;
+static int     BLASTER_TotalDMABufferSize;
+
+static int      BLASTER_TransferLength   = 0;
+static int      BLASTER_MixMode          = BLASTER_DefaultMixMode;
+static int      BLASTER_SamplePacketSize = MONO_16BIT_SAMPLE_SIZE;
+static unsigned BLASTER_SampleRate       = BLASTER_DefaultSampleRate;
+
+static unsigned BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+
+volatile int   BLASTER_SoundPlaying;
+volatile int   BLASTER_SoundRecording;
+
+void ( *BLASTER_CallBack )( void );
+
+static int  BLASTER_IntController1Mask;
+static int  BLASTER_IntController2Mask;
+
+static int BLASTER_MixerAddress = UNDEFINED;
+static int BLASTER_MixerType    = 0;
+static int BLASTER_OriginalMidiVolumeLeft   = 255;
+static int BLASTER_OriginalMidiVolumeRight  = 255;
+static int BLASTER_OriginalVoiceVolumeLeft  = 255;
+static int BLASTER_OriginalVoiceVolumeRight = 255;
+
+static int BLASTER_WaveBlasterState = 0x0F;
+
+// adequate stack size
+#define kStackSize 2048
+
+static unsigned short StackSelector = NULL;
+static unsigned long  StackPointer;
+
+static unsigned short oldStackSelector;
+static unsigned long  oldStackPointer;
+
+// This is defined because we can't create local variables in a
+// function that switches stacks.
+static int GlobalStatus;
+
+// These declarations are necessary to use the inline assembly pragmas.
+
+extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
+extern void SetStack(unsigned short selector,unsigned long stackptr);
+
+// This function will get the current stack selector and pointer and save
+// them off.
+#pragma aux GetStack =  \
+   "mov  [edi],esp"     \
+   "mov  ax,ss"         \
+   "mov  [esi],ax"      \
+   parm [esi] [edi]     \
+   modify [eax esi edi];
+
+// This function will set the stack selector and pointer to the specified
+// values.
+#pragma aux SetStack =  \
+   "mov  ss,ax"         \
+   "mov  esp,edx"       \
+   parm [ax] [edx]      \
+   modify [eax edx];
+
+int BLASTER_DMAChannel;
+
+int BLASTER_ErrorCode = BLASTER_Ok;
+
+#define BLASTER_SetErrorCode( status ) \
+   BLASTER_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *BLASTER_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case BLASTER_Warning :
+      case BLASTER_Error :
+         ErrorString = BLASTER_ErrorString( BLASTER_ErrorCode );
+         break;
+
+      case BLASTER_Ok :
+         ErrorString = "Sound Blaster ok.";
+         break;
+
+      case BLASTER_EnvNotFound :
+         ErrorString = "BLASTER environment variable not set.";
+         break;
+
+      case BLASTER_AddrNotSet :
+         ErrorString = "Sound Blaster address not set.";
+         break;
+
+      case BLASTER_DMANotSet :
+         ErrorString = "Sound Blaster 8-bit DMA channel not set.";
+         break;
+
+      case BLASTER_DMA16NotSet :
+         ErrorString = "Sound Blaster 16-bit DMA channel not set.";
+         break;
+
+      case BLASTER_InvalidParameter :
+         ErrorString = "Invalid parameter in BLASTER environment variable.";
+         break;
+
+      case BLASTER_CardNotReady :
+         ErrorString = "Sound Blaster not responding on selected port.";
+         break;
+
+      case BLASTER_NoSoundPlaying :
+         ErrorString = "No sound playing on Sound Blaster.";
+         break;
+
+      case BLASTER_InvalidIrq :
+         ErrorString = "Invalid Sound Blaster Irq.";
+         break;
+
+      case BLASTER_UnableToSetIrq :
+         ErrorString = "Unable to set Sound Blaster IRQ.  Try selecting an IRQ of 7 or below.";
+         break;
+
+      case BLASTER_DmaError :
+         ErrorString = DMA_ErrorString( DMA_Error );
+         break;
+
+      case BLASTER_NoMixer :
+         ErrorString = "Mixer not available on selected Sound Blaster card.";
+         break;
+
+      case BLASTER_DPMI_Error :
+         ErrorString = "DPMI Error in Blaster.";
+         break;
+
+      case BLASTER_OutOfMemory :
+         ErrorString = "Out of conventional memory in Blaster.";
+         break;
+
+      default :
+         ErrorString = "Unknown Sound Blaster error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define BLASTER_LockStart BLASTER_EnableInterrupt
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_EnableInterrupt
+
+   Enables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+void BLASTER_EnableInterrupt
+   (
+   void
+   )
+
+   {
+   int Irq;
+   int mask;
+
+   // Unmask system interrupt
+   Irq  = BLASTER_Config.Interrupt;
+   if ( Irq < 8 )
+      {
+      mask = inp( 0x21 ) & ~( 1 << Irq );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask = inp( 0xA1 ) & ~( 1 << ( Irq - 8 ) );
+      outp( 0xA1, mask  );
+
+      mask = inp( 0x21 ) & ~( 1 << 2 );
+      outp( 0x21, mask  );
+      }
+
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DisableInterrupt
+
+   Disables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+void BLASTER_DisableInterrupt
+   (
+   void
+   )
+
+   {
+   int Irq;
+   int mask;
+
+   // Restore interrupt mask
+   Irq  = BLASTER_Config.Interrupt;
+   if ( Irq < 8 )
+      {
+      mask  = inp( 0x21 ) & ~( 1 << Irq );
+      mask |= BLASTER_IntController1Mask & ( 1 << Irq );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask  = inp( 0x21 ) & ~( 1 << 2 );
+      mask |= BLASTER_IntController1Mask & ( 1 << 2 );
+      outp( 0x21, mask  );
+
+      mask  = inp( 0xA1 ) & ~( 1 << ( Irq - 8 ) );
+      mask |= BLASTER_IntController2Mask & ( 1 << ( Irq - 8 ) );
+      outp( 0xA1, mask  );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ServiceInterrupt
+
+   Handles interrupt generated by sound card at the end of a voice
+   transfer.  Calls the user supplied callback function.
+---------------------------------------------------------------------*/
+
+void __interrupt __far BLASTER_ServiceInterrupt
+   (
+   void
+   )
+
+   {
+   #ifdef USESTACK
+   // save stack
+   GetStack( &oldStackSelector, &oldStackPointer );
+
+   // set our stack
+   SetStack( StackSelector, StackPointer );
+   #endif
+
+   // Acknowledge interrupt
+   // Check if this is this an SB16 or newer
+   if ( BLASTER_Version >= DSP_Version4xx )
+      {
+      outp( BLASTER_Config.Address + BLASTER_MixerAddressPort,
+         MIXER_DSP4xxISR_Ack );
+
+      GlobalStatus = inp( BLASTER_Config.Address + BLASTER_MixerDataPort );
+
+      // Check if a 16-bit DMA interrupt occurred
+      if ( GlobalStatus & MIXER_16BITDMA_INT )
+         {
+         // Acknowledge 16-bit transfer interrupt
+         inp( BLASTER_Config.Address + BLASTER_16BitDMAAck );
+         }
+      else if ( GlobalStatus & MIXER_8BITDMA_INT )
+         {
+         inp( BLASTER_Config.Address + BLASTER_DataAvailablePort );
+         }
+      else
+         {
+         #ifdef USESTACK
+         // restore stack
+         SetStack( oldStackSelector, oldStackPointer );
+         #endif
+
+         // Wasn't our interrupt.  Call the old one.
+         _chain_intr( BLASTER_OldInt );
+         }
+      }
+   else
+      {
+      // Older card - can't detect if an interrupt occurred.
+      inp( BLASTER_Config.Address + BLASTER_DataAvailablePort );
+      }
+
+   // Keep track of current buffer
+   BLASTER_CurrentDMABuffer += BLASTER_TransferLength;
+
+   if ( BLASTER_CurrentDMABuffer >= BLASTER_DMABufferEnd )
+      {
+      BLASTER_CurrentDMABuffer = BLASTER_DMABuffer;
+      }
+
+   // Continue playback on cards without autoinit mode
+   if ( BLASTER_Version < DSP_Version2xx )
+      {
+      if ( BLASTER_SoundPlaying )
+         {
+         BLASTER_DSP1xx_BeginPlayback( BLASTER_TransferLength );
+         }
+
+      if ( BLASTER_SoundRecording )
+         {
+         BLASTER_DSP1xx_BeginRecord( BLASTER_TransferLength );
+         }
+      }
+
+   // Call the caller's callback function
+   if ( BLASTER_CallBack != NULL )
+      {
+      BLASTER_CallBack();
+      }
+
+   #ifdef USESTACK
+   // restore stack
+   SetStack( oldStackSelector, oldStackPointer );
+   #endif
+
+   // send EOI to Interrupt Controller
+   if ( BLASTER_Config.Interrupt > 7 )
+      {
+      outp( 0xA0, 0x20 );
+      }
+
+   outp( 0x20, 0x20 );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_WriteDSP
+
+   Writes a byte of data to the sound card's DSP.
+---------------------------------------------------------------------*/
+
+int BLASTER_WriteDSP
+   (
+   unsigned data
+   )
+
+   {
+   int      port;
+   unsigned count;
+   int      status;
+
+   port = BLASTER_Config.Address + BLASTER_WritePort;
+
+   status = BLASTER_Error;
+
+   count = 0xFFFF;
+
+   do
+      {
+      if ( ( inp( port ) & 0x80 ) == 0 )
+         {
+         outp( port, data );
+         status = BLASTER_Ok;
+         break;
+         }
+
+      count--;
+      }
+   while( count > 0 );
+
+   if ( status != BLASTER_Ok )
+      {
+      BLASTER_SetErrorCode( BLASTER_CardNotReady );
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ReadDSP
+
+   Reads a byte of data from the sound card's DSP.
+---------------------------------------------------------------------*/
+
+int BLASTER_ReadDSP
+   (
+   void
+   )
+
+   {
+   int      port;
+   unsigned count;
+   int      status;
+
+   port = BLASTER_Config.Address + BLASTER_DataAvailablePort;
+
+   status = BLASTER_Error;
+
+   count = 0xFFFF;
+
+   do
+      {
+      if ( inp( port ) & 0x80 )
+         {
+         status = inp( BLASTER_Config.Address + BLASTER_ReadPort );
+         break;
+         }
+
+      count--;
+      }
+   while( count > 0 );
+
+   if ( status == BLASTER_Error )
+      {
+      BLASTER_SetErrorCode( BLASTER_CardNotReady );
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ResetDSP
+
+   Sends a reset command to the sound card's Digital Signal Processor
+   (DSP), causing it to perform an initialization.
+---------------------------------------------------------------------*/
+
+int BLASTER_ResetDSP
+   (
+   void
+   )
+
+   {
+   volatile int count;
+   int port;
+   int status;
+
+   port = BLASTER_Config.Address + BLASTER_ResetPort;
+
+   status = BLASTER_CardNotReady;
+
+   outp( port, 1 );
+
+/* What the hell am I doing here?
+   count = 100;
+
+   do
+      {
+      if ( inp( port ) == 255 )
+         {
+         break;
+         }
+
+      count--;
+      }
+   while( count > 0 );
+*/
+
+   count = 0x100;
+   do
+      {
+      count--;
+      }
+   while( count > 0 );
+
+   outp( port, 0 );
+
+   count = 100;
+
+   do
+      {
+      if ( BLASTER_ReadDSP() == BLASTER_Ready )
+         {
+         status = BLASTER_Ok;
+         break;
+         }
+
+      count--;
+      }
+   while( count > 0 );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetDSPVersion
+
+   Returns the version number of the sound card's DSP.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetDSPVersion
+   (
+   void
+   )
+
+   {
+   int MajorVersion;
+   int MinorVersion;
+   int version;
+
+   BLASTER_WriteDSP( DSP_GetVersion );
+
+   MajorVersion   = BLASTER_ReadDSP();
+   MinorVersion   = BLASTER_ReadDSP();
+
+   if ( ( MajorVersion == BLASTER_Error ) ||
+      ( MinorVersion == BLASTER_Error ) )
+      {
+      BLASTER_SetErrorCode( BLASTER_CardNotReady );
+      return( BLASTER_Error );
+      }
+
+   version = ( MajorVersion << 8 ) + MinorVersion;
+
+   if ( version >= DSP_Version4xx )
+      {
+      BLASTER_Card.IsSupported     = TRUE;
+      BLASTER_Card.HasMixer        = YES;
+      BLASTER_Card.MaxMixMode      = STEREO_16BIT;
+      BLASTER_Card.MinSamplingRate = 5000;
+      BLASTER_Card.MaxSamplingRate = 44100;
+      BLASTER_MixerType = SB16;
+      }
+   else if ( version >= DSP_Version3xx )
+      {
+      BLASTER_Card.IsSupported     = TRUE;
+      BLASTER_Card.HasMixer        = YES;
+      BLASTER_Card.MaxMixMode      = STEREO_8BIT;
+      BLASTER_Card.MinSamplingRate = 4000;
+      BLASTER_Card.MaxSamplingRate = 44100;
+      BLASTER_MixerType = SBPro;
+      }
+   else if ( version >= DSP_Version2xx )
+      {
+      BLASTER_Card.IsSupported     = TRUE;
+      BLASTER_Card.HasMixer        = NO;
+      BLASTER_Card.MaxMixMode      = MONO_8BIT;
+      BLASTER_Card.MinSamplingRate = 4000;
+      BLASTER_Card.MaxSamplingRate = 23000;
+      BLASTER_MixerType = 0;
+      }
+   else
+      {
+      // DSP_Version1xx
+      BLASTER_Card.IsSupported     = TRUE;
+      BLASTER_Card.HasMixer        = NO;
+      BLASTER_Card.MaxMixMode      = MONO_8BIT;
+      BLASTER_Card.MinSamplingRate = 4000;
+      BLASTER_Card.MaxSamplingRate = 23000;
+      BLASTER_MixerType = 0;
+      }
+
+   return( version );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SpeakerOn
+
+   Enables output from the DAC.
+---------------------------------------------------------------------*/
+
+void BLASTER_SpeakerOn
+   (
+   void
+   )
+
+   {
+   BLASTER_WriteDSP( DSP_SpeakerOn );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SpeakerOff
+
+   Disables output from the DAC.
+---------------------------------------------------------------------*/
+
+void BLASTER_SpeakerOff
+   (
+   void
+   )
+
+   {
+   BLASTER_WriteDSP( DSP_SpeakerOff );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetPlaybackRate
+
+   Sets the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+void BLASTER_SetPlaybackRate
+   (
+   unsigned rate
+   )
+
+   {
+   int LoByte;
+   int HiByte;
+
+   if ( BLASTER_Version < DSP_Version4xx )
+      {
+      int  timeconstant;
+      long ActualRate;
+
+      // Send sampling rate as time constant for older Sound
+      // Blaster compatible cards.
+
+      ActualRate = rate * BLASTER_SamplePacketSize;
+      if ( ActualRate < BLASTER_Card.MinSamplingRate )
+         {
+         rate = BLASTER_Card.MinSamplingRate / BLASTER_SamplePacketSize;
+         }
+
+      if ( ActualRate > BLASTER_Card.MaxSamplingRate )
+         {
+         rate = BLASTER_Card.MaxSamplingRate / BLASTER_SamplePacketSize;
+         }
+
+      timeconstant = ( int )CalcTimeConstant( rate, BLASTER_SamplePacketSize );
+
+      // Keep track of what the actual rate is
+      BLASTER_SampleRate  = ( unsigned )CalcSamplingRate( timeconstant );
+      BLASTER_SampleRate /= BLASTER_SamplePacketSize;
+
+      BLASTER_WriteDSP( DSP_SetTimeConstant );
+      BLASTER_WriteDSP( timeconstant );
+      }
+   else
+      {
+      // Send literal sampling rate for cards with DSP version
+      // 4.xx (Sound Blaster 16)
+
+      BLASTER_SampleRate = rate;
+
+      if ( BLASTER_SampleRate < BLASTER_Card.MinSamplingRate )
+         {
+         BLASTER_SampleRate = BLASTER_Card.MinSamplingRate;
+         }
+
+      if ( BLASTER_SampleRate > BLASTER_Card.MaxSamplingRate )
+         {
+         BLASTER_SampleRate = BLASTER_Card.MaxSamplingRate;
+         }
+
+      HiByte = hibyte( BLASTER_SampleRate );
+      LoByte = lobyte( BLASTER_SampleRate );
+
+      // Set playback rate
+      BLASTER_WriteDSP( DSP_Set_DA_Rate );
+      BLASTER_WriteDSP( HiByte );
+      BLASTER_WriteDSP( LoByte );
+
+      // Set recording rate
+      BLASTER_WriteDSP( DSP_Set_AD_Rate );
+      BLASTER_WriteDSP( HiByte );
+      BLASTER_WriteDSP( LoByte );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetPlaybackRate
+
+   Returns the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+unsigned BLASTER_GetPlaybackRate
+   (
+   void
+   )
+
+   {
+   return( BLASTER_SampleRate );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetMixMode
+
+   Sets the sound card to play samples in mono or stereo.
+---------------------------------------------------------------------*/
+
+int BLASTER_SetMixMode
+   (
+   int mode
+   )
+
+   {
+   int   port;
+   int   data;
+   int   CardType;
+
+   CardType = BLASTER_Config.Type;
+
+   mode &= BLASTER_MaxMixMode;
+
+   if ( !( BLASTER_Card.MaxMixMode & STEREO ) )
+      {
+      mode &= ~STEREO;
+      }
+
+   if ( !( BLASTER_Card.MaxMixMode & SIXTEEN_BIT ) )
+      {
+      mode &= ~SIXTEEN_BIT;
+      }
+
+   BLASTER_MixMode = mode;
+   BLASTER_SamplePacketSize = BLASTER_SampleSize[ mode ];
+
+   // For the Sound Blaster Pro, we have to set the mixer chip
+   // to play mono or stereo samples.
+
+   if ( ( CardType == SBPro ) || ( CardType == SBPro2 ) )
+      {
+      port = BLASTER_Config.Address + BLASTER_MixerAddressPort;
+      outp( port, MIXER_SBProOutputSetting );
+
+      port = BLASTER_Config.Address + BLASTER_MixerDataPort;
+
+      // Get current mode
+      data = inp( port );
+
+      // set stereo mode bit
+      if ( mode & STEREO )
+         {
+         data |= MIXER_SBProStereoFlag;
+         }
+      else
+         {
+         data &= ~MIXER_SBProStereoFlag;
+         }
+
+      // set the mode
+      outp( port, data );
+
+      BLASTER_SetPlaybackRate( BLASTER_SampleRate );
+      }
+
+   return( mode );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_StopPlayback
+
+   Ends the DMA transfer of digitized sound to the sound card.
+---------------------------------------------------------------------*/
+
+void BLASTER_StopPlayback
+   (
+   void
+   )
+
+   {
+   int DmaChannel;
+
+   // Don't allow anymore interrupts
+   BLASTER_DisableInterrupt();
+
+   if ( BLASTER_HaltTransferCommand == DSP_Reset )
+      {
+      BLASTER_ResetDSP();
+      }
+   else
+      {
+      BLASTER_WriteDSP( BLASTER_HaltTransferCommand );
+      }
+
+   // Disable the DMA channel
+   if ( BLASTER_MixMode & SIXTEEN_BIT )
+      {
+      DmaChannel = BLASTER_Config.Dma16;
+      }
+   else
+      {
+      DmaChannel = BLASTER_Config.Dma8;
+      }
+   DMA_EndTransfer( DmaChannel );
+
+   // Turn off speaker
+   BLASTER_SpeakerOff();
+
+   BLASTER_SoundPlaying = FALSE;
+   BLASTER_SoundRecording = FALSE;
+
+   BLASTER_DMABuffer = NULL;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetupDMABuffer
+
+   Programs the DMAC for sound transfer.
+---------------------------------------------------------------------*/
+
+int BLASTER_SetupDMABuffer
+   (
+   char *BufferPtr,
+   int   BufferSize,
+   int   mode
+   )
+
+   {
+   int DmaChannel;
+   int DmaStatus;
+   int errorcode;
+
+   if ( BLASTER_MixMode & SIXTEEN_BIT )
+      {
+      DmaChannel = BLASTER_Config.Dma16;
+      errorcode  = BLASTER_DMA16NotSet;
+      }
+   else
+      {
+      DmaChannel = BLASTER_Config.Dma8;
+      errorcode  = BLASTER_DMANotSet;
+      }
+
+   if ( DmaChannel == UNDEFINED )
+      {
+      BLASTER_SetErrorCode( errorcode );
+      return( BLASTER_Error );
+      }
+
+   DmaStatus = DMA_SetupTransfer( DmaChannel, BufferPtr, BufferSize, mode );
+   if ( DmaStatus == DMA_Error )
+      {
+      BLASTER_SetErrorCode( BLASTER_DmaError );
+      return( BLASTER_Error );
+      }
+
+   BLASTER_DMAChannel = DmaChannel;
+
+   BLASTER_DMABuffer          = BufferPtr;
+   BLASTER_CurrentDMABuffer   = BufferPtr;
+   BLASTER_TotalDMABufferSize = BufferSize;
+   BLASTER_DMABufferEnd       = BufferPtr + BufferSize;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetCurrentPos
+
+   Returns the offset within the current sound being played.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetCurrentPos
+   (
+   void
+   )
+
+   {
+   char *CurrentAddr;
+   int   DmaChannel;
+   int   offset;
+
+   if ( !BLASTER_SoundPlaying )
+      {
+      BLASTER_SetErrorCode( BLASTER_NoSoundPlaying );
+      return( BLASTER_Error );
+      }
+
+   if ( BLASTER_MixMode & SIXTEEN_BIT )
+      {
+      DmaChannel = BLASTER_Config.Dma16;
+      }
+   else
+      {
+      DmaChannel = BLASTER_Config.Dma8;
+      }
+
+   if ( DmaChannel == UNDEFINED )
+      {
+      BLASTER_SetErrorCode( BLASTER_DMANotSet );
+      return( BLASTER_Error );
+      }
+
+   CurrentAddr = DMA_GetCurrentPos( DmaChannel );
+
+   offset = ( int )( ( ( unsigned long )CurrentAddr ) -
+      ( ( unsigned long )BLASTER_CurrentDMABuffer ) );
+
+   if ( BLASTER_MixMode & SIXTEEN_BIT )
+      {
+      offset >>= 1;
+      }
+
+   if ( BLASTER_MixMode & STEREO )
+      {
+      offset >>= 1;
+      }
+
+   return( offset );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP1xx_BeginPlayback
+
+   Starts playback of digitized sound on cards compatible with DSP
+   version 1.xx.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP1xx_BeginPlayback
+   (
+   int length
+   )
+
+   {
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   SampleLength = length - 1;
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   // Program DSP to play sound
+   BLASTER_WriteDSP( DSP_Old8BitDAC );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+
+   BLASTER_SoundPlaying = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP2xx_BeginPlayback
+
+   Starts playback of digitized sound on cards compatible with DSP
+   version 2.xx.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP2xx_BeginPlayback
+   (
+   int length
+   )
+
+   {
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   SampleLength = length - 1;
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   BLASTER_WriteDSP( DSP_SetBlockLength );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   if ( ( BLASTER_Version >= DSP_Version201 ) && ( DSP_MaxNormalRate <
+      ( BLASTER_SampleRate * BLASTER_SamplePacketSize ) ) )
+      {
+      BLASTER_WriteDSP( DSP_8BitHighSpeedAutoInitMode );
+      BLASTER_HaltTransferCommand = DSP_Reset;
+      }
+   else
+      {
+      BLASTER_WriteDSP( DSP_8BitAutoInitMode );
+      BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+      }
+
+   BLASTER_SoundPlaying = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP4xx_BeginPlayback
+
+   Starts playback of digitized sound on cards compatible with DSP
+   version 4.xx, such as the Sound Blaster 16.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP4xx_BeginPlayback
+   (
+   int length
+   )
+
+   {
+   int TransferCommand;
+   int TransferMode;
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   if ( BLASTER_MixMode & SIXTEEN_BIT )
+      {
+      TransferCommand = DSP_16BitDAC;
+      SampleLength = ( length / 2 ) - 1;
+      BLASTER_HaltTransferCommand = DSP_Halt16bitTransfer;
+      if ( BLASTER_MixMode & STEREO )
+         {
+         TransferMode = DSP_SignedStereoData;
+         }
+      else
+         {
+         TransferMode = DSP_SignedMonoData;
+         }
+      }
+   else
+      {
+      TransferCommand = DSP_8BitDAC;
+      SampleLength = length - 1;
+      BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+      if ( BLASTER_MixMode & STEREO )
+         {
+         TransferMode = DSP_UnsignedStereoData;
+         }
+      else
+         {
+         TransferMode = DSP_UnsignedMonoData;
+         }
+      }
+
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   // Program DSP to play sound
+   BLASTER_WriteDSP( TransferCommand );
+   BLASTER_WriteDSP( TransferMode );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   BLASTER_SoundPlaying = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_BeginBufferedPlayback
+
+   Begins multibuffered playback of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+int BLASTER_BeginBufferedPlayback
+   (
+   char    *BufferStart,
+   int      BufferSize,
+   int      NumDivisions,
+   unsigned SampleRate,
+   int      MixMode,
+   void  ( *CallBackFunc )( void )
+   )
+
+   {
+   int DmaStatus;
+   int TransferLength;
+
+//JIM
+//   if ( BLASTER_SoundPlaying || BLASTER_SoundRecording )
+      {
+      BLASTER_StopPlayback();
+      }
+
+   BLASTER_SetMixMode( MixMode );
+
+   DmaStatus = BLASTER_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitRead );
+   if ( DmaStatus == BLASTER_Error )
+      {
+      return( BLASTER_Error );
+      }
+
+   BLASTER_SetPlaybackRate( SampleRate );
+
+   BLASTER_SetCallBack( CallBackFunc );
+
+   BLASTER_EnableInterrupt();
+
+   // Turn on speaker
+   BLASTER_SpeakerOn();
+
+   TransferLength = BufferSize / NumDivisions;
+   BLASTER_TransferLength = TransferLength;
+
+   //  Program the sound card to start the transfer.
+   if ( BLASTER_Version < DSP_Version2xx )
+      {
+      BLASTER_DSP1xx_BeginPlayback( TransferLength );
+      }
+   else if ( BLASTER_Version < DSP_Version4xx )
+      {
+      BLASTER_DSP2xx_BeginPlayback( TransferLength );
+      }
+   else
+      {
+      BLASTER_DSP4xx_BeginPlayback( TransferLength );
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP4xx_BeginRecord
+
+   Starts recording of digitized sound on cards compatible with DSP
+   version 4.xx, such as the Sound Blaster 16.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP4xx_BeginRecord
+   (
+   int length
+   )
+
+   {
+   int TransferCommand;
+   int TransferMode;
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   TransferCommand = DSP_8BitADC;
+   SampleLength = length - 1;
+   BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+
+   TransferMode = DSP_UnsignedMonoData;
+
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   // Program DSP to play sound
+   BLASTER_WriteDSP( TransferCommand );
+   BLASTER_WriteDSP( TransferMode );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   BLASTER_SoundRecording = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP2xx_BeginRecord
+
+   Starts recording of digitized sound on cards compatible with DSP
+   version 2.xx.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP2xx_BeginRecord
+   (
+   int length
+   )
+
+   {
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   SampleLength = length - 1;
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   BLASTER_WriteDSP( DSP_SetBlockLength );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   if ( ( BLASTER_Version >= DSP_Version201 ) && ( DSP_MaxNormalRate <
+      ( BLASTER_SampleRate * BLASTER_SamplePacketSize ) ) )
+      {
+      BLASTER_WriteDSP( DSP_8BitHighSpeedAutoInitRecord );
+      BLASTER_HaltTransferCommand = DSP_Reset;
+      }
+   else
+      {
+      BLASTER_WriteDSP( DSP_8BitAutoInitRecord );
+      BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+      }
+
+   BLASTER_SoundRecording = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_DSP1xx_BeginRecord
+
+   Starts recording of digitized sound on cards compatible with DSP
+   version 1.xx.
+---------------------------------------------------------------------*/
+
+int BLASTER_DSP1xx_BeginRecord
+   (
+   int length
+   )
+
+   {
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   SampleLength = length - 1;
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+
+   // Program DSP to play sound
+   BLASTER_WriteDSP( DSP_Old8BitADC );
+   BLASTER_WriteDSP( LoByte );
+   BLASTER_WriteDSP( HiByte );
+
+   BLASTER_HaltTransferCommand = DSP_Halt8bitTransfer;
+
+   BLASTER_SoundRecording = TRUE;
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_BeginBufferedRecord
+
+   Begins multibuffered recording of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+int BLASTER_BeginBufferedRecord
+   (
+   char    *BufferStart,
+   int      BufferSize,
+   int      NumDivisions,
+   unsigned SampleRate,
+   int      MixMode,
+   void  ( *CallBackFunc )( void )
+   )
+
+   {
+   int DmaStatus;
+   int TransferLength;
+
+//JIM
+//   if ( BLASTER_SoundPlaying || BLASTER_SoundRecording )
+      {
+      BLASTER_StopPlayback();
+      }
+
+   BLASTER_SetMixMode( MixMode );
+
+   DmaStatus = BLASTER_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitWrite );
+   if ( DmaStatus == BLASTER_Error )
+      {
+      return( BLASTER_Error );
+      }
+
+   BLASTER_SetPlaybackRate( SampleRate );
+
+   BLASTER_SetCallBack( CallBackFunc );
+
+   BLASTER_EnableInterrupt();
+
+   // Turn off speaker
+   BLASTER_SpeakerOff();
+
+   TransferLength = BufferSize / NumDivisions;
+   BLASTER_TransferLength = TransferLength;
+
+   //  Program the sound card to start the transfer.
+   if ( BLASTER_Version < DSP_Version2xx )
+      {
+      BLASTER_DSP1xx_BeginRecord( TransferLength );
+      }
+   else if ( BLASTER_Version < DSP_Version4xx )
+      {
+      BLASTER_DSP2xx_BeginRecord( TransferLength );
+      }
+   else
+      {
+      BLASTER_DSP4xx_BeginRecord( TransferLength );
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_WriteMixer
+
+   Writes a byte of data to the Sound Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+void BLASTER_WriteMixer
+   (
+   int reg,
+   int data
+   )
+
+   {
+   outp( BLASTER_MixerAddress + BLASTER_MixerAddressPort, reg );
+   outp( BLASTER_MixerAddress + BLASTER_MixerDataPort, data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ReadMixer
+
+   Reads a byte of data from the Sound Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+int BLASTER_ReadMixer
+   (
+   int reg
+   )
+
+   {
+   int data;
+
+   outp( BLASTER_MixerAddress + BLASTER_MixerAddressPort, reg );
+   data = inp( BLASTER_MixerAddress + BLASTER_MixerDataPort );
+   return( data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetVoiceVolume
+
+   Reads the average volume of the digitized sound channel from the
+   Sound Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetVoiceVolume
+   (
+   void
+   )
+
+   {
+   int volume;
+   int left;
+   int right;
+
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         left   = BLASTER_ReadMixer( MIXER_SBProVoice );
+         right  = ( left & 0x0f ) << 4;
+         left  &= 0xf0;
+         volume = ( left + right ) / 2;
+         break;
+
+      case SB16 :
+         left  = BLASTER_ReadMixer( MIXER_SB16VoiceLeft );
+         right = BLASTER_ReadMixer( MIXER_SB16VoiceRight );
+         volume = ( left + right ) / 2;
+         break;
+
+      default :
+         BLASTER_SetErrorCode( BLASTER_NoMixer );
+         volume = BLASTER_Error;
+      }
+
+   return( volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetVoiceVolume
+
+   Sets the volume of the digitized sound channel on the Sound
+   Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+int BLASTER_SetVoiceVolume
+   (
+   int volume
+   )
+
+   {
+   int data;
+   int status;
+
+   volume = min( 255, volume );
+   volume = max( 0, volume );
+
+   status = BLASTER_Ok;
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         data = ( volume & 0xf0 ) + ( volume >> 4 );
+         BLASTER_WriteMixer( MIXER_SBProVoice, data );
+         break;
+
+      case SB16 :
+         BLASTER_WriteMixer( MIXER_SB16VoiceLeft, volume & 0xf8 );
+         BLASTER_WriteMixer( MIXER_SB16VoiceRight, volume & 0xf8 );
+         break;
+
+      default :
+         BLASTER_SetErrorCode( BLASTER_NoMixer );
+         status = BLASTER_Error;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetMidiVolume
+
+   Reads the average volume of the Midi sound channel from the
+   Sound Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetMidiVolume
+   (
+   void
+   )
+
+   {
+   int volume;
+   int left;
+   int right;
+
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         left   = BLASTER_ReadMixer( MIXER_SBProMidi );
+         right  = ( left & 0x0f ) << 4;
+         left  &= 0xf0;
+         volume = ( left + right ) / 2;
+         break;
+
+      case SB16 :
+         left  = BLASTER_ReadMixer( MIXER_SB16MidiLeft );
+         right = BLASTER_ReadMixer( MIXER_SB16MidiRight );
+         volume = ( left + right ) / 2;
+         break;
+
+      default :
+         BLASTER_SetErrorCode( BLASTER_NoMixer );
+         volume = BLASTER_Error;
+      }
+
+   return( volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetMidiVolume
+
+   Sets the volume of the Midi sound channel on the Sound
+   Blaster's mixer chip.
+---------------------------------------------------------------------*/
+
+int BLASTER_SetMidiVolume
+   (
+   int volume
+   )
+
+   {
+   int data;
+   int status;
+
+   volume = min( 255, volume );
+   volume = max( 0, volume );
+
+   status = BLASTER_Ok;
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         data = ( volume & 0xf0 ) + ( volume >> 4 );
+         BLASTER_WriteMixer( MIXER_SBProMidi, data );
+         break;
+
+      case SB16 :
+         BLASTER_WriteMixer( MIXER_SB16MidiLeft, volume & 0xf8 );
+         BLASTER_WriteMixer( MIXER_SB16MidiRight, volume & 0xf8 );
+         break;
+
+      default :
+         BLASTER_SetErrorCode( BLASTER_NoMixer );
+         status = BLASTER_Error;
+      }
+
+   return( status );
+   }
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_CardHasMixer
+
+   Checks if the selected Sound Blaster card has a mixer.
+---------------------------------------------------------------------*/
+
+int BLASTER_CardHasMixer
+   (
+   void
+   )
+
+   {
+   return( BLASTER_Card.HasMixer );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SaveVoiceVolume
+
+   Saves the user's voice mixer settings.
+---------------------------------------------------------------------*/
+
+void BLASTER_SaveVoiceVolume
+   (
+   void
+   )
+
+   {
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         BLASTER_OriginalVoiceVolumeLeft =
+            BLASTER_ReadMixer( MIXER_SBProVoice );
+         break;
+
+      case SB16 :
+         BLASTER_OriginalVoiceVolumeLeft =
+            BLASTER_ReadMixer( MIXER_SB16VoiceLeft );
+         BLASTER_OriginalVoiceVolumeRight =
+            BLASTER_ReadMixer( MIXER_SB16VoiceRight );
+         break;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_RestoreVoiceVolume
+
+   Restores the user's voice mixer settings.
+---------------------------------------------------------------------*/
+
+void BLASTER_RestoreVoiceVolume
+   (
+   void
+   )
+
+   {
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         BLASTER_WriteMixer( MIXER_SBProVoice,
+            BLASTER_OriginalVoiceVolumeLeft );
+         break;
+
+      case SB16 :
+         BLASTER_WriteMixer( MIXER_SB16VoiceLeft,
+            BLASTER_OriginalVoiceVolumeLeft );
+         BLASTER_WriteMixer( MIXER_SB16VoiceRight,
+            BLASTER_OriginalVoiceVolumeRight );
+         break;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SaveMidiVolume
+
+   Saves the user's FM mixer settings.
+---------------------------------------------------------------------*/
+
+void BLASTER_SaveMidiVolume
+   (
+   void
+   )
+
+   {
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         BLASTER_OriginalMidiVolumeLeft =
+            BLASTER_ReadMixer( MIXER_SBProMidi );
+         break;
+
+      case SB16 :
+         BLASTER_OriginalMidiVolumeLeft =
+            BLASTER_ReadMixer( MIXER_SB16MidiLeft );
+         BLASTER_OriginalMidiVolumeRight =
+            BLASTER_ReadMixer( MIXER_SB16MidiRight );
+         break;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_RestoreMidiVolume
+
+   Restores the user's FM mixer settings.
+---------------------------------------------------------------------*/
+
+void BLASTER_RestoreMidiVolume
+   (
+   void
+   )
+
+   {
+   switch( BLASTER_MixerType )
+      {
+      case SBPro :
+      case SBPro2 :
+         BLASTER_WriteMixer( MIXER_SBProMidi,
+            BLASTER_OriginalMidiVolumeLeft );
+         break;
+
+      case SB16 :
+         BLASTER_WriteMixer( MIXER_SB16MidiLeft,
+            BLASTER_OriginalMidiVolumeLeft );
+         BLASTER_WriteMixer( MIXER_SB16MidiRight,
+            BLASTER_OriginalMidiVolumeRight );
+         break;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetEnv
+
+   Retrieves the BLASTER environment settings and returns them to
+   the caller.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetEnv
+   (
+   BLASTER_CONFIG *Config
+   )
+
+   {
+   char *Blaster;
+   char parameter;
+
+   Config->Address   = UNDEFINED;
+   Config->Type      = UNDEFINED;
+   Config->Interrupt = UNDEFINED;
+   Config->Dma8      = UNDEFINED;
+   Config->Dma16     = UNDEFINED;
+   Config->Midi      = UNDEFINED;
+   Config->Emu       = UNDEFINED;
+
+   Blaster = getenv( "BLASTER" );
+   if ( Blaster == NULL )
+      {
+      BLASTER_SetErrorCode( BLASTER_EnvNotFound );
+      return( BLASTER_Error );
+      }
+
+   while( *Blaster != 0 )
+      {
+      if ( *Blaster == ' ' )
+         {
+         Blaster++;
+         continue;
+         }
+
+      parameter = toupper( *Blaster );
+      Blaster++;
+
+      if ( !isxdigit( *Blaster ) )
+         {
+         BLASTER_SetErrorCode( BLASTER_InvalidParameter );
+         return( BLASTER_Error );
+         }
+
+      switch( parameter )
+         {
+         case BlasterEnv_Address :
+            sscanf( Blaster, "%x", &Config->Address );
+            break;
+         case BlasterEnv_Interrupt :
+            sscanf( Blaster, "%d", &Config->Interrupt );
+            break;
+         case BlasterEnv_8bitDma :
+            sscanf( Blaster, "%d", &Config->Dma8 );
+            break;
+         case BlasterEnv_Type :
+            sscanf( Blaster, "%d", &Config->Type );
+            break;
+         case BlasterEnv_16bitDma :
+            sscanf( Blaster, "%d", &Config->Dma16 );
+            break;
+         case BlasterEnv_Midi :
+            sscanf( Blaster, "%x", &Config->Midi );
+            break;
+         case BlasterEnv_EmuAddress :
+            sscanf( Blaster, "%x", &Config->Emu );
+            break;
+         default  :
+            // Skip the offending data
+            // sscanf( Blaster, "%*s" );
+            break;
+         }
+
+      while( isxdigit( *Blaster ) )
+         {
+         Blaster++;
+         }
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetCardSettings
+
+   Sets up the sound card's parameters.
+---------------------------------------------------------------------*/
+
+int BLASTER_SetCardSettings
+   (
+   BLASTER_CONFIG Config
+   )
+
+   {
+   if ( BLASTER_Installed )
+      {
+      BLASTER_Shutdown();
+      }
+
+   BLASTER_Config.Address   = Config.Address;
+   BLASTER_Config.Type      = Config.Type;
+   BLASTER_Config.Interrupt = Config.Interrupt;
+   BLASTER_Config.Dma8      = Config.Dma8;
+   BLASTER_Config.Dma16     = Config.Dma16;
+   BLASTER_Config.Midi      = Config.Midi;
+   BLASTER_Config.Emu       = Config.Emu;
+   BLASTER_MixerAddress     = Config.Address;
+   BLASTER_MixerType        = Config.Type;
+
+   if ( BLASTER_Config.Emu == UNDEFINED )
+      {
+      BLASTER_Config.Emu = BLASTER_Config.Address + 0x400;
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetCardSettings
+
+   Sets up the sound card's parameters.
+---------------------------------------------------------------------*/
+
+int BLASTER_GetCardSettings
+   (
+   BLASTER_CONFIG *Config
+   )
+
+   {
+   if ( BLASTER_Config.Address == UNDEFINED )
+      {
+      return( BLASTER_Warning );
+      }
+   else
+      {
+      Config->Address   = BLASTER_Config.Address;
+      Config->Type      = BLASTER_Config.Type;
+      Config->Interrupt = BLASTER_Config.Interrupt;
+      Config->Dma8      = BLASTER_Config.Dma8;
+      Config->Dma16     = BLASTER_Config.Dma16;
+      Config->Midi      = BLASTER_Config.Midi;
+      Config->Emu       = BLASTER_Config.Emu;
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_GetCardInfo
+
+   Returns the maximum number of bits that can represent a sample
+   (8 or 16) and the number of channels (1 for mono, 2 for stereo).
+---------------------------------------------------------------------*/
+
+int BLASTER_GetCardInfo
+   (
+   int *MaxSampleBits,
+   int *MaxChannels
+   )
+
+   {
+   if ( BLASTER_Card.MaxMixMode & STEREO )
+      {
+      *MaxChannels = 2;
+      }
+   else
+      {
+      *MaxChannels = 1;
+      }
+
+   if ( BLASTER_Card.MaxMixMode & SIXTEEN_BIT )
+      {
+      *MaxSampleBits = 16;
+      }
+   else
+      {
+      *MaxSampleBits = 8;
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetCallBack
+
+   Specifies the user function to call at the end of a sound transfer.
+---------------------------------------------------------------------*/
+
+void BLASTER_SetCallBack
+   (
+   void ( *func )( void )
+   )
+
+   {
+   BLASTER_CallBack = func;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void BLASTER_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void BLASTER_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( BLASTER_LockStart, BLASTER_LockEnd );
+   DPMI_UnlockMemory( ( void * )&BLASTER_Interrupts[ 0 ],
+      sizeof( BLASTER_Interrupts ) );
+   DPMI_UnlockMemory( ( void * )&BLASTER_SampleSize[ 0 ],
+      sizeof( BLASTER_SampleSize ) );
+   DPMI_Unlock( BLASTER_Card );
+   DPMI_Unlock( BLASTER_OldInt );
+   DPMI_Unlock( BLASTER_Config );
+   DPMI_Unlock( BLASTER_Installed );
+   DPMI_Unlock( BLASTER_Version );
+   DPMI_Unlock( BLASTER_DMABuffer );
+   DPMI_Unlock( BLASTER_DMABufferEnd );
+   DPMI_Unlock( BLASTER_CurrentDMABuffer );
+   DPMI_Unlock( BLASTER_TotalDMABufferSize );
+   DPMI_Unlock( BLASTER_TransferLength );
+   DPMI_Unlock( BLASTER_MixMode );
+   DPMI_Unlock( BLASTER_SamplePacketSize );
+   DPMI_Unlock( BLASTER_SampleRate );
+   DPMI_Unlock( BLASTER_HaltTransferCommand );
+   DPMI_Unlock( ( int )BLASTER_SoundPlaying );
+   DPMI_Unlock( ( int )BLASTER_SoundRecording );
+   DPMI_Unlock( BLASTER_CallBack );
+   DPMI_Unlock( BLASTER_IntController1Mask );
+   DPMI_Unlock( BLASTER_IntController2Mask );
+   DPMI_Unlock( BLASTER_MixerAddress );
+   DPMI_Unlock( BLASTER_MixerType );
+   DPMI_Unlock( BLASTER_OriginalMidiVolumeLeft );
+   DPMI_Unlock( BLASTER_OriginalMidiVolumeRight );
+   DPMI_Unlock( BLASTER_OriginalVoiceVolumeLeft );
+   DPMI_Unlock( BLASTER_OriginalVoiceVolumeRight );
+   DPMI_Unlock( GlobalStatus );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int BLASTER_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( BLASTER_LockStart, BLASTER_LockEnd );
+   status |= DPMI_LockMemory( ( void * )&BLASTER_Interrupts[ 0 ],
+      sizeof( BLASTER_Interrupts ) );
+   status |= DPMI_LockMemory( ( void * )&BLASTER_SampleSize[ 0 ],
+      sizeof( BLASTER_SampleSize ) );
+   status |= DPMI_Lock( BLASTER_Card );
+   status |= DPMI_Lock( BLASTER_OldInt );
+   status |= DPMI_Lock( BLASTER_Config );
+   status |= DPMI_Lock( BLASTER_Installed );
+   status |= DPMI_Lock( BLASTER_Version );
+   status |= DPMI_Lock( BLASTER_DMABuffer );
+   status |= DPMI_Lock( BLASTER_DMABufferEnd );
+   status |= DPMI_Lock( BLASTER_CurrentDMABuffer );
+   status |= DPMI_Lock( BLASTER_TotalDMABufferSize );
+   status |= DPMI_Lock( BLASTER_TransferLength );
+   status |= DPMI_Lock( BLASTER_MixMode );
+   status |= DPMI_Lock( BLASTER_SamplePacketSize );
+   status |= DPMI_Lock( BLASTER_SampleRate );
+   status |= DPMI_Lock( BLASTER_HaltTransferCommand );
+   status |= DPMI_Lock( ( ( int )BLASTER_SoundPlaying ) );
+   status |= DPMI_Lock( ( ( int )BLASTER_SoundRecording ) );
+   status |= DPMI_Lock( BLASTER_CallBack );
+   status |= DPMI_Lock( BLASTER_IntController1Mask );
+   status |= DPMI_Lock( BLASTER_IntController2Mask );
+   status |= DPMI_Lock( BLASTER_MixerAddress );
+   status |= DPMI_Lock( BLASTER_MixerType );
+   status |= DPMI_Lock( BLASTER_OriginalMidiVolumeLeft );
+   status |= DPMI_Lock( BLASTER_OriginalMidiVolumeRight );
+   status |= DPMI_Lock( BLASTER_OriginalVoiceVolumeLeft );
+   status |= DPMI_Lock( BLASTER_OriginalVoiceVolumeRight );
+   status |= DPMI_Lock( GlobalStatus );
+
+   if ( status != DPMI_Ok )
+      {
+      BLASTER_UnlockMemory();
+      BLASTER_SetErrorCode( BLASTER_DPMI_Error );
+      return( BLASTER_Error );
+      }
+
+   return( BLASTER_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: allocateTimerStack
+
+   Allocate a block of memory from conventional (low) memory and return
+   the selector (which can go directly into a segment register) of the
+   memory block or 0 if an error occured.
+---------------------------------------------------------------------*/
+
+static unsigned short allocateTimerStack
+   (
+   unsigned short size
+   )
+
+   {
+   union REGS regs;
+
+   // clear all registers
+   memset( &regs, 0, sizeof( regs ) );
+
+   // DPMI allocate conventional memory
+   regs.w.ax = 0x100;
+
+   // size in paragraphs
+   regs.w.bx = ( size + 15 ) / 16;
+
+   int386( 0x31, &regs, &regs );
+   if (!regs.w.cflag)
+      {
+      // DPMI call returns selector in dx
+      // (ax contains real mode segment
+      // which is ignored here)
+
+      return( regs.w.dx );
+      }
+
+   // Couldn't allocate memory.
+   return( NULL );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: deallocateTimerStack
+
+   Deallocate a block of conventional (low) memory given a selector to
+   it.  Assumes the block was allocated with DPMI function 0x100.
+---------------------------------------------------------------------*/
+
+static void deallocateTimerStack
+   (
+   unsigned short selector
+   )
+
+   {
+   union REGS regs;
+
+   if ( selector != NULL )
+      {
+      // clear all registers
+      memset( &regs, 0, sizeof( regs ) );
+
+      regs.w.ax = 0x101;
+      regs.w.dx = selector;
+      int386( 0x31, &regs, &regs );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_SetupWaveBlaster
+
+   Allows the WaveBlaster to play music while the Sound Blaster 16
+   plays digital sound.
+---------------------------------------------------------------------*/
+
+void BLASTER_SetupWaveBlaster
+   (
+   void
+   )
+
+   {
+
+   if ( BLASTER_MixerType == SB16 )
+      {
+      // Disable MPU401 interrupts.  If they are not disabled,
+      // the SB16 will not produce sound or music.
+      BLASTER_WaveBlasterState = BLASTER_ReadMixer( MIXER_DSP4xxISR_Enable );
+      BLASTER_WriteMixer( MIXER_DSP4xxISR_Enable, MIXER_DisableMPU401Interrupts );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_ShutdownWaveBlaster
+
+   Restores WaveBlaster mixer to original state.
+---------------------------------------------------------------------*/
+
+void BLASTER_ShutdownWaveBlaster
+   (
+   void
+   )
+
+   {
+   if ( BLASTER_MixerType == SB16 )
+      {
+      // Restore the state of MPU401 interrupts.  If they are not disabled,
+      // the SB16 will not produce sound or music.
+      BLASTER_WriteMixer( MIXER_DSP4xxISR_Enable, BLASTER_WaveBlasterState );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_Init
+
+   Initializes the sound card and prepares the module to play
+   digitized sounds.
+---------------------------------------------------------------------*/
+
+int BLASTER_Init
+   (
+   void
+   )
+
+   {
+   int Irq;
+   int Interrupt;
+   int status;
+
+   if ( BLASTER_Installed )
+      {
+      BLASTER_Shutdown();
+      }
+
+   if ( BLASTER_Config.Address == UNDEFINED )
+      {
+      BLASTER_SetErrorCode( BLASTER_AddrNotSet );
+      return( BLASTER_Error );
+      }
+
+   // Save the interrupt masks
+   BLASTER_IntController1Mask = inp( 0x21 );
+   BLASTER_IntController2Mask = inp( 0xA1 );
+
+   status = BLASTER_ResetDSP();
+   if ( status == BLASTER_Ok )
+      {
+      BLASTER_SaveVoiceVolume();
+
+      BLASTER_SoundPlaying = FALSE;
+
+      BLASTER_SetCallBack( NULL );
+
+      BLASTER_DMABuffer = NULL;
+
+      BLASTER_Version = BLASTER_GetDSPVersion();
+
+      BLASTER_SetPlaybackRate( BLASTER_DefaultSampleRate );
+      BLASTER_SetMixMode( BLASTER_DefaultMixMode );
+
+      if ( BLASTER_Config.Dma16 != UNDEFINED )
+         {
+         status = DMA_VerifyChannel( BLASTER_Config.Dma16 );
+         if ( status == DMA_Error )
+            {
+            BLASTER_SetErrorCode( BLASTER_DmaError );
+            return( BLASTER_Error );
+            }
+         }
+
+      if ( BLASTER_Config.Dma8 != UNDEFINED )
+         {
+         status = DMA_VerifyChannel( BLASTER_Config.Dma8 );
+         if ( status == DMA_Error )
+            {
+            BLASTER_SetErrorCode( BLASTER_DmaError );
+            return( BLASTER_Error );
+            }
+         }
+
+      // Install our interrupt handler
+      Irq = BLASTER_Config.Interrupt;
+      if ( !VALID_IRQ( Irq ) )
+         {
+         BLASTER_SetErrorCode( BLASTER_InvalidIrq );
+         return( BLASTER_Error );
+         }
+
+      Interrupt = BLASTER_Interrupts[ Irq ];
+      if ( Interrupt == INVALID )
+         {
+         BLASTER_SetErrorCode( BLASTER_InvalidIrq );
+         return( BLASTER_Error );
+         }
+
+      status = BLASTER_LockMemory();
+      if ( status != BLASTER_Ok )
+         {
+         BLASTER_UnlockMemory();
+         return( status );
+         }
+
+      StackSelector = allocateTimerStack( kStackSize );
+      if ( StackSelector == NULL )
+         {
+         BLASTER_UnlockMemory();
+         BLASTER_SetErrorCode( BLASTER_OutOfMemory );
+         return( BLASTER_Error );
+         }
+
+      // Leave a little room at top of stack just for the hell of it...
+      StackPointer = kStackSize - sizeof( long );
+
+      BLASTER_OldInt = _dos_getvect( Interrupt );
+      if ( Irq < 8 )
+         {
+         _dos_setvect( Interrupt, BLASTER_ServiceInterrupt );
+         }
+      else
+         {
+         status = IRQ_SetVector( Interrupt, BLASTER_ServiceInterrupt );
+         if ( status != IRQ_Ok )
+            {
+            BLASTER_UnlockMemory();
+            deallocateTimerStack( StackSelector );
+            StackSelector = NULL;
+            BLASTER_SetErrorCode( BLASTER_UnableToSetIrq );
+            return( BLASTER_Error );
+            }
+         }
+
+      BLASTER_Installed = TRUE;
+      status = BLASTER_Ok;
+      }
+
+   BLASTER_SetErrorCode( status );
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: BLASTER_Shutdown
+
+   Ends transfer of sound data to the sound card and restores the
+   system resources used by the card.
+---------------------------------------------------------------------*/
+
+void BLASTER_Shutdown
+   (
+   void
+   )
+
+   {
+   int Irq;
+   int Interrupt;
+
+   // Halt the DMA transfer
+   BLASTER_StopPlayback();
+
+   BLASTER_RestoreVoiceVolume();
+
+   // Reset the DSP
+   BLASTER_ResetDSP();
+
+   // Restore the original interrupt
+   Irq = BLASTER_Config.Interrupt;
+   Interrupt = BLASTER_Interrupts[ Irq ];
+   if ( Irq >= 8 )
+      {
+      IRQ_RestoreVector( Interrupt );
+      }
+   _dos_setvect( Interrupt, BLASTER_OldInt );
+
+   BLASTER_SoundPlaying = FALSE;
+
+   BLASTER_DMABuffer = NULL;
+
+   BLASTER_SetCallBack( NULL );
+
+   BLASTER_UnlockMemory();
+
+   deallocateTimerStack( StackSelector );
+   StackSelector = NULL;
+
+   BLASTER_Installed = FALSE;
+   }
--- /dev/null
+++ b/Game/src/audiolib/blaster.h
@@ -1,0 +1,148 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: BLASTER.H
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Public header for BLASTER.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __BLASTER_H
+#define __BLASTER_H
+
+typedef struct
+   {
+   unsigned Address;
+   unsigned Type;
+   unsigned Interrupt;
+   unsigned Dma8;
+   unsigned Dma16;
+   unsigned Midi;
+   unsigned Emu;
+   } BLASTER_CONFIG;
+
+extern BLASTER_CONFIG BLASTER_Config;
+extern int BLASTER_DMAChannel;
+
+#define UNDEFINED -1
+
+enum BLASTER_ERRORS
+   {
+   BLASTER_Warning = -2,
+   BLASTER_Error = -1,
+   BLASTER_Ok = 0,
+   BLASTER_EnvNotFound,
+   BLASTER_AddrNotSet,
+   BLASTER_DMANotSet,
+   BLASTER_DMA16NotSet,
+   BLASTER_InvalidParameter,
+   BLASTER_CardNotReady,
+   BLASTER_NoSoundPlaying,
+   BLASTER_InvalidIrq,
+   BLASTER_UnableToSetIrq,
+   BLASTER_DmaError,
+   BLASTER_NoMixer,
+   BLASTER_DPMI_Error,
+   BLASTER_OutOfMemory
+   };
+
+enum BLASTER_Types
+   {
+   SB     = 1,
+   SBPro  = 2,
+   SB20   = 3,
+   SBPro2 = 4,
+   SB16   = 6
+   };
+
+#define BLASTER_MinCardType    SB
+#define BLASTER_MaxCardType    SB16
+
+#define STEREO      1
+#define SIXTEEN_BIT 2
+
+#define MONO_8BIT    0
+#define STEREO_8BIT  ( STEREO )
+#define MONO_16BIT   ( SIXTEEN_BIT )
+#define STEREO_16BIT ( STEREO | SIXTEEN_BIT )
+
+#define BLASTER_MaxMixMode        STEREO_16BIT
+
+#define MONO_8BIT_SAMPLE_SIZE    1
+#define MONO_16BIT_SAMPLE_SIZE   2
+#define STEREO_8BIT_SAMPLE_SIZE  ( 2 * MONO_8BIT_SAMPLE_SIZE )
+#define STEREO_16BIT_SAMPLE_SIZE ( 2 * MONO_16BIT_SAMPLE_SIZE )
+
+#define BLASTER_DefaultSampleRate 11000
+#define BLASTER_DefaultMixMode    MONO_8BIT
+#define BLASTER_MaxIrq            15
+
+char *BLASTER_ErrorString( int ErrorNumber );
+void  BLASTER_EnableInterrupt( void );
+void  BLASTER_DisableInterrupt( void );
+int   BLASTER_WriteDSP( unsigned data );
+int   BLASTER_ReadDSP( void );
+int   BLASTER_ResetDSP( void );
+int   BLASTER_GetDSPVersion( void );
+void  BLASTER_SpeakerOn( void );
+void  BLASTER_SpeakerOff( void );
+void  BLASTER_SetPlaybackRate( unsigned rate );
+unsigned BLASTER_GetPlaybackRate( void );
+int   BLASTER_SetMixMode( int mode );
+void  BLASTER_StopPlayback( void );
+int   BLASTER_SetupDMABuffer( char *BufferPtr, int BufferSize, int mode );
+int   BLASTER_GetCurrentPos( void );
+int   BLASTER_DSP1xx_BeginPlayback( int length );
+int   BLASTER_DSP2xx_BeginPlayback( int length );
+int   BLASTER_DSP4xx_BeginPlayback( int length );
+int   BLASTER_BeginBufferedRecord( char *BufferStart, int BufferSize,
+          int NumDivisions, unsigned SampleRate, int MixMode,
+          void ( *CallBackFunc )( void ) );
+int   BLASTER_BeginBufferedPlayback( char *BufferStart,
+         int BufferSize, int NumDivisions, unsigned SampleRate,
+         int MixMode, void ( *CallBackFunc )( void ) );
+void  BLASTER_WriteMixer( int reg, int data );
+int   BLASTER_ReadMixer( int reg );
+int   BLASTER_GetVoiceVolume( void );
+int   BLASTER_SetVoiceVolume( int volume );
+int   BLASTER_GetMidiVolume( void );
+int   BLASTER_SetMidiVolume( int volume );
+int   BLASTER_CardHasMixer( void );
+void  BLASTER_SaveVoiceVolume( void );
+void  BLASTER_RestoreVoiceVolume( void );
+void  BLASTER_SaveMidiVolume( void );
+void  BLASTER_RestoreMidiVolume( void );
+int   BLASTER_GetEnv( BLASTER_CONFIG *Config );
+int   BLASTER_SetCardSettings( BLASTER_CONFIG Config );
+int   BLASTER_GetCardSettings( BLASTER_CONFIG *Config );
+int   BLASTER_GetCardInfo( int *MaxSampleBits, int *MaxChannels );
+void  BLASTER_SetCallBack( void ( *func )( void ) );
+void  BLASTER_SetupWaveBlaster( void );
+void  BLASTER_ShutdownWaveBlaster( void );
+int   BLASTER_Init( void );
+void  BLASTER_Shutdown( void );
+void  BLASTER_UnlockMemory( void );
+int   BLASTER_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/ctaweapi.h
@@ -1,0 +1,352 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/****************************************************************************\
+*                                                                            *
+* CTAWEAPI.H SB AWE32 DOS API header                                         *
+*                                                                            *
+* (C) Copyright Creative Technology Ltd. 1992-94. All rights reserved        *
+* worldwide.                                                                 *
+*                                                                            *
+* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY      *
+* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE        *
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR      *
+* PURPOSE.                                                                   *
+*                                                                            *
+* You have a royalty-free right to use, modify, reproduce and                *
+* distribute the Sample Files (and/or any modified version) in               *
+* any way you find useful, provided that you agree to                        *
+* the Creative's Software Licensing Aggreement and you also agree that       *
+* Creative has no warranty obligations or liability for any Sample Files.    *
+*                                                                            *
+\****************************************************************************/
+
+/****************************************************************************\
+*      File name       : CTAWEAPI.H                                          *
+*                                                                            *
+*      Programmer      : Creative SB AWE32 Team                              *
+*                        Creative Technology Ltd, 1994. All rights reserved. *
+*                                                                            *
+*      Version         : 2.0b                                                *
+*                                                                            *
+\****************************************************************************/
+
+#ifndef _CTAWEAPI
+#define _CTAWEAPI
+
+
+#define MAXBANKS            64      /* maximum number of banks */
+#define MAXNRPN             32      /* maximum number of NRPN */
+
+
+#if defined(__FLAT__) || defined(__HIGHC__) || defined(DOS386)
+#define PACKETSIZE      8192        /* packet size for 32bit libraries */
+#else
+#define PACKETSIZE      512         /* packet size for real mode libraries */
+#endif
+
+
+#if defined(__FLAT__)
+    #define NEAR
+    #define FAR
+#endif
+
+
+#if defined(__SC__)
+    #pragma pack(1)
+    #if defined(DOS386)
+        #define NEAR
+        #define FAR
+    #endif
+#endif
+
+
+#if defined(__WATCOMC__)
+    #pragma pack(1)
+#endif
+
+
+#if defined(__HIGHC__)
+    #define NEAR
+    #define FAR
+    #define PASCAL  _DCC((_DEFAULT_CALLING_CONVENTION|_CALLEE_POPS_STACK) & \
+                         ~ (_REVERSE_PARMS|_OVERLOADED))
+    #pragma Push_align_members(1)
+    #pragma Global_aliasing_convention("_%r")
+#endif
+
+
+typedef int                     BOOL;
+#define FALSE                   0
+#define TRUE                    1
+
+typedef unsigned char		BYTE;
+typedef unsigned short		WORD;
+typedef unsigned long		DWORD;
+
+typedef short int               SHORT;
+typedef unsigned int		UINT;
+typedef signed long 		LONG;
+
+#ifndef FAR
+#define FAR                     __far
+#endif
+
+#ifndef HUGE
+#define HUGE                    __huge
+#endif
+
+#ifndef PASCAL
+#define PASCAL                  __pascal
+#endif
+
+typedef void FAR*               LPVOID;
+typedef BYTE FAR*               LPBYTE;
+typedef WORD FAR*               LPWORD;
+typedef DWORD FAR*              LPDWORD;
+
+#define LOBYTE(w)               ((BYTE)(w))
+#define HIBYTE(w)               ((BYTE)(((UINT)(w) >> 8) & 0xFF))
+
+#define LOWORD(l)               ((WORD)(DWORD)(l))
+#define HIWORD(l)               ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* Start of modules */
+extern int* __midieng_code(void);
+extern int* __hardware_code(void);
+extern int* __sbkload_code(void);
+extern int* __nrpn_code(void);
+extern int __midivar_data;
+extern int __nrpnvar_data;
+extern int __embed_data;
+
+
+typedef char SCRATCH[702];
+typedef char SOUNDFONT[124];
+typedef char GCHANNEL[20];
+typedef char MIDICHANNEL[32];
+typedef char NRPNCHANNEL[96];
+
+typedef struct {
+    SHORT bank_no;          /* Slot number being used */
+    SHORT total_banks;      /* Total number of banks */
+    LONG FAR* banksizes;    /* Pointer to a list of bank sizes */
+    LONG file_size;         /* exact size of the sound font file */
+    char FAR* data;         /* Address of buffer of size >= PACKETSIZE */
+    char FAR* presets;      /* Allocated memory for preset data */
+
+    LONG total_patch_ram;   /* Total patch ram available */
+    SHORT no_sample_packets;/* Number of packets of sound sample to stream */
+    LONG sample_seek;       /* Start file location of sound sample */
+    LONG preset_seek;       /* Address of preset_seek location */
+    LONG preset_read_size;  /* Number of bytes from preset_seek to allocate and read */
+    LONG preset_size;       /* Preset actual size */
+} SOUND_PACKET;
+
+typedef struct {
+    SHORT tag;              /* Must be 0x100 or 0x101 */
+    SHORT preset_size;      /* Preset table of this size is required */
+    SHORT no_wave_packets;  /* Number of packets of Wave sample to stream. */
+    LONG reserved;
+
+    SHORT bank_no;          /* bank number */
+    char FAR* data;         /* Address of packet of size PACKETSIZE */
+    char FAR* presets;      /* Allocated memory for preset data */
+    LONG sample_size;       /* Sample size, i.e. number of samples */
+    LONG samples_per_sec;   /* Samples per second */
+    SHORT bits_per_sample;  /* Bits per sample, 8 or 16 */
+    SHORT no_channels;      /* Number of channels, 1=mono, 2=stereo */
+    SHORT looping;          /* Looping? 0=no, 1=yes */
+    LONG startloop;         /* if looping, then these are the addresses */
+    LONG endloop;
+    SHORT release;          /* release time, 0=24ms, 8191=23.78s */
+} WAVE_PACKET;
+
+typedef struct {
+    LPBYTE SPad1;
+    LPBYTE SPad2;
+    LPBYTE SPad3;
+    LPBYTE SPad4;
+    LPBYTE SPad5;
+    LPBYTE SPad6;
+    LPBYTE SPad7;
+} SOUNDPAD;
+
+/* AWE32 variables */
+extern WORD         awe32NumG;
+extern WORD         awe32BaseAddx;
+extern DWORD        awe32DramSize;
+
+/* MIDI variables */
+extern SCRATCH      awe32Scratch;
+extern SOUNDFONT    awe32SFont[4];
+extern GCHANNEL     awe32GChannel[32];
+extern MIDICHANNEL  awe32MIDIChannel[16];
+extern SOUNDPAD     awe32SoundPad;
+
+/* NRPN variables */
+extern NRPNCHANNEL  awe32NRPNChannel[16];
+
+/* SoundFont objects */
+extern BYTE awe32SPad1Obj[];
+extern BYTE awe32SPad2Obj[];
+extern BYTE awe32SPad3Obj[];
+extern BYTE awe32SPad4Obj[];
+extern BYTE awe32SPad5Obj[];
+extern BYTE awe32SPad6Obj[];
+extern BYTE awe32SPad7Obj[];
+
+/* AWE register functions */
+extern void PASCAL awe32RegW(WORD, WORD);
+extern WORD PASCAL awe32RegRW(WORD);
+extern void PASCAL awe32RegDW(WORD, DWORD);
+extern DWORD PASCAL awe32RegRDW(WORD);
+
+/* MIDI support functions */
+extern WORD PASCAL awe32InitMIDI(void);
+extern WORD PASCAL awe32NoteOn(WORD, WORD, WORD);
+extern WORD PASCAL awe32NoteOff(WORD, WORD, WORD);
+extern WORD PASCAL awe32ProgramChange(WORD, WORD);
+extern WORD PASCAL awe32Controller(WORD, WORD, WORD);
+extern WORD PASCAL awe32PolyKeyPressure(WORD, WORD, WORD);
+extern WORD PASCAL awe32ChannelPressure(WORD, WORD);
+extern WORD PASCAL awe32PitchBend(WORD, WORD, WORD);
+extern WORD PASCAL awe32Sysex(WORD, LPBYTE, WORD);
+extern WORD PASCAL __awe32NoteOff(WORD, WORD, WORD, WORD);
+extern WORD PASCAL __awe32IsPlaying(WORD, WORD, WORD, WORD);
+
+/* NRPN support functions */
+extern WORD PASCAL awe32InitNRPN(void);
+
+/* Hardware support functions */
+extern WORD PASCAL awe32Detect(WORD);
+extern WORD PASCAL awe32InitHardware(void);
+extern WORD PASCAL awe32Terminate(void);
+
+/* SoundFont support functions */
+extern WORD PASCAL awe32TotalPatchRam(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32DefineBankSizes(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32SFontLoadRequest(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32StreamSample(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32SetPresets(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32ReleaseBank(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32ReleaseAllBanks(SOUND_PACKET FAR*);
+extern WORD PASCAL awe32WPLoadRequest(WAVE_PACKET FAR*);
+extern WORD PASCAL awe32WPLoadWave(WAVE_PACKET FAR*);
+extern WORD PASCAL awe32WPStreamWave(WAVE_PACKET FAR*);
+extern WORD PASCAL awe32WPBuildSFont(WAVE_PACKET FAR*);
+
+/* End of modules */
+extern int* __midieng_ecode(void);
+extern int* __hardware_ecode(void);
+extern int* __sbkload_ecode(void);
+extern int* __nrpn_ecode(void);
+extern int __midivar_edata;
+extern int __nrpnvar_edata;
+extern int __embed_edata;
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#if defined(__SC__)
+    #pragma pack()
+#endif
+
+
+#if defined(__HIGHC__)
+    #pragma Pop_align_members
+    #pragma Global_aliasing_convention()
+    #pragma Alias(awe32RegW,"AWE32REGW")
+    #pragma Alias(awe32RegRW,"AWE32REGRW")
+    #pragma Alias(awe32RegDW,"AWE32REGDW")
+    #pragma Alias(awe32RegRDW,"AWE32REGRDW")
+    #pragma Alias(awe32InitMIDI,"AWE32INITMIDI")
+    #pragma Alias(awe32NoteOn,"AWE32NOTEON")
+    #pragma Alias(awe32NoteOff,"AWE32NOTEOFF")
+    #pragma Alias(awe32ProgramChange,"AWE32PROGRAMCHANGE")
+    #pragma Alias(awe32Controller,"AWE32CONTROLLER")
+    #pragma Alias(awe32PolyKeyPressure,"AWE32POLYKEYPRESSURE")
+    #pragma Alias(awe32ChannelPressure,"AWE32CHANNELPRESSURE")
+    #pragma Alias(awe32PitchBend,"AWE32PITCHBEND")
+    #pragma Alias(awe32Sysex,"AWE32SYSEX")
+    #pragma Alias(__awe32NoteOff,"__AWE32NOTEOFF")
+    #pragma Alias(__awe32IsPlaying,"__AWE32ISPLAYING")
+    #pragma Alias(awe32InitNRPN,"AWE32INITNRPN")
+    #pragma Alias(awe32Detect,"AWE32DETECT")
+    #pragma Alias(awe32InitHardware,"AWE32INITHARDWARE")
+    #pragma Alias(awe32Terminate,"AWE32TERMINATE")
+    #pragma Alias(awe32TotalPatchRam,"AWE32TOTALPATCHRAM")
+    #pragma Alias(awe32DefineBankSizes,"AWE32DEFINEBANKSIZES")
+    #pragma Alias(awe32SFontLoadRequest,"AWE32SFONTLOADREQUEST")
+    #pragma Alias(awe32StreamSample,"AWE32STREAMSAMPLE")
+    #pragma Alias(awe32SetPresets,"AWE32SETPRESETS")
+    #pragma Alias(awe32ReleaseBank,"AWE32RELEASEBANK")
+    #pragma Alias(awe32ReleaseAllBanks,"AWE32RELEASEALLBANKS")
+    #pragma Alias(awe32WPLoadRequest,"AWE32WPLOADREQUEST")
+    #pragma Alias(awe32WPLoadWave,"AWE32WPLOADWAVE")
+    #pragma Alias(awe32WPStreamWave,"AWE32WPSTREAMWAVE")
+    #pragma Alias(awe32WPBuildSFont,"AWE32WPBUILDSFONT")
+#endif
+
+
+#if defined(__WATCOMC__)
+    #pragma pack()
+    #pragma aux awe32NumG "_*"
+    #pragma aux awe32BaseAddx "_*"
+    #pragma aux awe32DramSize "_*"
+    #pragma aux awe32Scratch "_*"
+    #pragma aux awe32SFont "_*"
+    #pragma aux awe32GChannel "_*"
+    #pragma aux awe32MIDIChannel "_*"
+    #pragma aux awe32SoundPad "_*"
+    #pragma aux awe32NRPNChannel "_*"
+    #pragma aux awe32SPad1Obj "_*"
+    #pragma aux awe32SPad2Obj "_*"
+    #pragma aux awe32SPad3Obj "_*"
+    #pragma aux awe32SPad4Obj "_*"
+    #pragma aux awe32SPad5Obj "_*"
+    #pragma aux awe32SPad6Obj "_*"
+    #pragma aux awe32SPad7Obj "_*"
+    #pragma aux __midieng_code "_*"
+    #pragma aux __midieng_ecode "_*"
+    #pragma aux __hardware_code "_*"
+    #pragma aux __hardware_ecode "_*"
+    #pragma aux __sbkload_code "_*"
+    #pragma aux __sbkload_ecode "_*"
+    #pragma aux __nrpn_code "_*"
+    #pragma aux __nrpn_ecode "_*"
+    #pragma aux __midivar_data "_*"
+    #pragma aux __midivar_edata "_*"
+    #pragma aux __nrpnvar_data "_*"
+    #pragma aux __nrpnvar_edata "_*"
+    #pragma aux __embed_data "_*"
+    #pragma aux __embed_edata "_*"
+#endif
+
+
+#endif      /* _CTAWEAPI */
--- /dev/null
+++ b/Game/src/audiolib/debugio.c
@@ -1,0 +1,251 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "debugio.h"
+
+static unsigned short disp_offset = 160 * 24;
+static void myutoa( unsigned num, char *string, int radix );
+static void myitoa( int num, char *string, int radix );
+
+void DB_SetXY
+   (
+   int x,
+   int y
+   )
+
+   {
+   disp_offset = ( x * 2 ) + ( y * 160 );
+   }
+
+void DB_PutChar
+   (
+   char ch
+   )
+
+   {
+   int j;
+   char *disp_start = (char *)( 0xb0000 );
+
+   if ( disp_offset >= 160 * 24 )
+      {
+      for ( j = 160; j < 160 * 24; j += 2 )
+         {
+         *( disp_start + j - 160 ) = *( disp_start + j );
+         }
+
+      disp_offset = 160 * 23;
+
+      for ( j = disp_offset; j < ( 160 * 24 ); j += 2 )
+         {
+         *( disp_start + j ) = ' ';
+         }
+      }
+
+   if ( ch >= 32 )
+      {
+      *( disp_start + disp_offset ) = ch;
+      disp_offset = disp_offset + 2;
+      }
+
+   if ( ch == '\r' )
+      {
+      disp_offset = disp_offset / 160;
+      disp_offset = disp_offset * 160;
+      }
+
+   if ( ch == '\n' )
+      {
+      disp_offset = disp_offset + 160;
+      if ( disp_offset < 160 * 24 )
+         {
+         for ( j = disp_offset; j < ( ( ( disp_offset / 160 ) + 1 ) *
+            160 ); j += 2 )
+            {
+            *( disp_start + j ) = ' ';
+            }
+         }
+      }
+   }
+
+int DB_PrintString
+   (
+   char *string
+   )
+
+   {
+   int count;
+   char *ptr;
+
+   ptr = string;
+   count = 0;
+
+   while ( *ptr )
+      {
+      DB_PutChar( *ptr );
+      count++;
+      ptr++;
+      }
+
+   return( count );
+   }
+
+static void myutoa
+   (
+   unsigned num,
+   char *string,
+   int radix
+   )
+
+   {
+   int val;
+   int length;
+   int pos;
+   char temp[ 100 ];
+
+   length = 0;
+   do
+      {
+      val = num % radix;
+      if ( val < 10 )
+         {
+         temp[ length ] = '0' + val;
+         }
+      else
+         {
+         temp[ length ] = 'A' + val - 10;
+         }
+      num /= radix;
+      length++;
+      }
+   while( num > 0 );
+
+   pos = 0;
+   while( length > 0 )
+      {
+      length--;
+      string[ length ] = temp[ pos ];
+      pos++;
+      }
+   string[ pos ] = 0;
+   }
+
+static void myitoa
+   (
+   int num,
+   char *string,
+   int radix
+   )
+
+   {
+   if ( num < 0 )
+      {
+      *string++ = '-';
+      num = -num;
+      }
+
+   myutoa( num, string, radix );
+   }
+
+int DB_PrintNum
+   (
+   int number
+   )
+
+   {
+   char string[ 100 ];
+   int  count;
+
+   myitoa( number, &string[ 0 ], 10 );
+   count = DB_PrintString( &string[ 0 ] );
+
+   return( count );
+   }
+
+int DB_PrintUnsigned
+   (
+   unsigned long number,
+   int radix
+   )
+
+   {
+   char string[ 100 ];
+   int  count;
+
+   myutoa( number, &string[ 0 ], radix );
+   count = DB_PrintString( &string[ 0 ] );
+
+   return( count );
+   }
+
+int DB_printf
+   (
+   char *fmt,
+   ...
+   )
+
+   {
+   va_list argptr;
+   int     count;
+   char    *ptr;
+
+   va_start( argptr, fmt );
+   ptr = fmt;
+   count = 0;
+
+   while( *ptr != 0 )
+      {
+      if ( *ptr == '%' )
+         {
+         ptr++;
+         switch( *ptr )
+            {
+            case 0 :
+               return( EOF );
+               break;
+            case 'd' :
+               count += DB_PrintNum( va_arg( argptr, int ) );
+               break;
+            case 's' :
+               count += DB_PrintString( va_arg( argptr, char * ) );
+               break;
+            case 'u' :
+               count += DB_PrintUnsigned( va_arg( argptr, int ), 10 );
+               break;
+            case 'x' :
+            case 'X' :
+               count += DB_PrintUnsigned( va_arg( argptr, int ), 16 );
+               break;
+            }
+         ptr++;
+         }
+      else
+         {
+         DB_PutChar( *ptr );
+         count++;
+         ptr++;
+         }
+      }
+
+   va_end( argptr );
+
+   return( count );
+   }
--- /dev/null
+++ b/Game/src/audiolib/debugio.h
@@ -1,0 +1,30 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __DEBUGIO_H
+#define __DEBUGIO_H
+
+void DB_SetXY( int x, int y );
+void DB_PutChar( char ch );
+int  DB_PrintString( char *string );
+int  DB_PrintNum( int number );
+int  DB_PrintUnsigned( unsigned long number, int radix );
+int  DB_printf( char *fmt, ... );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/dma.c
@@ -1,0 +1,379 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: DMA.C
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Low level routines to for programming the DMA controller for 8 bit
+   and 16 bit transfers.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include "dma.h"
+
+#define DMA_MaxChannel 7
+
+#define VALID   ( 1 == 1 )
+#define INVALID ( !VALID )
+
+#define BYTE 0
+#define WORD 1
+
+typedef struct
+   {
+   int Valid;
+   int Width;
+   int Mask;
+   int Mode;
+   int Clear;
+   int Page;
+   int Address;
+   int Length;
+   } DMA_PORT;
+
+static const DMA_PORT DMA_PortInfo[ DMA_MaxChannel + 1 ] =
+   {
+      {   VALID, BYTE,  0xA,  0xB,  0xC, 0x87,  0x0,  0x1 },
+      {   VALID, BYTE,  0xA,  0xB,  0xC, 0x83,  0x2,  0x3 },
+      { INVALID, BYTE,  0xA,  0xB,  0xC, 0x81,  0x4,  0x5 },
+      {   VALID, BYTE,  0xA,  0xB,  0xC, 0x82,  0x6,  0x7 },
+      { INVALID, WORD, 0xD4, 0xD6, 0xD8, 0x8F, 0xC0, 0xC2 },
+      {   VALID, WORD, 0xD4, 0xD6, 0xD8, 0x8B, 0xC4, 0xC6 },
+      {   VALID, WORD, 0xD4, 0xD6, 0xD8, 0x89, 0xC8, 0xCA },
+      {   VALID, WORD, 0xD4, 0xD6, 0xD8, 0x8A, 0xCC, 0xCE },
+   };
+
+int DMA_ErrorCode = DMA_Ok;
+
+#define DMA_SetErrorCode( status ) \
+   DMA_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *DMA_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case DMA_Error :
+         ErrorString = DMA_ErrorString( DMA_ErrorCode );
+         break;
+
+      case DMA_Ok :
+         ErrorString = "DMA channel ok.";
+         break;
+
+      case DMA_ChannelOutOfRange :
+         ErrorString = "DMA channel out of valid range.";
+         break;
+
+      case DMA_InvalidChannel :
+         ErrorString = "Unsupported DMA channel.";
+         break;
+
+      default :
+         ErrorString = "Unknown DMA error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_VerifyChannel
+
+   Verifies whether a DMA channel is available to transfer data.
+---------------------------------------------------------------------*/
+
+int DMA_VerifyChannel
+   (
+   int channel
+   )
+
+   {
+   int      status;
+   int      Error;
+
+   status = DMA_Ok;
+   Error  = DMA_Ok;
+
+   if ( ( channel < 0 ) || ( DMA_MaxChannel < channel ) )
+      {
+      Error = DMA_ChannelOutOfRange;
+      status = DMA_Error;
+      }
+   else if ( DMA_PortInfo[ channel ].Valid == INVALID )
+      {
+      Error = DMA_InvalidChannel;
+      status = DMA_Error;
+      }
+
+   DMA_SetErrorCode( Error );
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_SetupTransfer
+
+   Programs the specified DMA channel to transfer data.
+---------------------------------------------------------------------*/
+
+int DMA_SetupTransfer
+   (
+   int  channel,
+   char *address,
+   int  length,
+   int  mode
+   )
+
+   {
+   DMA_PORT *Port;
+   int      addr;
+   int      ChannelSelect;
+   int      Page;
+   int      HiByte;
+   int      LoByte;
+   int      TransferLength;
+   int      status;
+
+   status = DMA_VerifyChannel( channel );
+
+   if ( status == DMA_Ok )
+      {
+      Port = &DMA_PortInfo[ channel ];
+      ChannelSelect = channel & 0x3;
+
+      addr = ( int )address;
+
+      if ( Port->Width == WORD )
+         {
+         Page   = ( addr >> 16 ) & 255;
+         HiByte = ( addr >> 9 ) & 255;
+         LoByte = ( addr >> 1 ) & 255;
+
+         // Convert the length in bytes to the length in words
+         TransferLength = ( length + 1 ) >> 1;
+
+         // The length is always one less the number of bytes or words
+         // that we're going to send
+         TransferLength--;
+         }
+      else
+         {
+         Page   = ( addr >> 16 ) & 255;
+         HiByte = ( addr >> 8 ) & 255;
+         LoByte = addr & 255;
+
+         // The length is always one less the number of bytes or words
+         // that we're going to send
+         TransferLength = length - 1;
+         }
+
+      // Mask off DMA channel
+      outp( Port->Mask, 4 | ChannelSelect );
+
+      // Clear flip-flop to lower byte with any data
+      outp( Port->Clear, 0 );
+
+      // Set DMA mode
+      switch( mode )
+         {
+         case DMA_SingleShotRead :
+            outp( Port->Mode, 0x48 | ChannelSelect );
+            break;
+
+         case DMA_SingleShotWrite :
+            outp( Port->Mode, 0x44 | ChannelSelect );
+            break;
+
+         case DMA_AutoInitRead :
+            outp( Port->Mode, 0x58 | ChannelSelect );
+            break;
+
+         case DMA_AutoInitWrite :
+            outp( Port->Mode, 0x54 | ChannelSelect );
+            break;
+         }
+
+      // Send address
+      outp( Port->Address, LoByte );
+      outp( Port->Address, HiByte );
+
+      // Send page
+      outp( Port->Page, Page );
+
+      // Send length
+      outp( Port->Length, TransferLength );
+      outp( Port->Length, TransferLength >> 8 );
+
+      // enable DMA channel
+      outp( Port->Mask, ChannelSelect );
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_EndTransfer
+
+   Ends use of the specified DMA channel.
+---------------------------------------------------------------------*/
+
+int DMA_EndTransfer
+   (
+   int channel
+   )
+
+   {
+   DMA_PORT *Port;
+   int       ChannelSelect;
+   int       status;
+
+   status = DMA_VerifyChannel( channel );
+   if ( status == DMA_Ok )
+      {
+      Port = &DMA_PortInfo[ channel ];
+      ChannelSelect = channel & 0x3;
+
+      // Mask off DMA channel
+      outp( Port->Mask, 4 | ChannelSelect );
+
+      // Clear flip-flop to lower byte with any data
+      outp( Port->Clear, 0 );
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_GetCurrentPos
+
+   Returns the position of the specified DMA transfer.
+---------------------------------------------------------------------*/
+
+char *DMA_GetCurrentPos
+   (
+   int channel
+   )
+
+   {
+   DMA_PORT      *Port;
+   unsigned long addr;
+   int           status;
+
+   addr   = NULL;
+   status = DMA_VerifyChannel( channel );
+
+   if ( status == DMA_Ok )
+      {
+      Port = &DMA_PortInfo[ channel ];
+
+      if ( Port->Width == WORD )
+         {
+         // Get address
+         addr  = inp( Port->Address ) << 1;
+         addr |= inp( Port->Address ) << 9;
+
+         // Get page
+         addr |= inp( Port->Page ) << 16;
+         }
+      else
+         {
+         // Get address
+         addr = inp( Port->Address );
+         addr |= inp( Port->Address ) << 8;
+
+         // Get page
+         addr |= inp( Port->Page ) << 16;
+         }
+      }
+
+   return( ( char * )addr );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DMA_GetTransferCount
+
+   Returns how many bytes are left in the DMA's transfer.
+---------------------------------------------------------------------*/
+
+int DMA_GetTransferCount
+   (
+   int channel
+   )
+
+   {
+   DMA_PORT      *Port;
+   int           count;
+   int           status;
+
+   status = DMA_Ok;
+
+   count = 0;
+
+   if ( ( channel < 0 ) || ( DMA_MaxChannel < channel ) )
+      {
+      status = DMA_ChannelOutOfRange;
+      }
+   else if ( DMA_PortInfo[ channel ].Valid == INVALID )
+      {
+      status = DMA_InvalidChannel;
+      }
+
+   if ( status == DMA_Ok )
+      {
+      Port = &DMA_PortInfo[ channel ];
+
+      outp( Port->Clear, 0 );
+      count  = inp( Port->Length );
+      count += inp( Port->Length ) << 8;
+
+      if ( Port->Width == WORD )
+         {
+         count <<= 1;
+         }
+      }
+
+   DMA_SetErrorCode( status );
+
+   return( count );
+   }
--- /dev/null
+++ b/Game/src/audiolib/dma.h
@@ -1,0 +1,83 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   DMA.H
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Public header file for DMA.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __DMA_H
+#define __DMA_H
+
+enum DMA_ERRORS
+   {
+   DMA_Error = -1,
+   DMA_Ok    = 0,
+   DMA_ChannelOutOfRange,
+   DMA_InvalidChannel
+   };
+
+enum DMA_Modes
+   {
+   DMA_SingleShotRead,
+   DMA_SingleShotWrite,
+   DMA_AutoInitRead,
+   DMA_AutoInitWrite
+   };
+
+char *DMA_ErrorString
+   (
+   int ErrorNumber
+   );
+
+int DMA_VerifyChannel
+   (
+   int channel
+   );
+
+int DMA_SetupTransfer
+   (
+   int  channel,
+   char *address,
+   int  length,
+   int  mode
+   );
+
+int DMA_EndTransfer
+   (
+   int channel
+   );
+
+char *DMA_GetCurrentPos
+   (
+   int channel
+   );
+
+int DMA_GetTransferCount
+   (
+   int channel
+   );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/dpmi.c
@@ -1,0 +1,250 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: DPMI.C
+
+   author: James R. Dose
+   date:   April 8, 1994
+
+   Functions for performing DPMI calls.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <string.h>
+#include "dpmi.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+static union  REGS  Regs;
+static struct SREGS SegRegs;
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_GetRealModeVector
+
+   Returns the vector of a real mode interrupt.
+---------------------------------------------------------------------*/
+
+unsigned long DPMI_GetRealModeVector
+   (
+   int num
+   )
+
+   {
+   unsigned long vector;
+
+   Regs.x.eax = 0x0200;
+   Regs.h.bl  = num;
+   int386( 0x31, &Regs, &Regs );
+
+   vector   = Regs.w.cx & 0xffff;
+   vector <<= 16;
+   vector  |= Regs.w.dx & 0xffff;
+
+   return( vector );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_SetRealModeVector
+
+   Sets the vector of a real mode interrupt.
+---------------------------------------------------------------------*/
+
+void DPMI_SetRealModeVector
+   (
+   int num,
+   unsigned long vector
+   )
+
+   {
+   Regs.x.eax = 0x0201;
+   Regs.h.bl  = num;
+   Regs.w.dx = vector & 0xffff;
+   Regs.w.cx = ( vector >> 16 ) & 0xffff;
+
+   int386( 0x31, &Regs, &Regs );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_CallRealModeFunction
+
+   Performs a call to a real mode function.
+---------------------------------------------------------------------*/
+
+int DPMI_CallRealModeFunction
+   (
+   dpmi_regs *callregs
+   )
+
+   {
+   // Setup our registers to call DPMI
+   Regs.w.ax = 0x0301;
+   Regs.h.bl = 0;
+   Regs.h.bh = 0;
+   Regs.w.cx = 0;
+
+   SegRegs.es = FP_SEG( callregs );
+   Regs.x.edi = FP_OFF( callregs );
+
+   // Call Real-mode procedure with Far Return Frame
+   int386x( 0x31, &Regs, &Regs, &SegRegs );
+
+   if ( Regs.x.cflag )
+      {
+      return( DPMI_Error );
+      }
+
+   return( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_LockMemory
+
+   Locks a region of memory to keep the virtual memory manager from
+   paging the region out.
+---------------------------------------------------------------------*/
+
+int DPMI_LockMemory
+   (
+   void *address,
+   unsigned length
+   )
+
+   {
+   unsigned linear;
+
+   // Thanks to DOS/4GW's zero-based flat memory model, converting
+   // a pointer of any type to a linear address is trivial.
+
+   linear = (unsigned) address;
+
+   // DPMI Lock Linear Region
+   Regs.w.ax = 0x600;
+
+   // Linear address in BX:CX
+   Regs.w.bx = (linear >> 16);
+   Regs.w.cx = (linear & 0xFFFF);
+
+   // Length in SI:DI
+   Regs.w.si = (length >> 16);
+   Regs.w.di = (length & 0xFFFF);
+
+   int386 (0x31, &Regs, &Regs);
+
+   // Return 0 if can't lock
+   if ( Regs.w.cflag )
+      {
+      return( DPMI_Error );
+      }
+
+   return ( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_LockMemoryRegion
+
+   Locks a region of memory to keep the virtual memory manager from
+   paging the region out.
+---------------------------------------------------------------------*/
+
+int DPMI_LockMemoryRegion
+   (
+   void *start,
+   void *end
+   )
+
+   {
+   int status;
+
+   status = DPMI_LockMemory( start, ( char * )end - ( char * )start );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_UnlockMemory
+
+   Unlocks a region of memory that was previously locked.
+---------------------------------------------------------------------*/
+
+int DPMI_UnlockMemory
+   (
+   void *address,
+   unsigned length
+   )
+
+   {
+   unsigned linear;
+
+   // Thanks to DOS/4GW's zero-based flat memory model, converting
+   // a pointer of any type to a linear address is trivial.
+
+   linear = (unsigned) address;
+
+   // DPMI Unlock Linear Region
+   Regs.w.ax = 0x601;
+
+   // Linear address in BX:CX
+   Regs.w.bx = (linear >> 16);
+   Regs.w.cx = (linear & 0xFFFF);
+
+   // Length in SI:DI
+   Regs.w.si = (length >> 16);
+   Regs.w.di = (length & 0xFFFF);
+
+   int386 (0x31, &Regs, &Regs);
+
+   // Return 0 if can't unlock
+   if ( Regs.w.cflag )
+      {
+      return( DPMI_Error );
+      }
+
+   return ( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_UnlockMemoryRegion
+
+   Unlocks a region of memory that was previously locked.
+---------------------------------------------------------------------*/
+
+int DPMI_UnlockMemoryRegion
+   (
+   void *start,
+   void *end
+   )
+
+   {
+   int status;
+
+   status = DPMI_UnlockMemory( start, ( char * )end - ( char * )start );
+
+   return( status );
+   }
--- /dev/null
+++ b/Game/src/audiolib/dpmi.h
@@ -1,0 +1,102 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: DPMI.H
+
+   author: James R. Dose
+   date:   March 31, 1994
+
+   Inline functions for performing DPMI calls.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __DPMI_H
+#define __DPMI_H
+
+enum DPMI_Errors
+   {
+   DPMI_Warning = -2,
+   DPMI_Error   = -1,
+   DPMI_Ok      = 0
+   };
+
+typedef struct
+   {
+   unsigned long  EDI;
+   unsigned long  ESI;
+   unsigned long  EBP;
+   unsigned long  Reserved;
+   unsigned long  EBX;
+   unsigned long  EDX;
+   unsigned long  ECX;
+   unsigned long  EAX;
+   unsigned short Flags;
+   unsigned short ES;
+   unsigned short DS;
+   unsigned short FS;
+   unsigned short GS;
+   unsigned short IP;
+   unsigned short CS;
+   unsigned short SP;
+   unsigned short SS;
+   } dpmi_regs;
+
+unsigned long DPMI_GetRealModeVector( int num );
+void DPMI_SetRealModeVector( int num, unsigned long vector );
+int  DPMI_CallRealModeFunction( dpmi_regs *callregs );
+int  DPMI_GetDOSMemory( void **ptr, int *descriptor, unsigned length );
+int  DPMI_FreeDOSMemory( int descriptor );
+int  DPMI_LockMemory( void *address, unsigned length );
+int  DPMI_LockMemoryRegion( void *start, void *end );
+int  DPMI_UnlockMemory( void *address, unsigned length );
+int  DPMI_UnlockMemoryRegion( void *start, void *end );
+
+#define DPMI_Lock( variable ) \
+   ( DPMI_LockMemory( &( variable ), sizeof( variable ) ) )
+
+#define DPMI_Unlock( variable ) \
+   ( DPMI_UnlockMemory( &( variable ), sizeof( variable ) ) )
+
+#ifdef PLAT_DOS
+#pragma aux DPMI_GetDOSMemory = \
+   "mov    eax, 0100h",         \
+   "add    ebx, 15",            \
+   "shr    ebx, 4",             \
+   "int    31h",                \
+   "jc     DPMI_Exit",          \
+   "movzx  eax, ax",            \
+   "shl    eax, 4",             \
+   "mov    [ esi ], eax",       \
+   "mov    [ edi ], edx",       \
+   "sub    eax, eax",           \
+   "DPMI_Exit:",                \
+   parm [ esi ] [ edi ] [ ebx ] modify exact [ eax ebx edx ];
+
+#pragma aux DPMI_FreeDOSMemory = \
+   "mov    eax, 0101h",          \
+   "int    31h",                 \
+   "jc     DPMI_Exit",           \
+   "sub    eax, eax",            \
+   "DPMI_Exit:",                 \
+   parm [ edx ] modify exact [ eax ];
+#endif
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/dsl.c
@@ -1,0 +1,232 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "dsl.h"
+#include "util.h"
+
+#include "SDL.h"
+#include "SDL_mixer.h"
+
+extern volatile int MV_MixPage;
+
+static int DSL_ErrorCode = DSL_Ok;
+
+static int mixer_initialized;
+
+static void ( *_CallBackFunc )( void );
+static volatile char *_BufferStart;
+static int _BufferSize;
+static int _NumDivisions;
+static int _SampleRate;
+static int _remainder;
+
+static Mix_Chunk *blank;
+static unsigned char *blank_buf;
+
+/*
+possible todo ideas: cache sdl/sdl mixer error messages.
+*/
+
+char *DSL_ErrorString( int ErrorNumber )
+{
+	char *ErrorString;
+	
+	switch (ErrorNumber) {
+		case DSL_Warning:
+		case DSL_Error:
+			ErrorString = DSL_ErrorString(DSL_ErrorCode);
+			break;
+		
+		case DSL_Ok:
+			ErrorString = "SDL Driver ok.";
+			break;
+		
+		case DSL_SDLInitFailure:
+			ErrorString = "SDL Audio initialization failed.";
+			break;
+		
+		case DSL_MixerActive:
+			ErrorString = "SDL Mixer already initialized.";
+			break;	
+	
+		case DSL_MixerInitFailure:
+			ErrorString = "SDL Mixer initialization failed.";
+			break;
+			
+		default:
+			ErrorString = "Unknown SDL Driver error.";
+			break;
+	}
+	
+	return ErrorString;
+}
+
+static void DSL_SetErrorCode(int ErrorCode)
+{
+	DSL_ErrorCode = ErrorCode;
+}
+
+int DSL_Init( void )
+{
+	DSL_SetErrorCode(DSL_Ok);
+	
+	if (SDL_InitSubSystem(SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE) < 0) {
+		DSL_SetErrorCode(DSL_SDLInitFailure);
+		
+		return DSL_Error;
+	}
+	
+	return DSL_Ok;
+}
+
+void DSL_Shutdown( void )
+{
+	DSL_StopPlayback();
+}
+
+static void mixer_callback(int chan, void *stream, int len, void *udata)
+{
+	Uint8 *stptr;
+	Uint8 *fxptr;
+	int copysize;
+	
+	/* len should equal _BufferSize, else this is screwed up */
+
+	stptr = (Uint8 *)stream;
+	
+	if (_remainder > 0) {
+		copysize = min(len, _remainder);
+		
+		fxptr = (Uint8 *)(&_BufferStart[MV_MixPage * 
+			_BufferSize]);
+		
+		memcpy(stptr, fxptr+(_BufferSize-_remainder), copysize);
+		
+		len -= copysize;
+		_remainder -= copysize;
+		
+		stptr += copysize;
+	}
+	
+	while (len > 0) {
+		/* new buffer */
+		
+		_CallBackFunc();
+		
+		fxptr = (Uint8 *)(&_BufferStart[MV_MixPage * 
+			_BufferSize]);
+
+		copysize = min(len, _BufferSize);
+		
+		memcpy(stptr, fxptr, copysize);
+		
+		len -= copysize;
+		
+		stptr += copysize;
+	}
+	
+	_remainder = len;
+}
+
+int   DSL_BeginBufferedPlayback( char *BufferStart,
+      int BufferSize, int NumDivisions, unsigned SampleRate,
+      int MixMode, void ( *CallBackFunc )( void ) )
+{
+	Uint16 format;
+	Uint8 *tmp;
+	int channels;
+	int chunksize;
+	int blah;
+		
+	if (mixer_initialized) {
+		DSL_SetErrorCode(DSL_MixerActive);
+		
+		return DSL_Error;
+	}
+	
+	_CallBackFunc = CallBackFunc;
+	_BufferStart = BufferStart;
+	_BufferSize = (BufferSize / NumDivisions);
+	_NumDivisions = NumDivisions;
+	_SampleRate = SampleRate;
+
+	_remainder = 0;
+	
+	format = (MixMode & SIXTEEN_BIT) ? AUDIO_S16LSB : AUDIO_U8;
+	channels = (MixMode & STEREO) ? 2 : 1;
+
+ /*
+	I find 50ms to be ideal, at least with my hardware. This clamping mechanism
+	was added because it seems the above remainder handling isn't so nice --kode54
+ */
+	chunksize = (5 * SampleRate) / 100;
+
+	blah = _BufferSize;
+	if (MixMode & SIXTEEN_BIT) blah >>= 1;
+	if (MixMode & STEREO) blah >>= 1;
+
+	if (chunksize % blah) chunksize += blah - (chunksize % blah);
+
+	if (Mix_OpenAudio(SampleRate, format, channels, chunksize) < 0) {
+		DSL_SetErrorCode(DSL_MixerInitFailure);
+		
+		return DSL_Error;
+	}
+
+/*
+	Mix_SetPostMix(mixer_callback, NULL);
+*/
+	/* have to use a channel because postmix will overwrite the music... */
+	Mix_RegisterEffect(0, mixer_callback, NULL, NULL);
+	
+	/* create a dummy sample just to allocate that channel */
+	blank_buf = (Uint8 *)malloc(4096);
+	memset(blank_buf, 0, 4096);
+	
+	blank = Mix_QuickLoad_RAW(blank_buf, 4096);
+		
+	Mix_PlayChannel(0, blank, -1);
+	
+	mixer_initialized = 1;
+	
+	return DSL_Ok;
+}
+
+void DSL_StopPlayback( void )
+{
+	if (mixer_initialized) {
+		Mix_HaltChannel(0);
+	}
+	
+	if (blank != NULL) {
+		Mix_FreeChunk(blank);
+	}
+	
+	blank = NULL;
+	
+	if (blank_buf  != NULL) {
+		free(blank_buf);
+	}
+	
+	blank_buf = NULL;
+	
+	if (mixer_initialized) {
+		Mix_CloseAudio();
+	}
+	
+	mixer_initialized = 0;
+}
+
+unsigned DSL_GetPlaybackRate( void )
+{
+	return _SampleRate;
+}
+
+unsigned long DisableInterrupts( void )
+{
+	return 0;
+}
+
+void RestoreInterrupts( unsigned long flags )
+{
+}
--- /dev/null
+++ b/Game/src/audiolib/dsl.h
@@ -1,0 +1,28 @@
+#ifndef AUDIOLIB__DSL_H
+#define AUDIOLIB__DSL_H
+
+#define MONO_8BIT    0
+#define STEREO      1
+#define SIXTEEN_BIT 2
+#define STEREO_16BIT ( STEREO | SIXTEEN_BIT )
+
+enum DSL_ERRORS
+   {
+   DSL_Warning = -2,
+   DSL_Error = -1,
+   DSL_Ok = 0,
+   DSL_SDLInitFailure,
+   DSL_MixerActive,
+   DSL_MixerInitFailure
+   };
+
+char *DSL_ErrorString( int ErrorNumber );
+int   DSL_Init( void );
+void  DSL_StopPlayback( void );
+unsigned DSL_GetPlaybackRate( void );
+int   DSL_BeginBufferedPlayback( char *BufferStart,
+      int BufferSize, int NumDivisions, unsigned SampleRate,
+      int MixMode, void ( *CallBackFunc )( void ) );
+void  DSL_Shutdown( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/fx_man.c
@@ -1,0 +1,1376 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: FX_MAN.C
+
+   author: James R. Dose
+   date:   March 17, 1994
+
+   Device independant sound effect routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "sndcards.h"
+#include "multivoc.h"
+
+#ifdef PLAT_DOS
+#include "blaster.h"
+#include "pas16.h"
+#include "sndscape.h"
+#include "guswave.h"
+#include "sndsrc.h"
+#else
+#include "dsl.h"
+#endif
+
+#include "ll_man.h"
+#include "user.h"
+#include "fx_man.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+static unsigned FX_MixRate;
+
+int FX_SoundDevice = -1;
+int FX_ErrorCode = FX_Ok;
+int FX_Installed = FALSE;
+
+#define FX_SetErrorCode( status ) \
+   FX_ErrorCode = ( status );
+
+/*---------------------------------------------------------------------
+   Function: FX_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *FX_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case FX_Warning :
+      case FX_Error :
+         ErrorString = FX_ErrorString( FX_ErrorCode );
+         break;
+
+      case FX_Ok :
+         ErrorString = "Fx ok\n";
+         break;
+
+      case FX_ASSVersion :
+         ErrorString = "Apogee Sound System Version " ASS_VERSION_STRING "  "
+            "Programmed by Jim Dose\n"
+            "(c) Copyright 1995 James R. Dose.  All Rights Reserved.\n";
+         break;
+
+#ifdef PLAT_DOS
+      case FX_BlasterError :
+         ErrorString = BLASTER_ErrorString( BLASTER_Error );
+         break;
+#endif
+
+      case FX_SoundCardError :
+#ifdef PLAT_DOS
+         switch( FX_SoundDevice )
+         {
+            case SoundBlaster :
+            case Awe32 :
+               ErrorString = BLASTER_ErrorString( BLASTER_Error );
+               break;
+
+            case ProAudioSpectrum :
+            case SoundMan16 :
+               ErrorString = PAS_ErrorString( PAS_Error );
+               break;
+
+            case SoundScape :
+               ErrorString = SOUNDSCAPE_ErrorString( SOUNDSCAPE_Error );
+               break;
+
+            case UltraSound :
+               ErrorString = GUSWAVE_ErrorString( GUSWAVE_Error );
+               break;
+
+            case SoundSource :
+            case TandySoundSource :
+               ErrorString = SS_ErrorString( SS_Error );
+               break;
+            }
+#else
+         ErrorString = DSL_ErrorString( DSL_Error );
+#endif
+         break;
+
+      case FX_InvalidCard :
+         ErrorString = "Invalid Sound Fx device.\n";
+         break;
+
+      case FX_MultiVocError :
+         ErrorString = MV_ErrorString( MV_Error );
+         break;
+
+      case FX_DPMI_Error :
+         ErrorString = "DPMI Error in FX_MAN.\n";
+         break;
+
+      default :
+         ErrorString = "Unknown Fx error code.\n";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetupCard
+
+   Sets the configuration of a sound device.
+---------------------------------------------------------------------*/
+
+int FX_SetupCard
+   (
+   int SoundCard,
+   fx_device *device
+   )
+
+   {
+   int status;
+   int DeviceStatus;
+
+   if ( USER_CheckParameter( "ASSVER" ) )
+      {
+      FX_SetErrorCode( FX_ASSVersion );
+      return( FX_Error );
+      }
+
+   FX_SoundDevice = SoundCard;
+
+   status = FX_Ok;
+   FX_SetErrorCode( FX_Ok );
+
+#ifdef PLAT_DOS
+   switch( SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         DeviceStatus = BLASTER_Init();
+         if ( DeviceStatus != BLASTER_Ok )
+            {
+            FX_SetErrorCode( FX_SoundCardError );
+            status = FX_Error;
+            break;
+            }
+
+         device->MaxVoices = 32;
+         BLASTER_GetCardInfo( &device->MaxSampleBits, &device->MaxChannels );
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         DeviceStatus = PAS_Init();
+         if ( DeviceStatus != PAS_Ok )
+            {
+            FX_SetErrorCode( FX_SoundCardError );
+            status = FX_Error;
+            break;
+            }
+
+         device->MaxVoices = 32;
+         PAS_GetCardInfo( &device->MaxSampleBits, &device->MaxChannels );
+         break;
+
+      case GenMidi :
+      case SoundCanvas :
+      case WaveBlaster :
+         device->MaxVoices     = 0;
+         device->MaxSampleBits = 0;
+         device->MaxChannels   = 0;
+         break;
+
+      case SoundScape :
+         device->MaxVoices = 32;
+         DeviceStatus = SOUNDSCAPE_GetCardInfo( &device->MaxSampleBits,
+            &device->MaxChannels );
+         if ( DeviceStatus != SOUNDSCAPE_Ok )
+            {
+            FX_SetErrorCode( FX_SoundCardError );
+            status = FX_Error;
+            }
+         break;
+
+      case UltraSound :
+         if ( GUSWAVE_Init( 8 ) != GUSWAVE_Ok )
+            {
+            FX_SetErrorCode( FX_SoundCardError );
+            status = FX_Error;
+            break;
+            }
+
+         device->MaxVoices     = 8;
+         device->MaxSampleBits = 0;
+         device->MaxChannels   = 0;
+         break;
+
+      case SoundSource :
+      case TandySoundSource :
+         DeviceStatus = SS_Init( SoundCard );
+         if ( DeviceStatus != SS_Ok )
+            {
+            FX_SetErrorCode( FX_SoundCardError );
+            status = FX_Error;
+            break;
+            }
+         SS_Shutdown();
+         device->MaxVoices     = 32;
+         device->MaxSampleBits = 8;
+         device->MaxChannels   = 1;
+         break;
+      default :
+         FX_SetErrorCode( FX_InvalidCard );
+         status = FX_Error;
+      }
+#else
+      DeviceStatus = DSL_Init();
+      if ( DeviceStatus != DSL_Ok )
+         {
+         FX_SetErrorCode( FX_SoundCardError );
+         status = FX_Error;
+         }
+         else
+         {
+         device->MaxVoices     = 32;
+         device->MaxSampleBits = 0;
+         device->MaxChannels   = 0;
+         }
+#endif
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_GetBlasterSettings
+
+   Returns the current BLASTER environment variable settings.
+---------------------------------------------------------------------*/
+
+int FX_GetBlasterSettings
+   (
+   fx_blaster_config *blaster
+   )
+
+   {
+#ifdef PLAT_DOS
+   int status;
+   BLASTER_CONFIG Blaster;
+
+   FX_SetErrorCode( FX_Ok );
+
+   status = BLASTER_GetEnv( &Blaster );
+   if ( status != BLASTER_Ok )
+      {
+      FX_SetErrorCode( FX_BlasterError );
+      return( FX_Error );
+      }
+
+   blaster->Type      = Blaster.Type;
+   blaster->Address   = Blaster.Address;
+   blaster->Interrupt = Blaster.Interrupt;
+   blaster->Dma8      = Blaster.Dma8;
+   blaster->Dma16     = Blaster.Dma16;
+   blaster->Midi      = Blaster.Midi;
+   blaster->Emu       = Blaster.Emu;
+#endif
+
+   return( FX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetupSoundBlaster
+
+   Handles manual setup of the Sound Blaster information.
+---------------------------------------------------------------------*/
+
+int FX_SetupSoundBlaster
+   (
+   fx_blaster_config blaster,
+   int *MaxVoices,
+   int *MaxSampleBits,
+   int *MaxChannels
+   )
+
+   {
+#ifdef PLAT_DOS
+   int DeviceStatus;
+   BLASTER_CONFIG Blaster;
+
+   FX_SetErrorCode( FX_Ok );
+
+   FX_SoundDevice = SoundBlaster;
+
+   Blaster.Type      = blaster.Type;
+   Blaster.Address   = blaster.Address;
+   Blaster.Interrupt = blaster.Interrupt;
+   Blaster.Dma8      = blaster.Dma8;
+   Blaster.Dma16     = blaster.Dma16;
+   Blaster.Midi      = blaster.Midi;
+   Blaster.Emu       = blaster.Emu;
+
+   BLASTER_SetCardSettings( Blaster );
+
+   DeviceStatus = BLASTER_Init();
+   if ( DeviceStatus != BLASTER_Ok )
+      {
+      FX_SetErrorCode( FX_SoundCardError );
+      return( FX_Error );
+      }
+
+   *MaxVoices = 8;
+   BLASTER_GetCardInfo( MaxSampleBits, MaxChannels );
+#endif
+
+   return( FX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_Init
+
+   Selects which sound device to use.
+---------------------------------------------------------------------*/
+
+int FX_Init
+   (
+   int SoundCard,
+   int numvoices,
+   int numchannels,
+   int samplebits,
+   unsigned mixrate
+   )
+
+   {
+   int status;
+   int devicestatus;
+
+   if ( FX_Installed )
+      {
+      FX_Shutdown();
+      }
+
+   if ( USER_CheckParameter( "ASSVER" ) )
+      {
+      FX_SetErrorCode( FX_ASSVersion );
+      return( FX_Error );
+      }
+
+   status = LL_LockMemory();
+   if ( status != LL_Ok )
+      {
+      FX_SetErrorCode( FX_DPMI_Error );
+      return( FX_Error );
+      }
+
+   FX_MixRate = mixrate;
+
+   status = FX_Ok;
+   FX_SoundDevice = SoundCard;
+   switch( SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+      case SoundScape :
+      case SoundSource :
+      case TandySoundSource :
+      case UltraSound :
+         devicestatus = MV_Init( SoundCard, FX_MixRate, numvoices,
+            numchannels, samplebits );
+         if ( devicestatus != MV_Ok )
+            {
+            FX_SetErrorCode( FX_MultiVocError );
+            status = FX_Error;
+            }
+         break;
+
+      default :
+         FX_SetErrorCode( FX_InvalidCard );
+         status = FX_Error;
+      }
+
+   if ( status != FX_Ok )
+      {
+      LL_UnlockMemory();
+      }
+   else
+      {
+      FX_Installed = TRUE;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_Shutdown
+
+   Terminates use of sound device.
+---------------------------------------------------------------------*/
+
+int FX_Shutdown
+   (
+   void
+   )
+
+   {
+   int status;
+
+   if ( !FX_Installed )
+      {
+      return( FX_Ok );
+      }
+
+   status = FX_Ok;
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+      case SoundScape :
+      case SoundSource :
+      case TandySoundSource :
+      case UltraSound :
+         status = MV_Shutdown();
+         if ( status != MV_Ok )
+            {
+            FX_SetErrorCode( FX_MultiVocError );
+            status = FX_Error;
+            }
+         break;
+
+      default :
+         FX_SetErrorCode( FX_InvalidCard );
+         status = FX_Error;
+      }
+
+   FX_Installed = FALSE;
+   LL_UnlockMemory();
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetCallback
+
+   Sets the function to call when a voice is done.
+---------------------------------------------------------------------*/
+
+int FX_SetCallBack
+   (
+   void ( *function )( unsigned long )
+   )
+
+   {
+   int status;
+
+   status = FX_Ok;
+
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+      case SoundScape :
+      case SoundSource :
+      case TandySoundSource :
+      case UltraSound :
+         MV_SetCallBack( function );
+         break;
+
+      default :
+         FX_SetErrorCode( FX_InvalidCard );
+         status = FX_Error;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetVolume
+
+   Sets the volume of the current sound device.
+---------------------------------------------------------------------*/
+
+void FX_SetVolume
+   (
+   int volume
+   )
+
+   {
+   int status;
+
+#ifdef PLAT_DOS
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         if ( BLASTER_CardHasMixer() )
+            {
+            BLASTER_SetVoiceVolume( volume );
+            }
+         else
+            {
+            MV_SetVolume( volume );
+            }
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         status = PAS_SetPCMVolume( volume );
+         if ( status != PAS_Ok )
+            {
+            MV_SetVolume( volume );
+            }
+         break;
+
+      case GenMidi :
+      case SoundCanvas :
+      case WaveBlaster :
+         break;
+
+      case SoundScape :
+         MV_SetVolume( volume );
+         break;
+
+      case UltraSound :
+         GUSWAVE_SetVolume( volume );
+         break;
+
+      case SoundSource :
+      case TandySoundSource :
+         MV_SetVolume( volume );
+         break;
+      }
+#else
+   MV_SetVolume( volume );
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_GetVolume
+
+   Returns the volume of the current sound device.
+---------------------------------------------------------------------*/
+
+int FX_GetVolume
+   (
+   void
+   )
+
+   {
+   int volume;
+
+#ifdef PLAT_DOS
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         if ( BLASTER_CardHasMixer() )
+            {
+            volume = BLASTER_GetVoiceVolume();
+            }
+         else
+            {
+            volume = MV_GetVolume();
+            }
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         volume = PAS_GetPCMVolume();
+         if ( volume == PAS_Error )
+            {
+            volume = MV_GetVolume();
+            }
+         break;
+
+      case GenMidi :
+      case SoundCanvas :
+      case WaveBlaster :
+         volume = 255;
+         break;
+
+      case SoundScape :
+         volume = MV_GetVolume();
+         break;
+
+      case UltraSound :
+         volume = GUSWAVE_GetVolume();
+         break;
+
+      case SoundSource :
+      case TandySoundSource :
+         volume = MV_GetVolume();
+         break;
+
+      default :
+         volume = 0;
+      }
+#else
+   volume = MV_GetVolume();
+#endif
+
+   return( volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetReverseStereo
+
+   Set the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+void FX_SetReverseStereo
+   (
+   int setting
+   )
+
+   {
+   MV_SetReverseStereo( setting );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_GetReverseStereo
+
+   Returns the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+int FX_GetReverseStereo
+   (
+   void
+   )
+
+   {
+   return MV_GetReverseStereo();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetReverb
+
+   Sets the reverb level.
+---------------------------------------------------------------------*/
+
+void FX_SetReverb
+   (
+   int reverb
+   )
+
+   {
+   MV_SetReverb( reverb );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetFastReverb
+
+   Sets the reverb level.
+---------------------------------------------------------------------*/
+
+void FX_SetFastReverb
+   (
+   int reverb
+   )
+
+   {
+   MV_SetFastReverb( reverb );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_GetMaxReverbDelay
+
+   Returns the maximum delay time for reverb.
+---------------------------------------------------------------------*/
+
+int FX_GetMaxReverbDelay
+   (
+   void
+   )
+
+   {
+   return MV_GetMaxReverbDelay();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_GetReverbDelay
+
+   Returns the current delay time for reverb.
+---------------------------------------------------------------------*/
+
+int FX_GetReverbDelay
+   (
+   void
+   )
+
+   {
+   return MV_GetReverbDelay();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetReverbDelay
+
+   Sets the delay level of reverb to add to mix.
+---------------------------------------------------------------------*/
+
+void FX_SetReverbDelay
+   (
+   int delay
+   )
+
+   {
+   MV_SetReverbDelay( delay );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_VoiceAvailable
+
+   Checks if a voice can be play at the specified priority.
+---------------------------------------------------------------------*/
+
+int FX_VoiceAvailable
+   (
+   int priority
+   )
+
+   {
+   return MV_VoiceAvailable( priority );
+   }
+
+/*---------------------------------------------------------------------
+   Function: FX_EndLooping
+
+   Stops the voice associated with the specified handle from looping
+   without stoping the sound.
+---------------------------------------------------------------------*/
+
+int FX_EndLooping
+   (
+   int handle
+   )
+
+   {
+   int status;
+
+   status = MV_EndLooping( handle );
+   if ( status == MV_Error )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      status = FX_Warning;
+      }
+
+   return( status );
+   }
+
+/*---------------------------------------------------------------------
+   Function: FX_SetPan
+
+   Sets the stereo and mono volume level of the voice associated
+   with the specified handle.
+---------------------------------------------------------------------*/
+
+int FX_SetPan
+   (
+   int handle,
+   int vol,
+   int left,
+   int right
+   )
+
+   {
+   int status;
+
+   status = MV_SetPan( handle, vol, left, right );
+   if ( status == MV_Error )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      status = FX_Warning;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetPitch
+
+   Sets the pitch of the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int FX_SetPitch
+   (
+   int handle,
+   int pitchoffset
+   )
+
+   {
+   int status;
+
+   status = MV_SetPitch( handle, pitchoffset );
+   if ( status == MV_Error )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      status = FX_Warning;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SetFrequency
+
+   Sets the frequency of the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int FX_SetFrequency
+   (
+   int handle,
+   int frequency
+   )
+
+   {
+   int status;
+
+   status = MV_SetFrequency( handle, frequency );
+   if ( status == MV_Error )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      status = FX_Warning;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayVOC
+
+   Begin playback of sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayVOC
+   (
+   char *ptr,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayVOC( ptr, pitchoffset, vol, left, right,
+      priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayLoopedVOC
+
+   Begin playback of sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayLoopedVOC
+   (
+   char *ptr,
+   long loopstart,
+   long loopend,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayLoopedVOC( ptr, loopstart, loopend, pitchoffset,
+      vol, left, right, priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayWAV
+
+   Begin playback of sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayWAV
+   (
+   char *ptr,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayWAV( ptr, pitchoffset, vol, left, right,
+      priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayWAV
+
+   Begin playback of sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayLoopedWAV
+   (
+   char *ptr,
+   long loopstart,
+   long loopend,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayLoopedWAV( ptr, loopstart, loopend,
+      pitchoffset, vol, left, right, priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayVOC3D
+
+   Begin playback of sound data at specified angle and distance
+   from listener.
+---------------------------------------------------------------------*/
+
+int FX_PlayVOC3D
+   (
+   char *ptr,
+   int pitchoffset,
+   int angle,
+   int distance,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayVOC3D( ptr, pitchoffset, angle, distance,
+      priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayWAV3D
+
+   Begin playback of sound data at specified angle and distance
+   from listener.
+---------------------------------------------------------------------*/
+
+int FX_PlayWAV3D
+   (
+   char *ptr,
+   int pitchoffset,
+   int angle,
+   int distance,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayWAV3D( ptr, pitchoffset, angle, distance,
+      priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayRaw
+
+   Begin playback of raw sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayRaw
+   (
+   char *ptr,
+   unsigned long length,
+   unsigned rate,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayRaw( ptr, length, rate, pitchoffset,
+      vol, left, right, priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_PlayLoopedRaw
+
+   Begin playback of raw sound data with the given volume and priority.
+---------------------------------------------------------------------*/
+
+int FX_PlayLoopedRaw
+   (
+   char *ptr,
+   unsigned long length,
+   char *loopstart,
+   char *loopend,
+   unsigned rate,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_PlayLoopedRaw( ptr, length, loopstart, loopend,
+      rate, pitchoffset, vol, left, right, priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_Pan3D
+
+   Set the angle and distance from the listener of the voice associated
+   with the specified handle.
+---------------------------------------------------------------------*/
+
+int FX_Pan3D
+   (
+   int handle,
+   int angle,
+   int distance
+   )
+
+   {
+   int status;
+
+   status = MV_Pan3D( handle, angle, distance );
+   if ( status != MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      status = FX_Warning;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SoundActive
+
+   Tests if the specified sound is currently playing.
+---------------------------------------------------------------------*/
+
+int FX_SoundActive
+   (
+   int handle
+   )
+
+   {
+   return( MV_VoicePlaying( handle ) );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_SoundsPlaying
+
+   Reports the number of voices playing.
+---------------------------------------------------------------------*/
+
+int FX_SoundsPlaying
+   (
+   void
+   )
+
+   {
+   return( MV_VoicesPlaying() );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_StopSound
+
+   Halts playback of a specific voice
+---------------------------------------------------------------------*/
+
+int FX_StopSound
+   (
+   int handle
+   )
+
+   {
+   int status;
+
+   status = MV_Kill( handle );
+   if ( status != MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      return( FX_Warning );
+      }
+
+   return( FX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_StopAllSounds
+
+   Halts playback of all sounds.
+---------------------------------------------------------------------*/
+
+int FX_StopAllSounds
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status = MV_KillAllVoices();
+   if ( status != MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      return( FX_Warning );
+      }
+
+   return( FX_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_StartDemandFeedPlayback
+
+   Plays a digitized sound from a user controlled buffering system.
+---------------------------------------------------------------------*/
+
+int FX_StartDemandFeedPlayback
+   (
+   void ( *function )( char **ptr, unsigned long *length ),
+   int rate,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+
+   handle = MV_StartDemandFeedPlayback( function, rate,
+      pitchoffset, vol, left, right, priority, callbackval );
+   if ( handle < MV_Ok )
+      {
+      FX_SetErrorCode( FX_MultiVocError );
+      handle = FX_Warning;
+      }
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_StartRecording
+
+   Starts the sound recording engine.
+---------------------------------------------------------------------*/
+
+int FX_StartRecording
+   (
+   int MixRate,
+   void ( *function )( char *ptr, int length )
+   )
+
+   {
+   int status;
+
+#ifdef PLAT_DOS
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         status = MV_StartRecording( MixRate, function );
+         if ( status != MV_Ok )
+            {
+            FX_SetErrorCode( FX_MultiVocError );
+            status = FX_Warning;
+            }
+         else
+            {
+            status = FX_Ok;
+            }
+         break;
+
+      default :
+         FX_SetErrorCode( FX_InvalidCard );
+         status = FX_Warning;
+         break;
+      }
+#else
+   FX_SetErrorCode( FX_InvalidCard );
+   status = FX_Warning;
+#endif
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: FX_StopRecord
+
+   Stops the sound record engine.
+---------------------------------------------------------------------*/
+
+void FX_StopRecord
+   (
+   void
+   )
+
+   {
+#ifdef PLAT_DOS
+   // Stop sound playback
+   switch( FX_SoundDevice )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         MV_StopRecord();
+         break;
+      }
+#endif
+   }
--- /dev/null
+++ b/Game/src/audiolib/fx_man.h
@@ -1,0 +1,135 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: FX_MAN.H
+
+   author: James R. Dose
+   date:   March 17, 1994
+
+   Public header for FX_MAN.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __FX_MAN_H
+#define __FX_MAN_H
+
+#include "sndcards.h"
+
+typedef struct
+   {
+   int MaxVoices;
+   int MaxSampleBits;
+   int MaxChannels;
+   } fx_device;
+
+#define MonoFx   1
+#define StereoFx 2
+
+typedef struct
+   {
+   unsigned long Address;
+   unsigned long Type;
+   unsigned long Interrupt;
+   unsigned long Dma8;
+   unsigned long Dma16;
+   unsigned long Midi;
+   unsigned long Emu;
+   } fx_blaster_config;
+
+enum FX_ERRORS
+   {
+   FX_Warning = -2,
+   FX_Error = -1,
+   FX_Ok = 0,
+   FX_ASSVersion,
+   FX_BlasterError,
+   FX_SoundCardError,
+   FX_InvalidCard,
+   FX_MultiVocError,
+   FX_DPMI_Error
+   };
+
+enum fx_BLASTER_Types
+   {
+   fx_SB     = 1,
+   fx_SBPro  = 2,
+   fx_SB20   = 3,
+   fx_SBPro2 = 4,
+   fx_SB16   = 6
+   };
+
+
+char *FX_ErrorString( int ErrorNumber );
+int   FX_SetupCard( int SoundCard, fx_device *device );
+int   FX_GetBlasterSettings( fx_blaster_config *blaster );
+int   FX_SetupSoundBlaster( fx_blaster_config blaster, int *MaxVoices, int *MaxSampleBits, int *MaxChannels );
+int   FX_Init( int SoundCard, int numvoices, int numchannels, int samplebits, unsigned mixrate );
+int   FX_Shutdown( void );
+int   FX_SetCallBack( void ( *function )( unsigned long ) );
+void  FX_SetVolume( int volume );
+int   FX_GetVolume( void );
+
+void  FX_SetReverseStereo( int setting );
+int   FX_GetReverseStereo( void );
+void  FX_SetReverb( int reverb );
+void  FX_SetFastReverb( int reverb );
+int   FX_GetMaxReverbDelay( void );
+int   FX_GetReverbDelay( void );
+void  FX_SetReverbDelay( int delay );
+
+int FX_VoiceAvailable( int priority );
+int FX_EndLooping( int handle );
+int FX_SetPan( int handle, int vol, int left, int right );
+int FX_SetPitch( int handle, int pitchoffset );
+int FX_SetFrequency( int handle, int frequency );
+
+int FX_PlayVOC( char *ptr, int pitchoffset, int vol, int left, int right,
+       int priority, unsigned long callbackval );
+int FX_PlayLoopedVOC( char *ptr, long loopstart, long loopend,
+       int pitchoffset, int vol, int left, int right, int priority,
+       unsigned long callbackval );
+int FX_PlayWAV( char *ptr, int pitchoffset, int vol, int left, int right,
+       int priority, unsigned long callbackval );
+int FX_PlayLoopedWAV( char *ptr, long loopstart, long loopend,
+       int pitchoffset, int vol, int left, int right, int priority,
+       unsigned long callbackval );
+int FX_PlayVOC3D( char *ptr, int pitchoffset, int angle, int distance,
+       int priority, unsigned long callbackval );
+int FX_PlayWAV3D( char *ptr, int pitchoffset, int angle, int distance,
+       int priority, unsigned long callbackval );
+int FX_PlayRaw( char *ptr, unsigned long length, unsigned rate,
+       int pitchoffset, int vol, int left, int right, int priority,
+       unsigned long callbackval );
+int FX_PlayLoopedRaw( char *ptr, unsigned long length, char *loopstart,
+       char *loopend, unsigned rate, int pitchoffset, int vol, int left,
+       int right, int priority, unsigned long callbackval );
+int FX_Pan3D( int handle, int angle, int distance );
+int FX_SoundActive( int handle );
+int FX_SoundsPlaying( void );
+int FX_StopSound( int handle );
+int FX_StopAllSounds( void );
+int FX_StartDemandFeedPlayback( void ( *function )( char **ptr, unsigned long *length ),
+       int rate, int pitchoffset, int vol, int left, int right,
+       int priority, unsigned long callbackval );
+int  FX_StartRecording( int MixRate, void ( *function )( char *ptr, int length ) );
+void FX_StopRecord( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/gmtimbre.c
@@ -1,0 +1,290 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+typedef struct
+   {
+   unsigned char SAVEK[ 2 ];
+   unsigned char Level[ 2 ];
+   unsigned char Env1[ 2 ];
+   unsigned char Env2[ 2 ];
+   unsigned char Wave[ 2 ];
+   unsigned char Feedback;
+   signed   char Transpose;
+   signed   char Velocity;
+   } TIMBRE;
+
+TIMBRE ADLIB_TimbreBank[ 256 ] =
+   {
+      { { 33, 33 }, { 143, 6 }, { 242, 242 }, { 69, 118 }, { 0, 0 }, 8, 0 },
+      { { 49, 33 }, { 75, 0 }, { 242, 242 }, { 84, 86 }, { 0, 0 }, 8, 0 },
+      { { 49, 33 }, { 73, 0 }, { 242, 242 }, { 85, 118 }, { 0, 0 }, 8, 0 },
+      { { 177, 97 }, { 14, 0 }, { 242, 243 }, { 59, 11 }, { 0, 0 }, 6, 0 },
+      { { 1, 33 }, { 87, 0 }, { 241, 241 }, { 56, 40 }, { 0, 0 }, 0, 0 },
+      { { 1, 33 }, { 147, 0 }, { 241, 241 }, { 56, 40 }, { 0, 0 }, 0, 0 },
+      { { 33, 54 }, { 128, 14 }, { 162, 241 }, { 1, 213 }, { 0, 0 }, 8, 0 },
+      { { 1, 1 }, { 146, 0 }, { 194, 194 }, { 168, 88 }, { 0, 0 }, 10, 0 },
+      { { 12, 129 }, { 92, 0 }, { 246, 243 }, { 84, 181 }, { 0, 0 }, 0, 0 },
+      { { 7, 17 }, { 151, 128 }, { 246, 245 }, { 50, 17 }, { 0, 0 }, 2, 0 },
+      { { 23, 1 }, { 33, 0 }, { 86, 246 }, { 4, 4 }, { 0, 0 }, 2, 0 },
+      { { 24, 129 }, { 98, 0 }, { 243, 242 }, { 230, 246 }, { 0, 0 }, 0, 0 },
+      { { 24, 33 }, { 35, 0 }, { 247, 229 }, { 85, 216 }, { 0, 0 }, 0, 0 },
+      { { 21, 1 }, { 145, 0 }, { 246, 246 }, { 166, 230 }, { 0, 0 }, 4, 0 },
+      { { 69, 129 }, { 89, 128 }, { 211, 163 }, { 130, 227 }, { 0, 0 }, 12, 0 },
+      { { 3, 129 }, { 73, 128 }, { 116, 179 }, { 85, 5 }, { 1, 0 }, 4, 0 },
+      { { 113, 49 }, { 146, 0 }, { 246, 241 }, { 20, 7 }, { 0, 0 }, 2, 0 },
+      { { 114, 48 }, { 20, 0 }, { 199, 199 }, { 88, 8 }, { 0, 0 }, 2, 0 },
+      { { 112, 177 }, { 68, 0 }, { 170, 138 }, { 24, 8 }, { 0, 0 }, 4, 0 },
+      { { 35, 177 }, { 147, 0 }, { 151, 85 }, { 35, 20 }, { 1, 0 }, 4, 0 },
+      { { 97, 177 }, { 19, 128 }, { 151, 85 }, { 4, 4 }, { 1, 0 }, 0, 0 },
+      { { 36, 177 }, { 72, 0 }, { 152, 70 }, { 42, 26 }, { 1, 0 }, 12, 0 },
+      { { 97, 33 }, { 19, 0 }, { 145, 97 }, { 6, 7 }, { 1, 0 }, 10, 0 },
+      { { 33, 161 }, { 19, 137 }, { 113, 97 }, { 6, 7 }, { 0, 0 }, 6, 0 },
+      { { 2, 65 }, { 156, 128 }, { 243, 243 }, { 148, 200 }, { 1, 0 }, 12, 0 },
+      { { 3, 17 }, { 84, 0 }, { 243, 241 }, { 154, 231 }, { 1, 0 }, 12, 0 },
+      { { 35, 33 }, { 95, 0 }, { 241, 242 }, { 58, 248 }, { 0, 0 }, 0, 0 },
+      { { 3, 33 }, { 135, 128 }, { 246, 243 }, { 34, 243 }, { 1, 0 }, 6, 0 },
+      { { 3, 33 }, { 71, 0 }, { 249, 246 }, { 84, 58 }, { 0, 0 }, 0, 0 },
+      { { 35, 33 }, { 72, 0 }, { 149, 132 }, { 25, 25 }, { 1, 0 }, 8, 0 },
+      { { 35, 33 }, { 74, 0 }, { 149, 148 }, { 25, 25 }, { 1, 0 }, 8, 0 },
+      { { 9, 132 }, { 161, 128 }, { 32, 209 }, { 79, 248 }, { 0, 0 }, 8, 0 },
+      { { 33, 162 }, { 30, 0 }, { 148, 195 }, { 6, 166 }, { 0, 0 }, 2, 0 },
+      { { 49, 49 }, { 18, 0 }, { 241, 241 }, { 40, 24 }, { 0, 0 }, 10, 0 },
+      { { 49, 49 }, { 141, 0 }, { 241, 241 }, { 232, 120 }, { 0, 0 }, 10, 0 },
+      { { 49, 50 }, { 91, 0 }, { 81, 113 }, { 40, 72 }, { 0, 0 }, 12, 0 },
+      { { 1, 33 }, { 139, 64 }, { 161, 242 }, { 154, 223 }, { 0, 0 }, 8, 0 },
+      { { 1, 33 }, { 137, 64 }, { 161, 242 }, { 154, 223 }, { 0, 0 }, 8, 0 },
+      { { 49, 49 }, { 139, 0 }, { 244, 241 }, { 232, 120 }, { 0, 0 }, 10, 0 },
+      { { 49, 49 }, { 18, 0 }, { 241, 241 }, { 40, 24 }, { 0, 0 }, 10, 0 },
+      { { 49, 33 }, { 21, 0 }, { 221, 86 }, { 19, 38 }, { 1, 0 }, 8, 0 },
+      { { 49, 33 }, { 22, 0 }, { 221, 102 }, { 19, 6 }, { 1, 0 }, 8, 0 },
+      { { 113, 49 }, { 73, 0 }, { 209, 97 }, { 28, 12 }, { 1, 0 }, 8, 0 },
+      { { 33, 35 }, { 77, 128 }, { 113, 114 }, { 18, 6 }, { 1, 0 }, 2, 0 },
+      { { 241, 225 }, { 64, 0 }, { 241, 111 }, { 33, 22 }, { 1, 0 }, 2, 0 },
+      { { 2, 1 }, { 26, 128 }, { 245, 133 }, { 117, 53 }, { 1, 0 }, 0, 0 },
+      { { 2, 1 }, { 29, 128 }, { 245, 243 }, { 117, 244 }, { 1, 0 }, 0, 0 },
+      { { 16, 17 }, { 65, 0 }, { 245, 242 }, { 5, 195 }, { 1, 0 }, 2, 0 },
+      { { 33, 162 }, { 155, 1 }, { 177, 114 }, { 37, 8 }, { 1, 0 }, 14, 0 },
+      { { 161, 33 }, { 152, 0 }, { 127, 63 }, { 3, 7 }, { 1, 1 }, 0, 0 },
+      { { 161, 97 }, { 147, 0 }, { 193, 79 }, { 18, 5 }, { 0, 0 }, 10, 0 },
+      { { 33, 97 }, { 24, 0 }, { 193, 79 }, { 34, 5 }, { 0, 0 }, 12, 0 },
+      { { 49, 114 }, { 91, 131 }, { 244, 138 }, { 21, 5 }, { 0, 0 }, 0, 0 },
+      { { 161, 97 }, { 144, 0 }, { 116, 113 }, { 57, 103 }, { 0, 0 }, 0, 0 },
+      { { 113, 114 }, { 87, 0 }, { 84, 122 }, { 5, 5 }, { 0, 0 }, 12, 0 },
+      { { 144, 65 }, { 0, 0 }, { 84, 165 }, { 99, 69 }, { 0, 0 }, 8, 0 },
+      { { 33, 33 }, { 146, 1 }, { 133, 143 }, { 23, 9 }, { 0, 0 }, 12, 0 },
+      { { 33, 33 }, { 148, 5 }, { 117, 143 }, { 23, 9 }, { 0, 0 }, 12, 0 },
+      { { 33, 97 }, { 148, 0 }, { 118, 130 }, { 21, 55 }, { 0, 0 }, 12, 0 },
+      { { 49, 33 }, { 67, 0 }, { 158, 98 }, { 23, 44 }, { 1, 1 }, 2, 0 },
+      { { 33, 33 }, { 155, 0 }, { 97, 127 }, { 106, 10 }, { 0, 0 }, 2, 0 },
+      { { 97, 34 }, { 138, 6 }, { 117, 116 }, { 31, 15 }, { 0, 0 }, 8, 0 },
+      { { 161, 33 }, { 134, 13 }, { 114, 113 }, { 85, 24 }, { 1, 0 }, 0, 0 },
+      { { 33, 33 }, { 77, 0 }, { 84, 166 }, { 60, 28 }, { 0, 0 }, 8, 0 },
+      { { 49, 97 }, { 143, 0 }, { 147, 114 }, { 2, 11 }, { 1, 0 }, 8, 0 },
+      { { 49, 97 }, { 142, 0 }, { 147, 114 }, { 3, 9 }, { 1, 0 }, 8, 0 },
+      { { 49, 97 }, { 145, 0 }, { 147, 130 }, { 3, 9 }, { 1, 0 }, 10, 0 },
+      { { 49, 97 }, { 142, 0 }, { 147, 114 }, { 15, 15 }, { 1, 0 }, 10, 0 },
+      { { 33, 33 }, { 75, 0 }, { 170, 143 }, { 22, 10 }, { 1, 0 }, 8, 0 },
+      { { 49, 33 }, { 144, 0 }, { 126, 139 }, { 23, 12 }, { 1, 1 }, 6, 0 },
+      { { 49, 50 }, { 129, 0 }, { 117, 97 }, { 25, 25 }, { 1, 0 }, 0, 0 },
+      { { 50, 33 }, { 144, 0 }, { 155, 114 }, { 33, 23 }, { 0, 0 }, 4, 0 },
+      { { 225, 225 }, { 31, 0 }, { 133, 101 }, { 95, 26 }, { 0, 0 }, 0, 0 },
+      { { 225, 225 }, { 70, 0 }, { 136, 101 }, { 95, 26 }, { 0, 0 }, 0, 0 },
+      { { 161, 33 }, { 156, 0 }, { 117, 117 }, { 31, 10 }, { 0, 0 }, 2, 0 },
+      { { 49, 33 }, { 139, 0 }, { 132, 101 }, { 88, 26 }, { 0, 0 }, 0, 0 },
+      { { 225, 161 }, { 76, 0 }, { 102, 101 }, { 86, 38 }, { 0, 0 }, 0, 0 },
+      { { 98, 161 }, { 203, 0 }, { 118, 85 }, { 70, 54 }, { 0, 0 }, 0, 0 },
+      { { 98, 161 }, { 153, 0 }, { 87, 86 }, { 7, 7 }, { 0, 0 }, 11, 0 },
+      { { 98, 161 }, { 147, 0 }, { 119, 118 }, { 7, 7 }, { 0, 0 }, 11, 0 },
+      { { 34, 33 }, { 89, 0 }, { 255, 255 }, { 3, 15 }, { 2, 0 }, 0, 0 },
+      { { 33, 33 }, { 14, 0 }, { 255, 255 }, { 15, 15 }, { 1, 1 }, 0, 0 },
+      { { 34, 33 }, { 70, 128 }, { 134, 100 }, { 85, 24 }, { 0, 0 }, 0, 0 },
+      { { 33, 161 }, { 69, 0 }, { 102, 150 }, { 18, 10 }, { 0, 0 }, 0, 0 },
+      { { 33, 34 }, { 139, 0 }, { 146, 145 }, { 42, 42 }, { 1, 0 }, 0, 0 },
+      { { 162, 97 }, { 158, 64 }, { 223, 111 }, { 5, 7 }, { 0, 0 }, 2, 0 },
+      { { 32, 96 }, { 26, 0 }, { 239, 143 }, { 1, 6 }, { 0, 2 }, 0, 0 },
+      { { 33, 33 }, { 143, 128 }, { 241, 244 }, { 41, 9 }, { 0, 0 }, 10, 0 },
+      { { 119, 161 }, { 165, 0 }, { 83, 160 }, { 148, 5 }, { 0, 0 }, 2, 0 },
+      { { 97, 177 }, { 31, 128 }, { 168, 37 }, { 17, 3 }, { 0, 0 }, 10, 0 },
+      { { 97, 97 }, { 23, 0 }, { 145, 85 }, { 52, 22 }, { 0, 0 }, 12, 0 },
+      { { 113, 114 }, { 93, 0 }, { 84, 106 }, { 1, 3 }, { 0, 0 }, 0, 0 },
+      { { 33, 162 }, { 151, 0 }, { 33, 66 }, { 67, 53 }, { 0, 0 }, 8, 0 },
+      { { 161, 33 }, { 28, 0 }, { 161, 49 }, { 119, 71 }, { 1, 1 }, 0, 0 },
+      { { 33, 97 }, { 137, 3 }, { 17, 66 }, { 51, 37 }, { 0, 0 }, 10, 0 },
+      { { 161, 33 }, { 21, 0 }, { 17, 207 }, { 71, 7 }, { 1, 0 }, 0, 0 },
+      { { 58, 81 }, { 206, 0 }, { 248, 134 }, { 246, 2 }, { 0, 0 }, 2, 0 },
+      { { 33, 33 }, { 21, 0 }, { 33, 65 }, { 35, 19 }, { 1, 0 }, 0, 0 },
+      { { 6, 1 }, { 91, 0 }, { 116, 165 }, { 149, 114 }, { 0, 0 }, 0, 0 },
+      { { 34, 97 }, { 146, 131 }, { 177, 242 }, { 129, 38 }, { 0, 0 }, 12, 0 },
+      { { 65, 66 }, { 77, 0 }, { 241, 242 }, { 81, 245 }, { 1, 0 }, 0, 0 },
+      { { 97, 163 }, { 148, 128 }, { 17, 17 }, { 81, 19 }, { 1, 0 }, 6, 0 },
+      { { 97, 161 }, { 140, 128 }, { 17, 29 }, { 49, 3 }, { 0, 0 }, 6, 0 },
+      { { 164, 97 }, { 76, 0 }, { 243, 129 }, { 115, 35 }, { 1, 0 }, 4, 0 },
+      { { 2, 7 }, { 133, 3 }, { 210, 242 }, { 83, 246 }, { 0, 1 }, 0, 0 },
+      { { 17, 19 }, { 12, 128 }, { 163, 162 }, { 17, 229 }, { 1, 0 }, 0, 0 },
+      { { 17, 17 }, { 6, 0 }, { 246, 242 }, { 65, 230 }, { 1, 2 }, 4, 0 },
+      { { 147, 145 }, { 145, 0 }, { 212, 235 }, { 50, 17 }, { 0, 1 }, 8, 0 },
+      { { 4, 1 }, { 79, 0 }, { 250, 194 }, { 86, 5 }, { 0, 0 }, 12, 0 },
+      { { 33, 34 }, { 73, 0 }, { 124, 111 }, { 32, 12 }, { 0, 1 }, 6, 0 },
+      { { 49, 33 }, { 133, 0 }, { 221, 86 }, { 51, 22 }, { 1, 0 }, 10, 0 },
+      { { 32, 33 }, { 4, 129 }, { 218, 143 }, { 5, 11 }, { 2, 0 }, 6, 0 },
+      { { 5, 3 }, { 106, 128 }, { 241, 195 }, { 229, 229 }, { 0, 0 }, 6, 0 },
+      { { 7, 2 }, { 21, 0 }, { 236, 248 }, { 38, 22 }, { 0, 0 }, 10, 0 },
+      { { 5, 1 }, { 157, 0 }, { 103, 223 }, { 53, 5 }, { 0, 0 }, 8, 0 },
+      { { 24, 18 }, { 150, 0 }, { 250, 248 }, { 40, 229 }, { 0, 0 }, 10, 0 },
+      { { 16, 0 }, { 134, 3 }, { 168, 250 }, { 7, 3 }, { 0, 0 }, 6, 0 },
+      { { 17, 16 }, { 65, 3 }, { 248, 243 }, { 71, 3 }, { 2, 0 }, 4, 0 },
+      { { 1, 16 }, { 142, 0 }, { 241, 243 }, { 6, 2 }, { 2, 0 }, 14, 0 },
+      { { 14, 192 }, { 0, 0 }, { 31, 31 }, { 0, 255 }, { 0, 3 }, 14, 0 },
+      { { 6, 3 }, { 128, 136 }, { 248, 86 }, { 36, 132 }, { 0, 2 }, 14, 0 },
+      { { 14, 208 }, { 0, 5 }, { 248, 52 }, { 0, 4 }, { 0, 3 }, 14, 0 },
+      { { 14, 192 }, { 0, 0 }, { 246, 31 }, { 0, 2 }, { 0, 3 }, 14, 0 },
+      { { 213, 218 }, { 149, 64 }, { 55, 86 }, { 163, 55 }, { 0, 0 }, 0, 0 },
+      { { 53, 20 }, { 92, 8 }, { 178, 244 }, { 97, 21 }, { 2, 0 }, 10, 0 },
+      { { 14, 208 }, { 0, 0 }, { 246, 79 }, { 0, 245 }, { 0, 3 }, 14, 0 },
+      { { 38, 228 }, { 0, 0 }, { 255, 18 }, { 1, 22 }, { 0, 1 }, 14, 0 },
+      { { 0, 0 }, { 0, 0 }, { 243, 246 }, { 240, 201 }, { 0, 2 }, 14, 0 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 0, 0 }, { 0, 0 }, { 252, 250 }, { 5, 23 }, { 2, 0 }, 14, 52 },
+      { { 0, 1 }, { 2, 0 }, { 255, 255 }, { 7, 8 }, { 0, 0 }, 0, 48 },
+      { { 0, 0 }, { 0, 0 }, { 252, 250 }, { 5, 23 }, { 2, 0 }, 14, 58 },
+      { { 0, 0 }, { 0, 0 }, { 246, 246 }, { 12, 6 }, { 0, 0 }, 4, 60 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 47 },
+      { { 0, 0 }, { 3, 0 }, { 248, 246 }, { 42, 69 }, { 0, 1 }, 4, 43 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 49 },
+      { { 0, 0 }, { 3, 0 }, { 248, 246 }, { 42, 69 }, { 0, 1 }, 4, 43 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 51 },
+      { { 0, 0 }, { 3, 0 }, { 248, 246 }, { 42, 69 }, { 0, 1 }, 4, 43 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 54 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 57 },
+      { { 0, 0 }, { 3, 0 }, { 248, 246 }, { 42, 69 }, { 0, 1 }, 4, 72 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 60 },
+      { { 14, 208 }, { 0, 10 }, { 245, 159 }, { 48, 2 }, { 0, 0 }, 14, 76 },
+      { { 14, 7 }, { 10, 93 }, { 228, 245 }, { 228, 229 }, { 3, 1 }, 6, 84 },
+      { { 2, 5 }, { 3, 10 }, { 180, 151 }, { 4, 247 }, { 0, 0 }, 14, 36 },
+      { { 78, 158 }, { 0, 0 }, { 246, 159 }, { 0, 2 }, { 0, 3 }, 14, 76 },
+      { { 17, 16 }, { 69, 8 }, { 248, 243 }, { 55, 5 }, { 2, 0 }, 8, 84 },
+      { { 14, 208 }, { 0, 0 }, { 246, 159 }, { 0, 2 }, { 0, 3 }, 14, 83 },
+      { { 128, 16 }, { 0, 13 }, { 255, 255 }, { 3, 20 }, { 3, 0 }, 12, 84 },
+      { { 14, 7 }, { 8, 81 }, { 248, 244 }, { 66, 228 }, { 0, 3 }, 14, 24 },
+      { { 14, 208 }, { 0, 10 }, { 245, 159 }, { 48, 2 }, { 0, 0 }, 14, 77 },
+      { { 1, 2 }, { 0, 0 }, { 250, 200 }, { 191, 151 }, { 0, 0 }, 7, 60 },
+      { { 1, 1 }, { 81, 0 }, { 250, 250 }, { 135, 183 }, { 0, 0 }, 6, 65 },
+      { { 1, 2 }, { 84, 0 }, { 250, 248 }, { 141, 184 }, { 0, 0 }, 6, 59 },
+      { { 1, 2 }, { 89, 0 }, { 250, 248 }, { 136, 182 }, { 0, 0 }, 6, 51 },
+      { { 1, 0 }, { 0, 0 }, { 249, 250 }, { 10, 6 }, { 3, 0 }, 14, 45 },
+      { { 0, 0 }, { 128, 0 }, { 249, 246 }, { 137, 108 }, { 3, 0 }, 14, 71 },
+      { { 3, 12 }, { 128, 8 }, { 248, 246 }, { 136, 182 }, { 3, 0 }, 15, 60 },
+      { { 3, 12 }, { 133, 0 }, { 248, 246 }, { 136, 182 }, { 3, 0 }, 15, 58 },
+      { { 14, 0 }, { 64, 8 }, { 118, 119 }, { 79, 24 }, { 0, 2 }, 14, 53 },
+      { { 14, 3 }, { 64, 0 }, { 200, 155 }, { 73, 105 }, { 0, 2 }, 14, 64 },
+      { { 215, 199 }, { 220, 0 }, { 173, 141 }, { 5, 5 }, { 3, 0 }, 14, 71 },
+      { { 215, 199 }, { 220, 0 }, { 168, 136 }, { 4, 4 }, { 3, 0 }, 14, 61 },
+      { { 128, 17 }, { 0, 0 }, { 246, 103 }, { 6, 23 }, { 3, 3 }, 14, 61 },
+      { { 128, 17 }, { 0, 9 }, { 245, 70 }, { 5, 22 }, { 2, 3 }, 14, 48 },
+      { { 6, 21 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 0, 0 }, 1, 48 },
+      { { 6, 18 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 3, 0 }, 0, 69 },
+      { { 6, 18 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 0, 0 }, 1, 68 },
+      { { 1, 2 }, { 88, 0 }, { 103, 117 }, { 231, 7 }, { 0, 0 }, 0, 63 },
+      { { 65, 66 }, { 69, 8 }, { 248, 117 }, { 72, 5 }, { 0, 0 }, 0, 74 },
+      { { 10, 30 }, { 64, 78 }, { 224, 255 }, { 240, 5 }, { 3, 0 }, 8, 60 },
+      { { 10, 30 }, { 124, 82 }, { 224, 255 }, { 240, 2 }, { 3, 0 }, 8, 80 },
+      { { 14, 0 }, { 64, 8 }, { 122, 123 }, { 74, 27 }, { 0, 2 }, 14, 64 },
+      { { 14, 7 }, { 10, 64 }, { 228, 85 }, { 228, 57 }, { 3, 1 }, 6, 69 },
+      { { 5, 4 }, { 5, 64 }, { 249, 214 }, { 50, 165 }, { 3, 0 }, 14, 73 },
+      { { 2, 21 }, { 63, 0 }, { 0, 247 }, { 243, 245 }, { 3, 0 }, 8, 75 },
+      { { 1, 2 }, { 79, 0 }, { 250, 248 }, { 141, 181 }, { 0, 0 }, 7, 68 },
+      { { 0, 0 }, { 0, 0 }, { 246, 246 }, { 12, 6 }, { 0, 0 }, 4, 48 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 53 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 35 }
+   };
--- /dev/null
+++ b/Game/src/audiolib/gus.c
@@ -1,0 +1,283 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   GUS.C
+
+   author: James R. Dose
+   date:   September 7, 1994
+
+   Gravis Ultrasound initialization routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <conio.h>
+#include <dos.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include "usrhooks.h"
+#include "interrup.h"
+#include "newgf1.h"
+#include "gusmidi.h"
+#include "guswave.h"
+#include "_guswave.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+// size of DMA buffer for patch loading
+#define DMABUFFSIZE 2048U
+
+struct gf1_dma_buff GUS_HoldBuffer;
+static int          HoldBufferAllocated = FALSE;
+
+static int GUS_Installed = 0;
+
+extern VoiceNode   GUSWAVE_Voices[ VOICES ];
+extern int GUSWAVE_Installed;
+
+unsigned long GUS_TotalMemory;
+int           GUS_MemConfig;
+
+int GUS_AuxError  = 0;
+
+int GUS_ErrorCode = GUS_Ok;
+
+#define GUS_SetErrorCode( status ) \
+   GUS_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: GUS_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *GUS_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case GUS_Warning :
+      case GUS_Error :
+         ErrorString = GUS_ErrorString( GUS_ErrorCode );
+         break;
+
+      case GUS_Ok :
+         ErrorString = "Ultrasound music ok.";
+         break;
+
+      case GUS_OutOfMemory :
+         ErrorString = "Out of memory in GusMidi.";
+         break;
+
+      case GUS_OutOfDosMemory :
+         ErrorString = "Out of conventional (640K) memory in GusMidi.";
+         break;
+
+      case GUS_GF1Error :
+         ErrorString = gf1_error_str( GUS_AuxError );
+         break;
+
+      case GUS_InvalidIrq :
+         ErrorString = "Ultrasound IRQ must be 7 or less.";
+         break;
+
+      case GUS_ULTRADIRNotSet :
+         ErrorString = "ULTRADIR environment variable not set.";
+         break;
+
+      case GUS_MissingConfig :
+//         ErrorString = "Can't find GUSMIDI.INI file.";
+         ErrorString = "Can't find ULTRAMID.INI file.";
+         break;
+
+      case GUS_FileError :
+         ErrorString = strerror( GUS_AuxError );
+         break;
+
+      default :
+         ErrorString = "Unknown Ultrasound error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+
+/*---------------------------------------------------------------------
+   Function: D32DosMemAlloc
+
+   Allocate a block of Conventional memory.
+---------------------------------------------------------------------*/
+
+void *D32DosMemAlloc
+   (
+   unsigned size
+   )
+
+   {
+   union REGS r;
+
+   // DPMI allocate DOS memory
+   r.x.eax = 0x0100;
+
+   // Number of paragraphs requested
+   r.x.ebx = ( size + 15 ) >> 4;
+   int386( 0x31, &r, &r );
+   if ( r.x.cflag )
+      {
+      // Failed
+      return( NULL );
+      }
+
+   return( ( void * )( ( r.x.eax & 0xFFFF ) << 4 ) );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUS_Init
+
+   Initializes the Gravis Ultrasound for sound and music playback.
+---------------------------------------------------------------------*/
+
+int GUS_Init
+   (
+   void
+   )
+
+   {
+   struct load_os os;
+   int ret;
+
+   if ( GUS_Installed > 0 )
+      {
+      GUS_Installed++;
+      return( GUS_Ok );
+      }
+
+   GUS_SetErrorCode( GUS_Ok );
+
+   GUS_Installed = 0;
+
+   GetUltraCfg( &os );
+
+   if ( os.forced_gf1_irq > 7 )
+      {
+      GUS_SetErrorCode( GUS_InvalidIrq );
+      return( GUS_Error );
+      }
+
+   if ( !HoldBufferAllocated )
+      {
+      GUS_HoldBuffer.vptr = D32DosMemAlloc( DMABUFFSIZE );
+      if ( GUS_HoldBuffer.vptr == NULL )
+         {
+         GUS_SetErrorCode( GUS_OutOfDosMemory );
+         return( GUS_Error );
+         }
+      GUS_HoldBuffer.paddr = ( unsigned long )GUS_HoldBuffer.vptr;
+
+      HoldBufferAllocated = TRUE;
+      }
+
+   os.voices = 24;
+   ret = gf1_load_os( &os );
+   if ( ret )
+      {
+      GUS_AuxError = ret;
+      GUS_SetErrorCode( GUS_GF1Error );
+      return( GUS_Error );
+      }
+
+   GUS_TotalMemory = gf1_mem_avail();
+   GUS_MemConfig   = ( GUS_TotalMemory - 1 ) >> 18;
+
+   GUS_Installed = 1;
+   return( GUS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUS_Shutdown
+
+   Ends use of the Gravis Ultrasound.  Must be called the same number
+   of times as GUS_Init.
+---------------------------------------------------------------------*/
+
+void GUS_Shutdown
+   (
+   void
+   )
+
+   {
+   if ( GUS_Installed > 0 )
+      {
+      GUS_Installed--;
+      if ( GUS_Installed == 0 )
+         {
+         gf1_unload_os();
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_Shutdown
+
+   Terminates use of the Gravis Ultrasound for digitized sound playback.
+---------------------------------------------------------------------*/
+
+void GUSWAVE_Shutdown
+   (
+   void
+   )
+
+   {
+   int i;
+
+   if ( GUSWAVE_Installed )
+      {
+      GUSWAVE_KillAllVoices();
+
+      // free memory
+      for ( i = 0; i < VOICES; i++ )
+         {
+         if ( GUSWAVE_Voices[ i ].mem != NULL )
+            {
+            gf1_free( GUSWAVE_Voices[ i ].mem );
+            GUSWAVE_Voices[ i ].mem = NULL;
+            }
+         }
+
+      GUS_Shutdown();
+      GUSWAVE_Installed = FALSE;
+      }
+   }
--- /dev/null
+++ b/Game/src/audiolib/gusmidi.c
@@ -1,0 +1,561 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   GUSMIDI.C
+
+   author: James R. Dose
+   date:   March 23, 1994
+
+   General MIDI playback functions for the Gravis Ultrasound
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+// Module MUST be compiled with structure allignment set to a maximum
+// of 1 byte ( zp1 ).
+
+#include <conio.h>
+#include <dos.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include "usrhooks.h"
+#include "interrup.h"
+#include "newgf1.h"
+#include "gusmidi.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+// size of DMA buffer for patch loading
+#define DMABUFFSIZE 2048U
+
+#define MAX_MEM_CONFIG   3
+
+// size of patch array (128 perc, 128 melodic)
+#define NUM_PATCHES 256
+
+// size of largest patch name
+#define BIGGEST_NAME 9
+
+#define UNUSED_PATCH -1
+
+static struct patch   Patch[ NUM_PATCHES ];
+static unsigned char *PatchWaves[ NUM_PATCHES ];
+
+static int   PatchMap[ NUM_PATCHES ][ MAX_MEM_CONFIG + 1 ];
+static char  ProgramName[ NUM_PATCHES ][ BIGGEST_NAME ];
+static char  PatchLoaded[ NUM_PATCHES ];
+
+static char  ConfigFileName[] = "ULTRAMID.INI";
+static char  ConfigDirectory[ 80 ] = { '\0' };
+
+// The name of the configuration directory
+static char  InstrumentDirectory[ 80 ];
+
+extern struct gf1_dma_buff GUS_HoldBuffer;
+
+extern unsigned long GUS_TotalMemory;
+extern int           GUS_MemConfig;
+
+static int GUSMIDI_Volume = 255;
+
+extern int GUS_AuxError;
+extern int GUS_ErrorCode;
+
+int GUSMIDI_Installed = FALSE;
+
+#define GUS_SetErrorCode( status ) \
+   GUS_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: GUS_GetPatchMap
+
+   Reads the patch map from disk.
+---------------------------------------------------------------------*/
+
+int GUS_GetPatchMap
+   (
+   char *name
+   )
+
+   {
+   char text[ 80 ];
+   char *ud;
+   int  index;
+   int  ignore;
+   FILE *fp;
+
+   for( index = 0; index < NUM_PATCHES; index++ )
+      {
+      PatchMap[ index ][ 0 ] = UNUSED_PATCH;
+      PatchMap[ index ][ 1 ] = UNUSED_PATCH;
+      PatchMap[ index ][ 2 ] = UNUSED_PATCH;
+      PatchMap[ index ][ 3 ] = UNUSED_PATCH;
+      ProgramName[ index ][ 0 ] = 0;
+      }
+
+   ud = getenv( "ULTRADIR" );
+   if ( ud == NULL )
+      {
+      GUS_SetErrorCode( GUS_ULTRADIRNotSet );
+      return( GUS_Error );
+      }
+
+   strcpy( InstrumentDirectory, ud );
+   strcat( InstrumentDirectory, "\\midi\\" );
+   strcpy( ConfigDirectory, ud );
+   strcat( ConfigDirectory, "\\midi\\" );
+   strcpy( text, name );
+
+   fp = fopen( text, "r" );
+   if ( fp == NULL )
+      {
+      strcpy( text, InstrumentDirectory );
+      strcat( text, name );
+
+      fp = fopen( text, "r" );
+      if ( fp == NULL )
+         {
+         GUS_SetErrorCode( GUS_MissingConfig );
+         return( GUS_Error );
+         }
+      }
+
+   while( 1 )
+      {
+      if ( fgets( text, 80, fp ) == NULL )
+         {
+         break;
+         }
+
+      if ( text[ 0 ] == '#' )
+         {
+         continue;
+         }
+
+      if ( sscanf( text, "%d", &index ) != 1 )
+         {
+         continue;
+         }
+
+      sscanf( text, "%d, %d, %d, %d, %d, %s\n", &ignore,
+         &PatchMap[ index ][ 0 ],
+         &PatchMap[ index ][ 1 ],
+         &PatchMap[ index ][ 2 ],
+         &PatchMap[ index ][ 3 ],
+         ProgramName[ index ] );
+      }
+
+   fclose( fp );
+
+   return( GUS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_UnloadPatch
+
+   Unloads a patch from the GUS's memory.
+---------------------------------------------------------------------*/
+
+int GUSMIDI_UnloadPatch
+   (
+   int prognum
+   )
+
+   {
+   int      prog;
+   unsigned flags;
+
+   prog = PatchMap[ prognum ][ GUS_MemConfig ];
+
+   if ( PatchLoaded[ prog ] )
+      {
+      flags = DisableInterrupts();
+
+      gf1_unload_patch( &Patch[ prog ] );
+      if ( PatchWaves[ prog ] != NULL )
+         {
+         USRHOOKS_FreeMem( PatchWaves[ prog ] );
+         PatchWaves[ prog ] = NULL;
+         }
+
+      // just in case sequence is still playing
+      Patch[ prog ].nlayers = 0;
+      PatchLoaded[ prog ]   = FALSE;
+
+      RestoreInterrupts( flags );
+      }
+
+   return( GUS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_LoadPatch
+
+   Loads a patch into the GUS's memory.
+---------------------------------------------------------------------*/
+
+int GUSMIDI_LoadPatch
+   (
+   int prognum
+   )
+
+   {
+   int  prog;
+   char text[ 80 ];
+   int  ret;
+   unsigned char *wave_buff;
+   struct patchinfo  patchi;
+   int  status;
+
+   prog = PatchMap[ prognum ][ GUS_MemConfig ];
+
+   if ( ( PatchLoaded[ prog ] ) || ( prog == UNUSED_PATCH ) )
+      {
+      return( GUS_Ok );
+      }
+
+   if ( !ProgramName[ prog ][ 0 ] )
+      {
+      return( GUS_Ok );
+      }
+
+   strcpy( text, InstrumentDirectory );
+   strcat( text, ProgramName[ prog ] );
+   strcat( text, ".pat" );
+
+   ret = gf1_get_patch_info( text, &patchi );
+   if ( ret != OK )
+      {
+      GUS_AuxError = ret;
+      GUS_SetErrorCode( GUS_GF1Error );
+      return( GUS_Error );
+      }
+
+   status = USRHOOKS_GetMem( &wave_buff, patchi.header.wave_forms *
+      sizeof( struct wave_struct ) );
+   if ( status != USRHOOKS_Ok )
+      {
+      GUS_SetErrorCode( GUS_OutOfMemory );
+      return( GUS_Error );
+      }
+
+   ret = gf1_load_patch( text, &patchi, &Patch[ prog ], &GUS_HoldBuffer,
+      DMABUFFSIZE, ( unsigned char * )wave_buff, PATCH_LOAD_8_BIT );
+
+   if ( ret != OK )
+      {
+      USRHOOKS_FreeMem( wave_buff );
+      GUS_AuxError = ret;
+      GUS_SetErrorCode( GUS_GF1Error );
+      return( GUS_Error );
+      }
+
+   PatchWaves[ prog ] = wave_buff;
+   PatchLoaded[ prog ] = TRUE;
+
+   return( GUS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_ProgramChange
+
+   Selects the instrument to use on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_ProgramChange
+   (
+   int channel,
+   int prognum
+   )
+
+   {
+   int  prog;
+
+   prog = PatchMap[ prognum ][ GUS_MemConfig ];
+
+   if ( PatchLoaded[ prog ] )
+      {
+      gf1_midi_change_program( &Patch[ prog ], channel );
+      }
+   else
+      {
+      gf1_midi_change_program( NULL, channel );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_NoteOn
+
+   Plays a note on the specified channel.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_NoteOn
+   (
+   int chan,
+   int note,
+   int velocity
+   )
+
+   {
+   int prog;
+
+   if ( chan == 9 )
+      {
+      prog = PatchMap[ note + 128 ][ GUS_MemConfig ];
+
+      if ( PatchLoaded[ prog ] )
+         {
+         gf1_midi_note_on( &Patch[ note + 128 ], 1,
+            note, velocity, 9 );
+         }
+      }
+   else
+      {
+      gf1_midi_note_on( 0L, 1, note, velocity, chan );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_NoteOff
+
+   Turns off a note on the specified channel.
+---------------------------------------------------------------------*/
+#pragma warn -par
+void GUSMIDI_NoteOff
+   (
+   int chan,
+   int note,
+   int velocity
+   )
+
+   {
+   gf1_midi_note_off( note, chan );
+   }
+#pragma warn .par
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_ControlChange
+
+   Sets the value of a controller on the specified channel.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_ControlChange
+   (
+   int channel,
+   int number,
+   int value
+   )
+
+   {
+   gf1_midi_parameter( channel, number, value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_PitchBend
+
+   Sets the pitch bend on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_PitchBend
+   (
+   int channel,
+   int lsb,
+   int msb
+   )
+
+   {
+   gf1_midi_pitch_bend( channel, lsb, msb );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_ReleasePatches
+
+   Removes all the instruments from the GUS's memory.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_ReleasePatches
+   (
+   void
+   )
+
+   {
+   int i;
+
+   for( i = 0; i < 256; i++ )
+      {
+      GUSMIDI_UnloadPatch( i );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_SetVolume
+
+   Sets the total music volume.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_SetVolume
+   (
+   int volume
+   )
+
+   {
+   // Set the minimum to 2 because 0 has a tremolo problem
+   volume = max( 2, volume );
+   volume = min( volume, 255 );
+
+   GUSMIDI_Volume = volume;
+
+   // range = 0 to 127
+   gf1_midi_synth_volume( 0, volume >> 1 );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_GetVolume
+
+   Returns the total music volume.
+---------------------------------------------------------------------*/
+
+int GUSMIDI_GetVolume
+   (
+   void
+   )
+
+   {
+   return( GUSMIDI_Volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_Init
+
+   Initializes the Gravis Ultrasound for music playback.
+---------------------------------------------------------------------*/
+
+int GUSMIDI_Init
+   (
+   void
+   )
+
+   {
+   int ret;
+   int i;
+   int startmem;
+//   unsigned long mem;
+   extern int GUSWAVE_Installed;
+
+   if ( GUSMIDI_Installed )
+      {
+      GUSMIDI_Shutdown();
+      }
+
+   ret = GUS_Init();
+   if ( ret != GUS_Ok )
+      {
+      return( ret );
+      }
+
+   if ( GUS_MemConfig < 0 )
+      {
+      GUS_MemConfig = 0;
+      }
+
+   if ( GUS_MemConfig > MAX_MEM_CONFIG )
+      {
+      GUS_MemConfig = MAX_MEM_CONFIG;
+      }
+
+   for( i = 0; i < NUM_PATCHES; i++ )
+      {
+      ProgramName[ i ][ 0 ] = '\0';
+      PatchWaves[ i ]       = NULL;
+      PatchLoaded[ i ]      = FALSE;
+      }
+
+   GUSMIDI_SetVolume( 255 );
+
+   GUSMIDI_Installed = TRUE;
+
+   ret = GUS_GetPatchMap( ConfigFileName );
+   if ( ret != GUS_Ok )
+      {
+      GUSMIDI_Shutdown();
+      return( ret );
+      }
+
+//   if ( !GUSWAVE_Installed )
+//      {
+//      mem = gf1_malloc( 8192 );
+//      }
+
+   startmem = gf1_mem_avail();
+   for( i = 0; i < NUM_PATCHES; i++ )
+      {
+      ret = GUSMIDI_LoadPatch( i );
+      if ( ret != GUS_Ok )
+         {
+         }
+//      if ( ret != GUS_Ok )
+//         {
+//         return( ret );
+//         }
+      }
+
+//   if ( !GUSWAVE_Installed )
+//      {
+//      gf1_free( mem );
+//      }
+
+   GUSMIDI_Installed = TRUE;
+
+   return( GUS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSMIDI_Shutdown
+
+   Ends use of the Gravis Ultrasound for music playback.
+---------------------------------------------------------------------*/
+
+void GUSMIDI_Shutdown
+   (
+   void
+   )
+
+   {
+   GUSMIDI_ReleasePatches();
+   GUS_Shutdown();
+   GUSMIDI_Installed = FALSE;
+   }
--- /dev/null
+++ b/Game/src/audiolib/gusmidi.h
@@ -1,0 +1,59 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __GUSMIDI_H
+#define __GUSMIDI_H
+
+extern struct gf1_dma_buff GUS_HoldBuffer;
+
+enum GUS_Errors
+   {
+   GUS_Warning        = -2,
+   GUS_Error          = -1,
+   GUS_Ok             = 0,
+   GUS_OutOfMemory,
+   GUS_OutOfDosMemory,
+   GUS_OutOfDRAM,
+   GUS_GF1Error,
+   GUS_InvalidIrq,
+   GUS_ULTRADIRNotSet,
+   GUS_MissingConfig,
+   GUS_FileError
+   };
+
+char *GUS_ErrorString( int ErrorNumber );
+int   GUS_GetPatchMap( char *name );
+int   GUSMIDI_UnloadPatch( int prog );
+int   GUSMIDI_LoadPatch( int prog );
+void  GUSMIDI_ProgramChange( int channel, int prog );
+void  GUSMIDI_NoteOn( int chan, int note, int velocity );
+void  GUSMIDI_NoteOff( int chan, int note, int velocity );
+void  GUSMIDI_ControlChange( int channel, int number, int value );
+void  GUSMIDI_PitchBend( int channel, int lsb, int msb );
+void  GUSMIDI_ReleasePatches( void );
+void  GUSMIDI_SetVolume( int volume );
+int   GUSMIDI_GetVolume( void );
+int   GUS_Init( void );
+void  GUS_Shutdown( void );
+#pragma aux GUS_Shutdown frame;
+int   GUSMIDI_Init( void );
+void  GUSMIDI_Shutdown( void );
+void *D32DosMemAlloc( unsigned size );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/guswave.c
@@ -1,0 +1,1773 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   GUSWAVE.C
+
+   author: James R. Dose
+   date:   March 23, 1994
+
+   Digitized sound playback routines for the Gravis Ultrasound.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdlib.h>
+#include <conio.h>
+#include <dos.h>
+#include <stdio.h>
+#include <io.h>
+#include <string.h>
+#include "debugio.h"
+#include "interrup.h"
+#include "ll_man.h"
+#include "pitch.h"
+#include "user.h"
+#include "multivoc.h"
+#include "_guswave.h"
+#include "newgf1.h"
+#include "gusmidi.h"
+#include "guswave.h"
+
+#define ATR_INDEX               0x3c0
+#define STATUS_REGISTER_1       0x3da
+
+#define SetBorderColor(color) \
+   { \
+   inp  (STATUS_REGISTER_1); \
+   outp (ATR_INDEX,0x31);    \
+   outp (ATR_INDEX,color);   \
+   }
+
+static const int GUSWAVE_PanTable[ 32 ] =
+   {
+      8,  9, 10, 11, 11, 12, 13, 14,
+     15, 14, 13, 12, 11, 10,  9,  8,
+      7,  6,  5,  4,  4,  3,  2,  1,
+      0,  1,  2,  3,  4,  5,  6,  7
+   };
+
+static voicelist VoiceList;
+static voicelist VoicePool;
+
+static voicestatus VoiceStatus[ MAX_VOICES ];
+//static
+VoiceNode GUSWAVE_Voices[ VOICES ];
+
+static int GUSWAVE_VoiceHandle  = GUSWAVE_MinVoiceHandle;
+static int GUSWAVE_MaxVoices = VOICES;
+//static
+int GUSWAVE_Installed = FALSE;
+
+static void ( *GUSWAVE_CallBackFunc )( unsigned long ) = NULL;
+
+// current volume for dig audio - from 0 to 4095
+static int GUSWAVE_Volume = MAX_VOLUME;
+
+static int GUSWAVE_SwapLeftRight = FALSE;
+
+static int GUS_Debug = FALSE;
+
+extern int GUSMIDI_Installed;
+
+int GUSWAVE_ErrorCode = GUSWAVE_Ok;
+
+#define GUSWAVE_SetErrorCode( status ) \
+   GUSWAVE_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *GUSWAVE_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case GUSWAVE_Warning :
+      case GUSWAVE_Error :
+         ErrorString = GUSWAVE_ErrorString( GUSWAVE_ErrorCode );
+         break;
+
+      case GUSWAVE_Ok :
+         ErrorString = "GUSWAVE ok.";
+         break;
+
+      case GUSWAVE_GUSError :
+         ErrorString = GUS_ErrorString( GUS_Error );
+         break;
+
+      case GUSWAVE_NotInstalled :
+         ErrorString = "GUSWAVE not installed.";
+         break;
+
+      case GUSWAVE_NoVoices :
+         ErrorString = "No free voices available to GUSWAVE.";
+         break;
+
+      case GUSWAVE_UltraNoMem :
+         ErrorString = "Not enough Ultrasound memory available for GUSWAVE.";
+         break;
+
+      case GUSWAVE_UltraNoMemMIDI :
+         ErrorString = "Not enough Ultrasound memory available for GUSWAVE.  "
+            "Try initializing Sound FX before Music.";
+         break;
+
+      case GUSWAVE_VoiceNotFound :
+         ErrorString = "No voice with matching handle found.";
+         break;
+
+      case GUSWAVE_InvalidVOCFile :
+         ErrorString = "Invalid VOC file passed in to GUSWAVE.";
+         break;
+
+      case GUSWAVE_InvalidWAVFile :
+         ErrorString = "Invalid WAV file passed in to GUSWAVE.";
+         break;
+
+      default :
+         ErrorString = "Unknown GUSWAVE error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_CallBack
+
+   GF1 callback service routine.
+---------------------------------------------------------------------*/
+
+char GUS_Silence8[ 1024 ] = //256 ] =
+   {
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+   0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
+
+
+   };
+
+//unsigned short GUS_Silence16[ 128 ] =
+unsigned short GUS_Silence16[ 512 ] =
+   {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+
+
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+   };
+
+static int LOADDS GUSWAVE_CallBack
+   (
+   int reason,
+   int voice,
+   unsigned char **buf,
+   unsigned long *size
+   )
+
+   {
+   VoiceNode *Voice;
+   playbackstatus status;
+
+   // this function is called from an interrupt
+   // remember not to make any DOS or BIOS calls from here
+   // also don't call any C library functions unless you are sure that
+   // they are reentrant
+   // restore our DS register
+
+   if ( VoiceStatus[ voice ].playing == FALSE )
+      {
+      return( DIG_DONE );
+      }
+
+   if ( reason == DIG_MORE_DATA )
+      {
+//      SetBorderColor(16);
+      Voice = VoiceStatus[ voice ].Voice;
+
+      if ( ( Voice != NULL ) && ( Voice->Playing ) )
+/*
+         {
+         *buf = ( unsigned char * )GUS_Silence16;
+         *size = 1024;
+
+         SetBorderColor(0);
+         return( DIG_MORE_DATA );
+         }
+ */
+         {
+         status = Voice->GetSound( Voice );
+         if ( status != SoundDone )
+            {
+            if ( ( Voice->sound == NULL ) || ( status == NoMoreData ) )
+               {
+               if ( Voice->bits == 8 )
+                  {
+                  *buf = GUS_Silence8;
+                  }
+               else
+                  {
+                  *buf = ( unsigned char * )GUS_Silence16;
+                  }
+               *size = 256;
+               }
+            else
+               {
+               *buf  = Voice->sound;
+               *size = Voice->length;
+               }
+            return( DIG_MORE_DATA );
+            }
+         }
+//      SetBorderColor(16);
+      return( DIG_DONE );
+      }
+
+   if ( reason == DIG_DONE )
+      {
+      Voice = VoiceStatus[ voice ].Voice;
+      VoiceStatus[ voice ].playing = FALSE;
+
+      if ( Voice != NULL )
+         {
+         Voice->Active   = FALSE;
+         Voice->Playing  = FALSE;
+
+// I'm commenting this out because a -1 could cause a crash if it
+// is sent to the GF1 code.  This shouldn't be necessary since
+// Active should be false when GF1voice is -1, but this is just
+// a precaution.  Adjust the pan on the wrong voice is a lot
+// more pleasant than a crash!
+//         Voice->GF1voice = -1;
+
+         LL_Remove( VoiceNode, &VoiceList, Voice );
+         LL_AddToTail( VoiceNode, &VoicePool, Voice );
+         }
+
+      if ( GUSWAVE_CallBackFunc )
+         {
+         GUSWAVE_CallBackFunc( Voice->callbackval );
+         }
+      }
+
+   return( DIG_DONE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_DebugCallBack
+
+   GF1 callback service routine with debugging info.
+---------------------------------------------------------------------*/
+
+static int LOADDS GUSWAVE_DebugCallBack
+   (
+   int reason,
+   int voice,
+   unsigned char **buf,
+   unsigned long *size
+   )
+
+   {
+   VoiceNode *Voice;
+
+   // this function is called from an interrupt
+   // remember not to make any DOS or BIOS calls from here
+   // also don't call any C library functions unless you are sure that
+   // they are reentrant
+   // restore our DS register
+
+   if ( VoiceStatus[ voice ].playing == FALSE )
+      {
+//      DB_printf( "GUS Voice %d not playing.\n", voice );
+      DB_printf( "GUS Voice " );
+      DB_PrintNum( voice );
+      DB_printf( " not playing.\n" );
+      return( DIG_DONE );
+         }
+
+      if ( reason == DIG_MORE_DATA )
+         {
+         Voice = VoiceStatus[ voice ].Voice;
+
+//         DB_printf( "Voice %d : More data -- ", Voice );
+         DB_printf( "Voice " );
+         DB_PrintNum( voice );
+         DB_printf( " : More data -- " );
+         if ( Voice != NULL )
+            {
+            if ( Voice->Playing )
+               {
+               GUSWAVE_GetNextVOCBlock( Voice );
+               if ( Voice->Playing )
+                  {
+//                  DB_printf( "More data -- size = %u blocklength = %u\n",
+//                     Voice->length, Voice->BlockLength );
+                  DB_printf( "More data -- size = " );
+                  DB_PrintNum( Voice->length );
+                  DB_printf( " blocklength = " );
+                  DB_PrintNum( Voice->BlockLength );
+                  DB_printf( "\n" );
+                  *buf  = Voice->sound;
+                  *size = Voice->length;
+                  return( DIG_MORE_DATA );
+                  }
+               else
+                  {
+                  DB_printf( "Voice done.\n" );
+                  }
+               }
+            else
+               {
+               DB_printf( "Voice not active.\n" );
+               }
+            }
+         else
+            {
+            DB_printf( " NULL Voice\n" );
+            }
+
+         return( DIG_DONE );
+         }
+
+      if ( reason == DIG_DONE )
+         {
+         VoiceStatus[ voice ].playing = FALSE;
+         Voice = VoiceStatus[ voice ].Voice;
+//         DB_printf( "Voice %d : Done -- ", Voice );
+         DB_printf( "Voice " );
+         DB_PrintNum( voice );
+         DB_printf( " : Done -- " );
+
+         if ( Voice != NULL )
+            {
+            DB_printf( "Ok\n" );
+
+            Voice->Active   = FALSE;
+            Voice->Playing  = FALSE;
+
+// I'm commenting this out because a -1 could cause a crash if it
+// is sent to the GF1 code.  This shouldn't be necessary since
+// Active should be false when GF1voice is -1, but this is just
+// a precaution.  Adjust the pan on the wrong voice is a lot
+// more pleasant than a crash!
+//         Voice->GF1voice = -1;
+
+         LL_Remove( VoiceNode, &VoiceList, Voice );
+         LL_AddToTail( VoiceNode, &VoicePool, Voice );
+         }
+      else
+         {
+         DB_printf( "Null voice\n" );
+         }
+
+      if ( GUSWAVE_CallBackFunc )
+         {
+         GUSWAVE_CallBackFunc( Voice->callbackval );
+         }
+      }
+
+   return( DIG_DONE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetVoice
+
+   Locates the voice with the specified handle.
+---------------------------------------------------------------------*/
+
+static VoiceNode *GUSWAVE_GetVoice
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   voice = VoiceList.start;
+
+   while( voice != NULL )
+      {
+      if ( handle == voice->handle )
+         {
+         break;
+         }
+
+      voice = voice->next;
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( voice == NULL )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound );
+      }
+
+   return( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_VoicePlaying
+
+   Checks if the voice associated with the specified handle is
+   playing.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_VoicePlaying
+   (
+   int handle
+   )
+
+   {
+   VoiceNode   *voice;
+
+   voice = GUSWAVE_GetVoice( handle );
+   if ( voice != NULL )
+      {
+      return( voice->Active );
+      }
+
+   return( FALSE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_VoicesPlaying
+
+   Determines the number of currently active voices.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_VoicesPlaying
+   (
+   void
+   )
+
+   {
+   int         index;
+   int         NumVoices = 0;
+   unsigned    flags;
+
+   flags = DisableInterrupts();
+
+   for( index = 0; index < GUSWAVE_MaxVoices; index++ )
+      {
+      if ( GUSWAVE_Voices[ index ].Active )
+         {
+         NumVoices++;
+         }
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( GUS_Debug )
+      {
+      DB_printf( "Number of voices = %d.\n", NumVoices );
+      }
+
+   return( NumVoices );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_Kill
+
+   Stops output of the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_Kill
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   voice = GUSWAVE_GetVoice( handle );
+
+   if ( voice == NULL )
+      {
+      RestoreInterrupts( flags );
+      GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound );
+
+      if ( GUS_Debug )
+         {
+         DB_printf( "Could not find voice to kill.\n" );
+         }
+
+      return( GUSWAVE_Warning );
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( !GUS_Debug )
+      {
+      if ( voice->Active )
+         {
+         gf1_stop_digital( voice->GF1voice );
+         }
+      }
+   else
+      {
+      DB_printf( "Kill - GUS Voice %d ", voice->GF1voice );
+      if ( voice->Active )
+         {
+         DB_printf( "active\n" );
+         gf1_stop_digital( voice->GF1voice );
+         }
+      else
+         {
+         DB_printf( "inactive\n" );
+         }
+      }
+
+//   RestoreInterrupts( flags );
+
+   return( GUSWAVE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_KillAllVoices
+
+   Stops output of all currently active voices.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_KillAllVoices
+   (
+   void
+   )
+
+   {
+   int i;
+   unsigned  flags;
+
+   if ( !GUSWAVE_Installed )
+      {
+      return( GUSWAVE_Ok );
+      }
+
+   if ( GUS_Debug )
+      {
+      DB_printf( "Kill All Voices\n" );
+      }
+
+   flags = DisableInterrupts();
+
+   // Remove all the voices from the list
+   for( i = 0; i < GUSWAVE_MaxVoices; i++ )
+      {
+      if ( GUSWAVE_Voices[ i ].Active )
+         {
+//         GUSWAVE_Kill( GUSWAVE_Voices[ i ].handle );
+
+         gf1_stop_digital( GUSWAVE_Voices[ i ].GF1voice );
+         }
+      }
+
+   for( i = 0; i < MAX_VOICES; i++ )
+      {
+      VoiceStatus[ i ].playing = FALSE;
+      VoiceStatus[ i ].Voice   = NULL;
+      }
+
+   VoicePool.start = NULL;
+   VoicePool.end   = NULL;
+   VoiceList.start = NULL;
+   VoiceList.end   = NULL;
+
+   for( i = 0; i < GUSWAVE_MaxVoices; i++ )
+      {
+      GUSWAVE_Voices[ i ].Active = FALSE;
+      if ( GUSWAVE_Voices[ i ].mem != NULL )
+         {
+         LL_AddToTail( VoiceNode, &VoicePool, &GUSWAVE_Voices[ i ] );
+         }
+      }
+
+   RestoreInterrupts( flags );
+
+   return( GUSWAVE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_SetPitch
+
+   Sets the pitch for the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_SetPitch
+   (
+   int handle,
+   int pitchoffset
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   voice = GUSWAVE_GetVoice( handle );
+
+   if ( voice == NULL )
+      {
+      RestoreInterrupts( flags );
+
+      GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound );
+      return( GUSWAVE_Warning );
+      }
+
+   if ( voice->Active )
+      {
+      voice->PitchScale  = PITCH_GetScale( pitchoffset );
+      voice->RateScale   = ( voice->SamplingRate * voice->PitchScale ) >> 16;
+      gf1_dig_set_freq( voice->GF1voice, voice->RateScale );
+      }
+
+   RestoreInterrupts( flags );
+
+   return( GUSWAVE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_SetPan3D
+
+   Sets the pan position of the voice with the specified handle.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_SetPan3D
+   (
+   int handle,
+   int angle,
+   int distance
+   )
+
+   {
+   VoiceNode *voice;
+   int        pan;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   voice = GUSWAVE_GetVoice( handle );
+
+   if ( voice == NULL )
+      {
+      RestoreInterrupts( flags );
+
+      GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound );
+      return( GUSWAVE_Warning );
+      }
+
+   if ( voice->Active )
+      {
+      angle &= 31;
+
+      pan = GUSWAVE_PanTable[ angle ];
+      if ( GUSWAVE_SwapLeftRight )
+         {
+         pan = 15 - pan;
+         }
+
+      distance = max( 0, distance );
+      distance = min( 255, distance );
+
+      voice->Volume = 255 - distance;
+      voice->Pan    = pan;
+
+      gf1_dig_set_pan( voice->GF1voice, pan );
+      gf1_dig_set_vol( voice->GF1voice, GUSWAVE_Volume - distance * 4 );
+      }
+
+   RestoreInterrupts( flags );
+
+   return( GUSWAVE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_SetVolume
+
+   Sets the total volume of the digitized sounds.
+---------------------------------------------------------------------*/
+
+void GUSWAVE_SetVolume
+   (
+   int volume
+   )
+
+   {
+   int i;
+
+   volume = max( 0, volume );
+   volume = min( 255, volume );
+   GUSWAVE_Volume = MAX_VOLUME - ( 255 - volume ) * 4;
+
+   for( i = 0; i < GUSWAVE_MaxVoices; i++ )
+      {
+      if ( GUSWAVE_Voices[ i ].Active )
+         {
+         gf1_dig_set_vol( GUSWAVE_Voices[ i ].GF1voice,
+            GUSWAVE_Volume - ( 255 - GUSWAVE_Voices[ i ].Volume ) * 4 );
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetVolume
+
+   Returns the total volume of the digitized sounds.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_GetVolume
+   (
+   void
+   )
+
+   {
+   return( 255 - ( ( MAX_VOLUME - GUSWAVE_Volume ) / 4 ) );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_AllocVoice
+
+   Retrieve an inactive or lower priority voice for output.
+---------------------------------------------------------------------*/
+
+static VoiceNode *GUSWAVE_AllocVoice
+   (
+   int priority
+   )
+
+   {
+   VoiceNode   *voice;
+   VoiceNode   *node;
+   unsigned    flags;
+
+   // If we don't have any free voices, check if we have a higher
+   // priority than one that is playing.
+   if ( GUSWAVE_VoicesPlaying() >= GUSWAVE_MaxVoices )
+      {
+      flags = DisableInterrupts();
+
+      node = VoiceList.start;
+      voice = node;
+      while( node != NULL )
+         {
+         if ( node->priority < voice->priority )
+            {
+            voice = node;
+            }
+
+         node = node->next;
+         }
+
+      RestoreInterrupts( flags );
+
+      if ( priority >= voice->priority )
+         {
+         GUSWAVE_Kill( voice->handle );
+         }
+      }
+
+   // Check if any voices are in the voice pool
+   flags = DisableInterrupts();
+
+   voice = VoicePool.start;
+   if ( voice != NULL )
+      {
+      LL_Remove( VoiceNode, &VoicePool, voice );
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( voice != NULL )
+      {
+      do
+         {
+         GUSWAVE_VoiceHandle++;
+         if ( GUSWAVE_VoiceHandle < GUSWAVE_MinVoiceHandle )
+            {
+            GUSWAVE_VoiceHandle = GUSWAVE_MinVoiceHandle;
+            }
+         }
+      while( GUSWAVE_VoicePlaying( GUSWAVE_VoiceHandle ) );
+
+      voice->handle = GUSWAVE_VoiceHandle;
+      }
+
+   return( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_VoiceAvailable
+
+   Checks if a voice can be play at the specified priority.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_VoiceAvailable
+   (
+   int priority
+   )
+
+   {
+   VoiceNode   *voice;
+   VoiceNode   *node;
+   unsigned    flags;
+
+   if ( GUSWAVE_VoicesPlaying() < GUSWAVE_MaxVoices )
+      {
+      return( TRUE );
+      }
+
+   flags = DisableInterrupts();
+
+   node = VoiceList.start;
+   voice = node;
+   while( node != NULL )
+      {
+      if ( node->priority < voice->priority )
+         {
+         voice = node;
+         }
+
+      node = node->next;
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( priority >= voice->priority )
+      {
+      return( TRUE );
+      }
+
+   return( FALSE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetNextVOCBlock
+
+   Interperate the information of a VOC format sound file.
+---------------------------------------------------------------------*/
+
+playbackstatus GUSWAVE_GetNextVOCBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   unsigned char *ptr;
+   int            blocktype;
+   int            lastblocktype;
+   unsigned long  blocklength;
+   unsigned long  samplespeed;
+   unsigned int   tc;
+   int            packtype;
+   int            voicemode;
+   int            done;
+   unsigned       BitsPerSample;
+   unsigned       Channels;
+   unsigned       Format;
+
+   if ( voice->BlockLength > 0 )
+      {
+      voice->sound       += MAX_BLOCK_LENGTH;
+      voice->length       = min( voice->BlockLength, MAX_BLOCK_LENGTH );
+      voice->BlockLength -= voice->length;
+      return( KeepPlaying );
+      }
+
+   ptr = ( unsigned char * )voice->NextBlock;
+
+   voice->Playing = TRUE;
+
+   voicemode = 0;
+   lastblocktype = 0;
+   packtype = 0;
+
+   done = FALSE;
+   while( !done )
+      {
+      // Stop playing if we get a NULL pointer
+      if ( ptr == NULL )
+         {
+         voice->Playing = FALSE;
+         done = TRUE;
+         break;
+         }
+
+      blocktype = ( int )*ptr;
+      blocklength = ( *( unsigned long * )( ptr + 1 ) ) & 0x00ffffff;
+      ptr += 4;
+
+      switch( blocktype )
+         {
+         case 0 :
+            // End of data
+            voice->Playing = FALSE;
+            done = TRUE;
+            break;
+
+         case 1 :
+            // Sound data block
+            voice->bits = 8;
+            if ( lastblocktype != 8 )
+               {
+               tc = ( unsigned int )*ptr << 8;
+               packtype = *( ptr + 1 );
+               }
+
+            ptr += 2;
+            blocklength -= 2;
+
+            samplespeed = 256000000L / ( 65536 - tc );
+
+            // Skip packed or stereo data
+            if ( ( packtype != 0 ) || ( voicemode != 0 ) )
+               {
+               ptr += blocklength;
+               }
+            else
+               {
+               done = TRUE;
+               }
+            voicemode = 0;
+            break;
+
+         case 2 :
+            // Sound continuation block
+            samplespeed = voice->SamplingRate;
+            done = TRUE;
+            break;
+
+         case 3 :
+            // Silence
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 4 :
+            // Marker
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 5 :
+            // ASCII string
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 6 :
+            // Repeat begin
+            voice->LoopCount = *( unsigned short * )ptr;
+            ptr += blocklength;
+            voice->LoopStart = ptr;
+            break;
+
+         case 7 :
+            // Repeat end
+            ptr += blocklength;
+            if ( lastblocktype == 6 )
+               {
+               voice->LoopCount = 0;
+               }
+            else
+               {
+               if ( ( voice->LoopCount > 0 ) && ( voice->LoopStart != NULL ) )
+                  {
+                  ptr = voice->LoopStart;
+                  if ( voice->LoopCount < 0xffff )
+                     {
+                     voice->LoopCount--;
+                     if ( voice->LoopCount == 0 )
+                        {
+                        voice->LoopStart = NULL;
+                        }
+                     }
+                  }
+               }
+            break;
+
+         case 8 :
+            // Extended block
+            voice->bits = 8;
+            tc = *( unsigned short * )ptr;
+            packtype = *( ptr + 2 );
+            voicemode = *( ptr + 3 );
+            ptr += blocklength;
+            break;
+
+         case 9 :
+            // New sound data block
+            samplespeed = *( unsigned long * )ptr;
+            BitsPerSample = ( unsigned )*( ptr + 4 );
+            Channels = ( unsigned )*( ptr + 5 );
+            Format = ( unsigned )*( unsigned short * )( ptr + 6 );
+
+            if ( ( BitsPerSample == 8 ) && ( Channels == 1 ) &&
+               ( Format == VOC_8BIT ) )
+               {
+               ptr += 12;
+               blocklength -= 12;
+               voice->bits  = 8;
+               done = TRUE;
+               }
+            else if ( ( BitsPerSample == 16 ) && ( Channels == 1 ) &&
+               ( Format == VOC_16BIT ) )
+               {
+               ptr         += 12;
+               blocklength -= 12;
+               voice->bits  = 16;
+               done         = TRUE;
+               }
+            else
+               {
+               ptr += blocklength;
+               }
+            break;
+
+         default :
+            // Unknown data.  Probably not a VOC file.
+            voice->Playing = FALSE;
+            done = TRUE;
+            break;
+         }
+
+      lastblocktype = blocktype;
+      }
+
+   if ( voice->Playing )
+      {
+      voice->NextBlock    = ptr + blocklength;
+      voice->sound        = ptr;
+
+      voice->SamplingRate = samplespeed;
+      voice->RateScale    = ( voice->SamplingRate * voice->PitchScale ) >> 16;
+
+      voice->length       = min( blocklength, MAX_BLOCK_LENGTH );
+      voice->BlockLength  = blocklength - voice->length;
+
+      return( KeepPlaying );
+      }
+
+   return( SoundDone );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetNextWAVBlock
+
+   Controls playback of demand fed data.
+---------------------------------------------------------------------*/
+
+playbackstatus GUSWAVE_GetNextWAVBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   if ( voice->BlockLength <= 0 )
+      {
+      if ( voice->LoopStart == NULL )
+         {
+         voice->Playing = FALSE;
+         return( SoundDone );
+         }
+
+      voice->BlockLength = voice->LoopSize;
+      voice->NextBlock   = voice->LoopStart;
+      voice->length      = 0;
+      }
+
+   voice->sound        = voice->NextBlock;
+   voice->length       = min( voice->BlockLength, 0x8000 );
+   voice->NextBlock   += voice->length;
+   voice->BlockLength -= voice->length;
+
+   return( KeepPlaying );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetNextDemandFeedBlock
+
+   Controls playback of demand fed data.
+---------------------------------------------------------------------*/
+
+playbackstatus GUSWAVE_GetNextDemandFeedBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   if ( voice->BlockLength > 0 )
+      {
+      voice->sound       += voice->length;
+      voice->length       = min( voice->BlockLength, 0x8000 );
+      voice->BlockLength -= voice->length;
+
+      return( KeepPlaying );
+      }
+
+   if ( voice->DemandFeed == NULL )
+      {
+      return( SoundDone );
+      }
+
+   ( voice->DemandFeed )( &voice->sound, &voice->BlockLength );
+//   voice->sound = GUS_Silence16;
+//   voice->BlockLength = 256;
+
+   voice->length       = min( voice->BlockLength, 0x8000 );
+   voice->BlockLength -= voice->length;
+
+   if ( ( voice->length > 0 ) && ( voice->sound != NULL ) )
+      {
+      return( KeepPlaying );
+      }
+   return( NoMoreData );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_Play
+
+   Begins playback of digitized sound.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_Play
+   (
+   VoiceNode *voice,
+   int angle,
+   int volume,
+   int channels
+   )
+
+   {
+   int      VoiceNumber;
+   int      type;
+   int      pan;
+   unsigned flags;
+   int ( *servicefunction )( int reason, int voice, unsigned char **buf, unsigned long *size );
+
+   type = 0;
+   if ( channels != 1 )
+      {
+      type |= TYPE_STEREO;
+      }
+
+   if ( voice->bits == 8 )
+      {
+      type |= TYPE_8BIT;
+      type |= TYPE_INVERT_MSB;
+      }
+
+   voice->GF1voice  = -1;
+
+   angle &= 31;
+   pan = GUSWAVE_PanTable[ angle ];
+   if ( GUSWAVE_SwapLeftRight )
+      {
+      pan = 15 - pan;
+      }
+
+   voice->Pan = pan;
+
+   volume = max( 0, volume );
+   volume = min( 255, volume );
+   voice->Volume = volume;
+
+   if ( !GUS_Debug )
+      {
+      servicefunction = GUSWAVE_CallBack;
+      }
+   else
+      {
+      servicefunction = GUSWAVE_DebugCallBack;
+      }
+
+   VoiceNumber = gf1_play_digital( 0, voice->sound, voice->length,
+      voice->mem, GUSWAVE_Volume - ( 255 - volume ) * 4, pan,
+      voice->RateScale, type, &GUS_HoldBuffer, servicefunction );
+
+   if ( VoiceNumber == NO_MORE_VOICES )
+      {
+      if ( GUS_Debug )
+         {
+         DB_printf( "Out of voices.\n" );
+         }
+
+      flags = DisableInterrupts();
+      LL_AddToTail( VoiceNode, &VoicePool, voice );
+      RestoreInterrupts( flags );
+
+      GUSWAVE_SetErrorCode( GUSWAVE_NoVoices );
+      return( GUSWAVE_Warning );
+      }
+
+   flags = DisableInterrupts();
+   voice->GF1voice = VoiceNumber;
+   voice->Active   = TRUE;
+   LL_AddToTail( VoiceNode, &VoiceList, voice );
+   VoiceStatus[ VoiceNumber ].playing = TRUE;
+   VoiceStatus[ VoiceNumber ].Voice   = voice;
+
+   if ( GUS_Debug )
+      {
+      DB_printf( "GUS voice %d playing\n", VoiceNumber );
+      }
+
+   RestoreInterrupts( flags );
+
+   return( voice->handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_PlayVOC
+
+   Begins playback of digitized sound.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_PlayVOC
+   (
+   char *sample,
+   int   pitchoffset,
+   int   angle,
+   int   volume,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   int handle;
+   int status;
+   playbackstatus soundstatus;
+   VoiceNode *voice;
+   unsigned flags;
+
+   // Make sure it's a valid VOC file.
+   status = strncmp( sample, "Creative Voice File", 19 );
+   if ( status != 0 )
+      {
+      // Tell multivoc that we had a bad VOC file
+      MV_ErrorCode = MV_InvalidVOCFile;
+
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidVOCFile );
+      return( GUSWAVE_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = GUSWAVE_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      if ( GUS_Debug )
+         {
+         DB_printf( "No more voices.  Skipping sound.\n" );
+         }
+      GUSWAVE_SetErrorCode( GUSWAVE_NoVoices );
+      return( GUSWAVE_Warning );
+      }
+
+   voice->NextBlock   = sample + *( unsigned short int * )( sample + 0x14 );
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->BlockLength = 0;
+   voice->PitchScale  = PITCH_GetScale( pitchoffset );
+   voice->wavetype    = VOC;
+   voice->bits        = 8;
+   voice->GetSound    = GUSWAVE_GetNextVOCBlock;
+   voice->length      = 0;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->callbackval = callbackval;
+
+   soundstatus = GUSWAVE_GetNextVOCBlock( voice );
+   if ( soundstatus == SoundDone )
+      {
+      flags = DisableInterrupts();
+      LL_AddToTail( VoiceNode, &VoicePool, voice );
+      RestoreInterrupts( flags );
+
+      if ( GUS_Debug )
+         {
+         DB_printf( "Voice ended before playback.\n" );
+         }
+
+      // Tell multivoc that we had a bad VOC file
+      MV_ErrorCode = MV_InvalidVOCFile;
+
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidVOCFile );
+      return( GUSWAVE_Error );
+      }
+
+   handle = GUSWAVE_Play( voice, angle, volume, 1 );
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_PlayWAV
+
+   Begins playback of digitized sound.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_PlayWAV
+   (
+   char *sample,
+   int   pitchoffset,
+   int   angle,
+   int   volume,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   VoiceNode *voice;
+   int        handle;
+   int        channels;
+   int        bits;
+   int        length;
+   riff_header   *riff;
+   format_header *format;
+   data_header   *data;
+
+   riff = ( riff_header * )sample;
+
+   if ( ( strncmp( riff->RIFF, "RIFF", 4 ) != 0 ) ||
+      ( strncmp( riff->WAVE, "WAVE", 4 ) != 0 ) ||
+      ( strncmp( riff->fmt, "fmt ", 4) != 0 ) )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile );
+      return( GUSWAVE_Error );
+      }
+
+   format = ( format_header * )( riff + 1 );
+   data   = ( data_header * )( ( ( char * )format ) + riff->format_size );
+
+   if ( format->wFormatTag != 1 )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile );
+      return( GUSWAVE_Error );
+      }
+
+   channels = format->nChannels;
+   if ( ( channels != 1 ) && ( channels != 2 ) )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile );
+      return( GUSWAVE_Error );
+      }
+
+   bits = format->nBitsPerSample;
+   if ( ( bits != 8 ) && ( bits != 16 ) )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile );
+      return( GUSWAVE_Error );
+      }
+
+   if ( strncmp( data->DATA, "data", 4 ) != 0 )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile );
+      return( GUSWAVE_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = GUSWAVE_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      if ( GUS_Debug )
+         {
+         DB_printf( "No more voices.  Skipping sound.\n" );
+         }
+      GUSWAVE_SetErrorCode( GUSWAVE_NoVoices );
+      return( GUSWAVE_Warning );
+      }
+
+   voice->wavetype    = WAV;
+   voice->bits        = bits;
+   voice->GetSound    = GUSWAVE_GetNextWAVBlock;
+
+   length = data->size;
+
+   voice->Playing     = TRUE;
+   voice->DemandFeed  = NULL;
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->length      = min( length, 0x8000 );
+   voice->BlockLength = length - voice->length;//  min( loopend + 1, data->size );
+   voice->sound       = ( char * )( data + 1 );
+   voice->NextBlock   = voice->sound + voice->length;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->callbackval = callbackval;
+   voice->LoopStart   = NULL;// voice->NextBlock + loopstart;
+   voice->LoopEnd     = NULL;//voice->NextBlock + min( loopend, data->size - 1 );
+   voice->LoopSize    = 0;//( voice->LoopEnd - voice->LoopStart ) + 1;
+   voice->PitchScale  = PITCH_GetScale( pitchoffset );
+   voice->SamplingRate = format->nSamplesPerSec;
+   voice->RateScale   = ( voice->SamplingRate * voice->PitchScale ) >> 16;
+
+   handle = GUSWAVE_Play( voice, angle, volume, channels );
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_StartDemandFeedPlayback
+
+   Begins playback of digitized sound.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_StartDemandFeedPlayback
+   (
+   void ( *function )( char **ptr, unsigned long *length ),
+   int   channels,
+   int   bits,
+   int   rate,
+   int   pitchoffset,
+   int   angle,
+   int   volume,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   VoiceNode *voice;
+   int        handle;
+
+   // Request a voice from the voice pool
+   voice = GUSWAVE_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      if ( GUS_Debug )
+         {
+         DB_printf( "No more voices.  Skipping sound.\n" );
+         }
+      GUSWAVE_SetErrorCode( GUSWAVE_NoVoices );
+      return( GUSWAVE_Warning );
+      }
+
+   voice->wavetype    = DemandFeed;
+   voice->bits        = bits;
+   voice->GetSound    = GUSWAVE_GetNextDemandFeedBlock;
+   voice->Playing     = TRUE;
+   voice->DemandFeed  = function;
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->BlockLength = 0;
+   voice->length      = 256;
+   voice->sound       = ( bits == 8 ) ? GUS_Silence8 : ( char * )GUS_Silence16;
+   voice->NextBlock   = NULL;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->callbackval = callbackval;
+   voice->PitchScale  = PITCH_GetScale( pitchoffset );
+   voice->SamplingRate = rate;
+   voice->RateScale   = ( voice->SamplingRate * voice->PitchScale ) >> 16;
+
+   handle = GUSWAVE_Play( voice, angle, volume, channels );
+
+   return( handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_SetReverseStereo
+
+   Set the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+void GUSWAVE_SetReverseStereo
+   (
+   int setting
+   )
+
+   {
+   GUSWAVE_SwapLeftRight = setting;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_GetReverseStereo
+
+   Returns the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_GetReverseStereo
+   (
+   void
+   )
+
+   {
+   return( GUSWAVE_SwapLeftRight );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_InitVoices
+
+   Begins playback of digitized sound.
+---------------------------------------------------------------------*/
+
+static int GUSWAVE_InitVoices
+   (
+   void
+   )
+
+   {
+   int i;
+
+   for( i = 0; i < MAX_VOICES; i++ )
+      {
+      VoiceStatus[ i ].playing = FALSE;
+      VoiceStatus[ i ].Voice   = NULL;
+      }
+
+   VoicePool.start = NULL;
+   VoicePool.end   = NULL;
+   VoiceList.start = NULL;
+   VoiceList.end   = NULL;
+
+   for( i = 0; i < VOICES; i++ )
+      {
+      GUSWAVE_Voices[ i ].num      = -1;
+      GUSWAVE_Voices[ i ].Active   = FALSE;
+      GUSWAVE_Voices[ i ].GF1voice = -1;
+      GUSWAVE_Voices[ i ].mem      = NULL;
+      }
+
+   for( i = 0; i < VOICES; i++ )
+      {
+      GUSWAVE_Voices[ i ].num      = i;
+      GUSWAVE_Voices[ i ].Active   = FALSE;
+      GUSWAVE_Voices[ i ].GF1voice = 0;
+
+      GUSWAVE_Voices[ i ].mem = gf1_malloc( GF1BSIZE );
+      if ( GUSWAVE_Voices[ i ].mem == NULL )
+         {
+         GUSWAVE_MaxVoices = i;
+         if ( i < 1 )
+            {
+            if ( GUSMIDI_Installed )
+               {
+               GUSWAVE_SetErrorCode( GUSWAVE_UltraNoMemMIDI );
+               }
+            else
+               {
+               GUSWAVE_SetErrorCode( GUSWAVE_UltraNoMem );
+               }
+            return( GUSWAVE_Error );
+            }
+
+         break;
+         }
+
+      LL_AddToTail( VoiceNode, &VoicePool, &GUSWAVE_Voices[ i ] );
+      }
+
+   return( GUSWAVE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_SetCallBack
+
+   Set the function to call when a voice stops.
+---------------------------------------------------------------------*/
+
+void GUSWAVE_SetCallBack
+   (
+   void ( *function )( unsigned long )
+   )
+
+   {
+   GUSWAVE_CallBackFunc = function;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: GUSWAVE_Init
+
+   Initializes the Gravis Ultrasound for digitized sound playback.
+---------------------------------------------------------------------*/
+
+int GUSWAVE_Init
+   (
+   int numvoices
+   )
+
+   {
+   int status;
+
+   if ( GUSWAVE_Installed )
+      {
+      GUSWAVE_Shutdown();
+      }
+
+   GUSWAVE_SetErrorCode( GUSWAVE_Ok );
+
+   status = GUS_Init();
+   if ( status != GUS_Ok )
+      {
+      GUSWAVE_SetErrorCode( GUSWAVE_GUSError );
+      return( GUSWAVE_Error );
+      }
+
+   GUS_Debug = USER_CheckParameter( "DEBUGGUS" );
+
+   GUSWAVE_MaxVoices = min( numvoices, VOICES );
+   GUSWAVE_MaxVoices = max( GUSWAVE_MaxVoices, 0 );
+
+   status = GUSWAVE_InitVoices();
+   if ( status != GUSWAVE_Ok )
+      {
+      GUS_Shutdown();
+      return( status );
+      }
+
+   GUSWAVE_SetReverseStereo( FALSE );
+
+   GUSWAVE_CallBackFunc = NULL;
+   GUSWAVE_Installed = TRUE;
+
+   return( GUSWAVE_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/guswave.h
@@ -1,0 +1,75 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: GUSWAVE.H
+
+   author: James R. Dose
+   date:   March 23, 1994
+
+   Public header for for GUSWAVE.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __GUSWAVE_H
+#define __GUSWAVE_H
+
+#define GUSWAVE_MinVoiceHandle 1
+
+enum GUSWAVE_Errors
+   {
+   GUSWAVE_Warning  = -2,
+   GUSWAVE_Error    = -1,
+   GUSWAVE_Ok       = 0,
+   GUSWAVE_GUSError,
+   GUSWAVE_NotInstalled,
+   GUSWAVE_NoVoices,
+   GUSWAVE_UltraNoMem,
+   GUSWAVE_UltraNoMemMIDI,
+   GUSWAVE_VoiceNotFound,
+   GUSWAVE_InvalidVOCFile,
+   GUSWAVE_InvalidWAVFile
+   };
+
+char *GUSWAVE_ErrorString( int ErrorNumber );
+int   GUSWAVE_VoicePlaying( int handle );
+int   GUSWAVE_VoicesPlaying( void );
+int   GUSWAVE_Kill( int handle );
+int   GUSWAVE_KillAllVoices( void );
+int   GUSWAVE_SetPitch( int handle, int pitchoffset );
+int   GUSWAVE_SetPan3D( int handle, int angle, int distance );
+void  GUSWAVE_SetVolume( int volume );
+int   GUSWAVE_GetVolume( void );
+int   GUSWAVE_VoiceAvailable( int priority );
+int   GUSWAVE_PlayVOC( char *sample, int pitchoffset, int angle, int volume,
+         int priority, unsigned long callbackval );
+int   GUSWAVE_PlayWAV( char *sample, int pitchoffset, int angle, int volume,
+         int priority, unsigned long callbackval );
+int   GUSWAVE_StartDemandFeedPlayback( void ( *function )( char **ptr, unsigned long *length ),
+         int channels, int bits, int rate, int pitchoffset, int angle,
+         int volume, int priority, unsigned long callbackval );
+void  GUSWAVE_SetCallBack( void ( *function )( unsigned long ) );
+void  GUSWAVE_SetReverseStereo( int setting );
+int   GUSWAVE_GetReverseStereo( void );
+int   GUSWAVE_Init( int numvoices );
+void  GUSWAVE_Shutdown( void );
+#pragma aux GUSWAVE_Shutdown frame;
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/interrup.h
@@ -1,0 +1,50 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: INTERRUP.H
+
+   author: James R. Dose
+   date:   March 31, 1994
+
+   Inline functions for disabling and restoring the interrupt flag.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __INTERRUPT_H
+#define __INTERRUPT_H
+
+unsigned long DisableInterrupts( void );
+void          RestoreInterrupts( unsigned long flags );
+
+#ifdef PLAT_DOS
+#pragma aux DisableInterrupts = \
+   "pushfd",                    \
+   "pop    eax",                \
+   "cli"                        \
+   modify [ eax ];
+
+#pragma aux RestoreInterrupts = \
+   "push   eax",                \
+   "popfd"                      \
+   parm [ eax ];
+#endif
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/irq.c
@@ -1,0 +1,325 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: IRQ.C
+
+   author: James R. Dose
+   date:   August 26, 1994
+
+   Low level routines to set and restore IRQ's through DPMI.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <stdlib.h>
+#include "irq.h"
+
+#define D32RealSeg(P) ( ( ( ( unsigned long )( P ) ) >> 4 ) & 0xFFFF )
+#define D32RealOff(P) ( ( ( unsigned long )( P ) ) & 0xF )
+
+typedef struct
+   {
+   unsigned long  drdi;
+   unsigned long  drsi;
+   unsigned long  drbp;
+   unsigned long  drxx;
+   unsigned long  drbx;
+   unsigned long  drdx;
+   unsigned long  drcx;
+   unsigned long  drax;
+
+   unsigned short drflags;
+   unsigned short dres;
+   unsigned short drds;
+   unsigned short drfs;
+   unsigned short drgs;
+   unsigned short drip;
+   unsigned short drcs;
+   unsigned short drsp;
+   unsigned short drss;
+   } DPMI_REGS;
+
+static DPMI_REGS rmregs = { 0 };
+static void ( __interrupt __far *IRQ_Callback )( void ) = NULL;
+
+static char *IRQ_RealModeCode = NULL;
+
+static unsigned short IRQ_CallBackSegment;
+static unsigned short IRQ_CallBackOffset;
+static unsigned short IRQ_RealModeSegment;
+static unsigned short IRQ_RealModeOffset;
+static unsigned long  IRQ_ProtectedModeOffset;
+static unsigned short IRQ_ProtectedModeSelector;
+
+static union  REGS  Regs;
+static struct SREGS SegRegs;
+
+static void *D32DosMemAlloc
+   (
+   unsigned long size
+   )
+
+   {
+   // DPMI allocate DOS memory
+   Regs.x.eax = 0x0100;
+
+   // Number of paragraphs requested
+   Regs.x.ebx = ( size + 15 ) >> 4;
+
+   int386( 0x31, &Regs, &Regs );
+
+   if ( Regs.x.cflag != 0 )
+      {
+      // Failed
+      return ( ( unsigned long )0 );
+      }
+
+   return( ( void * )( ( Regs.x.eax & 0xFFFF ) << 4  ) );
+   }
+
+// Intermediary function: DPMI calls this, making it
+// easier to write in C
+// handle 16-bit incoming stack
+
+void fixebp
+   (
+   void
+   );
+
+#pragma aux fixebp = \
+        "mov   bx,  ss" \
+        "lar   ebx, ebx" \
+        "bt    ebx, 22" \
+        "jc    bigstk" \
+        "movzx esp, sp" \
+        "mov   ebp, esp" \
+        "bigstk:" \
+modify exact [ ebx ];
+
+#pragma aux rmcallback parm [];
+
+void rmcallback
+   (
+   unsigned short _far *stkp
+   )
+
+   {
+   // "Pop" the real mode return frame so we
+   // can resume where we left off
+   rmregs.drip = *stkp++;
+   rmregs.drcs = *stkp++;
+
+   rmregs.drsp = FP_OFF(stkp);
+
+   // Call protected-mode handler
+   IRQ_Callback();
+   }
+
+static void _interrupt _cdecl callback_x
+   (
+   // regs pushed in this order by prologue
+
+   int rgs,
+   int rfs,
+   int res,
+   int rds,
+   int rdi,
+   int rsi,
+   int rbp,
+   int rsp,
+   int rbx,
+   int rdx,
+   int rcx,
+   int rax
+   )
+
+   {
+//   unsigned short _far *stkp;
+//   return;
+
+   fixebp();
+   rmcallback (MK_FP(rds, rsi));
+   }
+
+/*
+static void _interrupt _cdecl callback_x
+   (
+   // regs pushed in this order by prologue
+
+   int rgs,
+   int rfs,
+   int res,
+   int rds,
+   int rdi,
+   int rsi,
+   int rbp,
+   int rsp,
+   int rbx,
+   int rdx,
+   int rcx,
+   int rax
+   )
+
+   {
+   unsigned short _far *stkp;
+
+   fixebp();
+   stkp = MK_FP(rds, rsi);
+
+   // "Pop" the real mode return frame so we
+   // can resume where we left off
+   rmregs.drip = *stkp++;
+   rmregs.drcs = *stkp++;
+
+   rmregs.drsp = FP_OFF(stkp);
+
+   // Call protected-mode handler
+   IRQ_Callback();
+   }
+*/
+
+
+int IRQ_SetVector
+   (
+   int vector,
+   void ( __interrupt __far *function )( void )
+   )
+
+   {
+   void far *fp;
+
+   IRQ_Callback = function;
+
+   // Save the starting real-mode and protected-mode handler addresses
+
+   // DPMI get protected mode vector */
+   Regs.w.ax = 0x0204;
+   Regs.w.bx = vector;
+   int386( 0x31, &Regs, &Regs );
+   IRQ_ProtectedModeSelector = Regs.w.cx;
+   IRQ_ProtectedModeOffset = Regs.x.edx;
+
+   // DPMI get real mode vector
+   Regs.w.ax = 0x0200;
+   Regs.w.bx = vector;
+   int386( 0x31, &Regs, &Regs );
+   IRQ_RealModeSegment = Regs.w.cx;
+   IRQ_RealModeOffset = Regs.w.dx;
+
+   // Set up callback
+   // DPMI allocate real mode callback
+   Regs.w.ax = 0x0303;
+   fp = ( void far * )callback_x;
+   SegRegs.ds   = FP_SEG( fp );
+   Regs.x.esi = FP_OFF( fp );
+   fp      = ( void _far * )&rmregs;
+   SegRegs.es   = FP_SEG( fp );
+   Regs.x.edi = FP_OFF( fp );
+   int386x( 0x31, &Regs, &Regs, &SegRegs );
+
+   IRQ_CallBackSegment = Regs.w.cx;
+   IRQ_CallBackOffset = Regs.w.dx;
+
+   if ( Regs.x.cflag != 0 )
+      {
+      return( IRQ_Error );
+      }
+
+   if ( IRQ_RealModeCode == NULL )
+      {
+      // Allocate 6 bytes of low memory for real mode interrupt handler
+      IRQ_RealModeCode = D32DosMemAlloc( 6 );
+      if ( IRQ_RealModeCode == NULL )
+         {
+         // Free callback
+         Regs.w.ax = 0x304;
+         Regs.w.cx = IRQ_CallBackSegment;
+         Regs.w.dx = IRQ_CallBackOffset;
+         int386x( 0x31, &Regs, &Regs, &SegRegs );
+
+         return( IRQ_Error );
+         }
+      }
+
+   // Poke code (to call callback) into real mode handler
+
+   // CALL FAR PTR (callback)
+   IRQ_RealModeCode[ 0 ] = '\x9A';
+   *( ( unsigned short * )&IRQ_RealModeCode[ 1 ] ) = IRQ_CallBackOffset;
+   *( ( unsigned short * )&IRQ_RealModeCode[ 3 ] ) = IRQ_CallBackSegment;
+
+   // IRET
+   IRQ_RealModeCode[ 5 ] = '\xCF';
+
+   // Install protected mode handler
+   // DPMI set protected mode vector
+   Regs.w.ax = 0x0205;
+   Regs.w.bx = vector;
+   fp = function;
+
+   Regs.w.cx  = FP_SEG( fp );
+   Regs.x.edx = FP_OFF( fp );
+   int386( 0x31, &Regs, &Regs );
+
+   // Install callback address as real mode handler
+   // DPMI set real mode vector
+   Regs.w.ax = 0x0201;
+   Regs.w.bx = vector;
+   Regs.w.cx = D32RealSeg( IRQ_RealModeCode );
+   Regs.w.dx = D32RealOff( IRQ_RealModeCode );
+   int386( 0x31, &Regs, &Regs );
+
+   return( IRQ_Ok );
+   }
+
+int IRQ_RestoreVector
+   (
+   int vector
+   )
+
+   {
+   // Restore original interrupt handlers
+   // DPMI set real mode vector
+   Regs.w.ax = 0x0201;
+   Regs.w.bx = vector;
+   Regs.w.cx = IRQ_RealModeSegment;
+   Regs.w.dx = IRQ_RealModeOffset;
+   int386( 0x31, &Regs, &Regs );
+
+   Regs.w.ax = 0x0205;
+   Regs.w.bx = vector;
+   Regs.w.cx = IRQ_ProtectedModeSelector;
+   Regs.x.edx = IRQ_ProtectedModeOffset;
+   int386( 0x31, &Regs, &Regs );
+
+   // Free callback
+   Regs.w.ax = 0x304;
+   Regs.w.cx = IRQ_CallBackSegment;
+   Regs.w.dx = IRQ_CallBackOffset;
+   int386x( 0x31, &Regs, &Regs, &SegRegs );
+
+   if ( Regs.x.cflag )
+      {
+      return( IRQ_Error );
+      }
+
+   return( IRQ_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/irq.h
@@ -1,0 +1,54 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: IRQ.H
+
+   author: James R. Dose
+   date:   August 8, 1994
+
+   Public header for IRQ.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __IRQ_H
+#define __IRQ_H
+
+enum IRQ_ERRORS
+   {
+   IRQ_Warning = -2,
+   IRQ_Error = -1,
+   IRQ_Ok = 0,
+   };
+
+#define VALID_IRQ( irq )  ( ( ( irq ) >= 0 ) && ( ( irq ) <= 15 ) )
+
+int IRQ_SetVector
+   (
+   int vector,
+   void ( __interrupt *function )( void )
+   );
+int IRQ_RestoreVector
+   (
+   int vector
+   );
+
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/leetimbr.c
@@ -1,0 +1,290 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+typedef struct
+   {
+   unsigned char SAVEK[ 2 ];
+   unsigned char Level[ 2 ];
+   unsigned char Env1[ 2 ];
+   unsigned char Env2[ 2 ];
+   unsigned char Wave[ 2 ];
+   unsigned char Feedback;
+   signed   char Transpose;
+   signed   char Velocity;
+   } TIMBRE;
+
+TIMBRE ADLIB_TimbreBank[ 256 ] =
+   {
+      { { 33, 49 }, { 79, 0 }, { 242, 210 }, { 82, 115 }, { 0, 0 }, 6, 0 },
+      { { 19, 17 }, { 198, 10 }, { 242, 241 }, { 245, 245 }, { 1, 0 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 3, 18 }, { 48, 16 }, { 246, 242 }, { 25, 244 }, { 0, 0 }, 15, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 24, 129 }, { 98, 0 }, { 243, 242 }, { 230, 246 }, { 0, 0 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 23, 2 }, { 79, 16 }, { 242, 242 }, { 96, 114 }, { 0, 0 }, 8, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 178, 176 }, { 192, 134 }, { 159, 148 }, { 6, 15 }, { 1, 1 }, 9, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 178, 176 }, { 192, 128 }, { 159, 148 }, { 6, 15 }, { 1, 1 }, 9, 0 },
+      { { 130, 128 }, { 192, 134 }, { 145, 145 }, { 246, 246 }, { 1, 1 }, 9, 0 },
+      { { 36, 49 }, { 79, 27 }, { 242, 82 }, { 11, 11 }, { 0, 0 }, 14, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 35, 33 }, { 72, 0 }, { 149, 132 }, { 25, 25 }, { 1, 0 }, 8, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 19, 49 }, { 150, 128 }, { 254, 242 }, { 33, 148 }, { 0, 0 }, 10, 0 },
+      { { 1, 17 }, { 77, 0 }, { 242, 245 }, { 83, 116 }, { 1, 1 }, 8, 0 },
+      { { 33, 40 }, { 1, 9 }, { 148, 148 }, { 25, 9 }, { 0, 0 }, 6, 0 },
+      { { 33, 40 }, { 1, 19 }, { 148, 148 }, { 25, 9 }, { 0, 0 }, 6, 0 },
+      { { 36, 194 }, { 138, 3 }, { 250, 145 }, { 111, 248 }, { 0, 0 }, 8, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 33 }, { 26, 0 }, { 241, 246 }, { 207, 72 }, { 0, 0 }, 10, 0 },
+      { { 1, 34 }, { 29, 0 }, { 183, 196 }, { 34, 55 }, { 0, 0 }, 14, 0 },
+      { { 49, 34 }, { 30, 0 }, { 242, 241 }, { 239, 104 }, { 0, 0 }, 14, 0 },
+      { { 49, 34 }, { 30, 0 }, { 242, 245 }, { 239, 120 }, { 0, 0 }, 14, 0 },
+      { { 49, 34 }, { 30, 0 }, { 242, 245 }, { 239, 120 }, { 0, 0 }, 14, 0 },
+      { { 17, 49 }, { 5, 9 }, { 249, 241 }, { 37, 52 }, { 0, 0 }, 10, 0 },
+      { { 17, 49 }, { 5, 0 }, { 249, 241 }, { 37, 52 }, { 0, 0 }, 10, 0 },
+      { { 49, 114 }, { 138, 0 }, { 213, 97 }, { 25, 27 }, { 0, 0 }, 12, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 17, 17 }, { 150, 128 }, { 165, 245 }, { 85, 179 }, { 2, 1 }, 12, 0 },
+      { { 1, 17 }, { 156, 128 }, { 128, 240 }, { 5, 6 }, { 0, 0 }, 0, 0 },
+      { { 17, 17 }, { 150, 128 }, { 165, 245 }, { 85, 179 }, { 2, 1 }, 12, 0 },
+      { { 2, 1 }, { 153, 128 }, { 245, 246 }, { 85, 83 }, { 0, 0 }, 0, 0 },
+      { { 192, 0 }, { 13, 0 }, { 165, 212 }, { 67, 35 }, { 2, 1 }, 0, 0 },
+      { { 33, 32 }, { 88, 0 }, { 194, 97 }, { 227, 22 }, { 1, 3 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 34 }, { 22, 0 }, { 176, 179 }, { 129, 44 }, { 0, 1 }, 12, 0 },
+      { { 49, 114 }, { 91, 131 }, { 244, 138 }, { 21, 5 }, { 0, 0 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 33 }, { 29, 0 }, { 113, 129 }, { 174, 158 }, { 0, 0 }, 14, 0 },
+      { { 49, 97 }, { 28, 128 }, { 65, 146 }, { 11, 59 }, { 0, 0 }, 14, 0 },
+      { { 49, 241 }, { 28, 0 }, { 65, 146 }, { 11, 27 }, { 0, 0 }, 10, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 33 }, { 155, 0 }, { 97, 127 }, { 106, 10 }, { 0, 0 }, 2, 0 },
+      { { 225, 226 }, { 21, 3 }, { 113, 129 }, { 174, 158 }, { 0, 0 }, 14, 0 },
+      { { 33, 33 }, { 29, 0 }, { 113, 129 }, { 174, 158 }, { 0, 0 }, 14, 0 },
+      { { 33, 33 }, { 77, 0 }, { 84, 166 }, { 60, 28 }, { 0, 0 }, 8, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 50 }, { 79, 0 }, { 113, 82 }, { 83, 76 }, { 0, 0 }, 10, 0 },
+      { { 33, 50 }, { 79, 0 }, { 113, 82 }, { 83, 76 }, { 0, 0 }, 10, 0 },
+      { { 32, 49 }, { 78, 0 }, { 113, 82 }, { 104, 94 }, { 0, 0 }, 10, 0 },
+      { { 33, 33 }, { 75, 0 }, { 170, 143 }, { 22, 10 }, { 1, 0 }, 8, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 50, 97 }, { 154, 130 }, { 81, 162 }, { 27, 59 }, { 0, 0 }, 12, 0 },
+      { { 50, 33 }, { 192, 0 }, { 155, 114 }, { 33, 7 }, { 0, 0 }, 4, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 162 }, { 131, 141 }, { 116, 101 }, { 23, 23 }, { 0, 0 }, 7, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 225, 98 }, { 236, 0 }, { 110, 101 }, { 143, 42 }, { 0, 0 }, 14, 0 },
+      { { 50, 33 }, { 144, 0 }, { 155, 114 }, { 33, 23 }, { 0, 0 }, 4, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 245, 242 }, { 154, 128 }, { 12, 96 }, { 199, 165 }, { 0, 0 }, 13, 0 },
+      { { 98, 161 }, { 147, 0 }, { 119, 118 }, { 7, 7 }, { 0, 0 }, 11, 0 },
+      { { 34, 33 }, { 89, 8 }, { 255, 255 }, { 3, 15 }, { 2, 0 }, 0, 0 },
+      { { 33, 33 }, { 29, 0 }, { 113, 129 }, { 14, 14 }, { 0, 0 }, 14, 0 },
+      { { 34, 33 }, { 70, 128 }, { 134, 100 }, { 85, 24 }, { 0, 0 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 113, 114 }, { 93, 0 }, { 84, 106 }, { 1, 3 }, { 0, 0 }, 0, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 0, 17 }, { 13, 128 }, { 241, 80 }, { 255, 255 }, { 0, 0 }, 6, 0 },
+      { { 33, 97 }, { 137, 3 }, { 17, 66 }, { 51, 37 }, { 0, 0 }, 10, 0 },
+      { { 0, 49 }, { 16, 128 }, { 17, 176 }, { 239, 15 }, { 0, 0 }, 10, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 164, 97 }, { 76, 16 }, { 243, 129 }, { 115, 35 }, { 1, 0 }, 4, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 1, 1 }, { 0, 0 }, { 255, 255 }, { 7, 7 }, { 0, 0 }, 7, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 1, 221 }, { 0, 0 }, { 246, 31 }, { 0, 6 }, { 2, 3 }, 12, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 36 },
+      { { 16, 17 }, { 68, 0 }, { 248, 243 }, { 119, 6 }, { 2, 0 }, 8, 36 },
+      { { 3, 15 }, { 0, 0 }, { 251, 245 }, { 43, 11 }, { 2, 0 }, 15, 36 },
+      { { 33, 0 }, { 128, 0 }, { 255, 249 }, { 7, 7 }, { 0, 1 }, 14, 36 },
+      { { 240, 229 }, { 192, 0 }, { 255, 251 }, { 255, 240 }, { 3, 0 }, 14, 48 },
+      { { 33, 0 }, { 128, 0 }, { 255, 248 }, { 10, 25 }, { 0, 1 }, 14, 36 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 48 },
+      { { 12, 18 }, { 0, 0 }, { 246, 251 }, { 8, 71 }, { 0, 2 }, 10, 69 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 52 },
+      { { 14, 0 }, { 64, 8 }, { 118, 119 }, { 79, 24 }, { 0, 2 }, 14, 48 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 55 },
+      { { 2, 5 }, { 3, 10 }, { 180, 151 }, { 4, 247 }, { 0, 0 }, 14, 57 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 58 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 60 },
+      { { 1, 221 }, { 12, 0 }, { 246, 159 }, { 0, 2 }, { 0, 3 }, 12, 62 },
+      { { 1, 0 }, { 0, 0 }, { 250, 242 }, { 124, 4 }, { 0, 0 }, 0, 63 },
+      { { 12, 18 }, { 0, 0 }, { 246, 203 }, { 2, 67 }, { 0, 2 }, 10, 70 },
+      { { 12, 18 }, { 0, 0 }, { 246, 203 }, { 2, 19 }, { 0, 2 }, 10, 70 },
+      { { 14, 7 }, { 6, 68 }, { 248, 244 }, { 66, 228 }, { 3, 3 }, 14, 53 },
+      { { 14, 0 }, { 64, 8 }, { 150, 183 }, { 79, 24 }, { 0, 2 }, 14, 48 },
+      { { 1, 221 }, { 0, 0 }, { 246, 159 }, { 0, 2 }, { 2, 3 }, 12, 84 },
+      { { 2, 9 }, { 27, 0 }, { 245, 246 }, { 118, 214 }, { 2, 0 }, 4, 43 },
+      { { 0, 223 }, { 9, 0 }, { 246, 147 }, { 0, 67 }, { 0, 2 }, 14, 56 },
+      { { 128, 144 }, { 13, 0 }, { 248, 159 }, { 0, 4 }, { 2, 3 }, 14, 24 },
+      { { 12, 18 }, { 0, 0 }, { 246, 203 }, { 2, 67 }, { 0, 2 }, 10, 65 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 1, 2 }, { 84, 0 }, { 250, 248 }, { 141, 184 }, { 0, 0 }, 6, 48 },
+      { { 1, 2 }, { 84, 0 }, { 250, 248 }, { 141, 184 }, { 0, 0 }, 6, 51 },
+      { { 1, 2 }, { 84, 0 }, { 250, 248 }, { 141, 184 }, { 0, 0 }, 6, 54 },
+      { { 2, 4 }, { 0, 0 }, { 250, 200 }, { 191, 151 }, { 0, 0 }, 11, 42 },
+      { { 2, 4 }, { 0, 0 }, { 250, 200 }, { 191, 151 }, { 0, 0 }, 11, 39 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 14, 0 }, { 64, 8 }, { 118, 119 }, { 79, 24 }, { 0, 2 }, 14, 64 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 128, 17 }, { 0, 0 }, { 255, 111 }, { 6, 22 }, { 3, 0 }, 14, 52 },
+      { { 128, 17 }, { 0, 0 }, { 255, 79 }, { 6, 22 }, { 3, 0 }, 14, 52 },
+      { { 6, 21 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 0, 0 }, 1, 60 },
+      { { 6, 21 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 0, 0 }, 1, 66 },
+      { { 6, 21 }, { 63, 0 }, { 0, 247 }, { 244, 245 }, { 0, 0 }, 1, 59 },
+      { { 65, 66 }, { 69, 0 }, { 252, 105 }, { 69, 5 }, { 0, 0 }, 0, 91 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 23, 2 }, { 79, 16 }, { 242, 242 }, { 96, 114 }, { 0, 0 }, 8, 109 },
+      { { 14, 0 }, { 64, 8 }, { 118, 119 }, { 79, 24 }, { 0, 2 }, 14, 64 },
+      { { 133, 132 }, { 5, 64 }, { 249, 214 }, { 50, 165 }, { 3, 0 }, 14, 79 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 },
+      { { 33, 17 }, { 17, 0 }, { 163, 196 }, { 67, 34 }, { 2, 0 }, 13, 0 }
+   };
--- /dev/null
+++ b/Game/src/audiolib/linklist.h
@@ -1,0 +1,118 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __linklist_h
+#define __linklist_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define NewNode(type)  ((type*)SafeMalloc(sizeof(type)))
+
+
+#define LL_CreateNewLinkedList(rootnode,type,next,prev) 			\
+   { 																				\
+   (rootnode) = NewNode(type);                            		\
+   (rootnode)->prev = (rootnode);                         		\
+   (rootnode)->next = (rootnode);                         		\
+   }
+
+
+
+#define LL_AddNode(rootnode, newnode, next, prev) 			\
+   {                                              			\
+   (newnode)->next = (rootnode);                  			\
+   (newnode)->prev = (rootnode)->prev;                	\
+   (rootnode)->prev->next = (newnode);                	\
+   (rootnode)->prev = (newnode);                      	\
+   }
+
+#define LL_TransferList(oldroot,newroot,next,prev)  \
+   {                                                \
+   if ((oldroot)->prev != (oldroot))                    \
+      {                                             \
+      (oldroot)->prev->next = (newroot);                \
+      (oldroot)->next->prev = (newroot)->prev;          \
+      (newroot)->prev->next = (oldroot)->next;          \
+      (newroot)->prev = (oldroot)->prev;                \
+      (oldroot)->next = (oldroot);                      \
+      (oldroot)->prev = (oldroot);                      \
+      }                                             \
+   }
+
+#define LL_ReverseList(root,type,next,prev)              \
+   {                                                     \
+   type *newend,*trav,*tprev;                            \
+                                                         \
+   newend = (root)->next;                                  \
+   for(trav = (root)->prev; trav != newend; trav = tprev)  \
+      {                                                  \
+      tprev = trav->prev;                                \
+      LL_MoveNode(trav,newend,next,prev);                \
+      }                                                  \
+   }
+
+
+#define LL_RemoveNode(node,next,prev) \
+   {                                  \
+   (node)->prev->next = (node)->next;     \
+   (node)->next->prev = (node)->prev;     \
+   (node)->next = (node);                 \
+   (node)->prev = (node);                 \
+   }
+
+
+#define LL_SortedInsertion(rootnode,insertnode,next,prev,type,sortparm) \
+   {                                                                    \
+   type *hoya;                                                          \
+                                                                        \
+   hoya = (rootnode)->next;                                               \
+   while((hoya != (rootnode)) && ((insertnode)->sortparm > hoya->sortparm)) \
+      {                                                                 \
+      hoya = hoya->next;                                                \
+      }                                                                 \
+   LL_AddNode(hoya,(insertnode),next,prev);                               \
+   }
+
+#define LL_MoveNode(node,newroot,next,prev) \
+   {                                        \
+   LL_RemoveNode((node),next,prev);           \
+   LL_AddNode((newroot),(node),next,prev);      \
+   }
+
+#define LL_ListEmpty(list,next,prev) \
+   (                                 \
+   ((list)->next == (list)) &&       \
+   ((list)->prev == (list))          \
+   )
+
+#define LL_Free(list)   SafeFree(list)
+#define LL_Reset(list,next,prev)    (list)->next = (list)->prev = (list)
+#define LL_New      LL_CreateNewLinkedList
+#define LL_Remove   LL_RemoveNode
+#define LL_Add      LL_AddNode
+#define LL_Empty    LL_ListEmpty
+#define LL_Move     LL_MoveNode
+
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/audiolib/ll_man.c
@@ -1,0 +1,173 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: LL_MAN.C
+
+   author: James R. Dose
+   date:   January 1, 1994
+
+   Linked list management routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#define LOCKMEMORY
+
+#include <stddef.h>
+#include "ll_man.h"
+
+#ifdef LOCKMEMORY
+#include "dpmi.h"
+#endif
+
+#define OFFSET( structure, offset ) \
+   ( *( ( char ** )&( structure )[ offset ] ) )
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define LL_LockStart LL_AddNode
+
+
+void LL_AddNode
+   (
+   char *item,
+   char **head,
+   char **tail,
+   int next,
+   int prev
+   )
+
+   {
+   OFFSET( item, prev ) = NULL;
+   OFFSET( item, next ) = *head;
+
+   if ( *head )
+      {
+      OFFSET( *head, prev ) = item;
+      }
+   else
+      {
+      *tail = item;
+      }
+
+   *head = item;
+   }
+
+void LL_RemoveNode
+   (
+   char *item,
+   char **head,
+   char **tail,
+   int next,
+   int prev
+   )
+
+   {
+   if ( OFFSET( item, prev ) == NULL )
+      {
+      *head = OFFSET( item, next );
+      }
+   else
+      {
+      OFFSET( OFFSET( item, prev ), next ) = OFFSET( item, next );
+      }
+
+   if ( OFFSET( item, next ) == NULL )
+      {
+      *tail = OFFSET( item, prev );
+      }
+   else
+      {
+      OFFSET( OFFSET( item, next ), prev ) = OFFSET( item, prev );
+      }
+
+   OFFSET( item, next ) = NULL;
+   OFFSET( item, prev ) = NULL;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: LL_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void LL_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: LL_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void LL_UnlockMemory
+   (
+   void
+   )
+
+   {
+#ifdef LOCKMEMORY
+
+   DPMI_UnlockMemoryRegion( LL_LockStart, LL_LockEnd );
+
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: LL_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int LL_LockMemory
+   (
+   void
+   )
+
+   {
+
+#ifdef LOCKMEMORY
+
+   int status;
+
+   status = DPMI_LockMemoryRegion( LL_LockStart, LL_LockEnd );
+   if ( status != DPMI_Ok )
+      {
+      return( LL_Error );
+      }
+
+#endif
+
+   return( LL_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/ll_man.h
@@ -1,0 +1,76 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: LL_MAN.H
+
+   author: James R. Dose
+   date:   February 4, 1994
+
+   Public header for LL_MAN.C.  Linked list management routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __LL_MAN_H
+#define __LL_MAN_H
+
+enum LL_Errors
+   {
+   LL_Warning = -2,
+   LL_Error   = -1,
+   LL_Ok      = 0
+   };
+
+typedef struct list
+   {
+   void *start;
+   void *end;
+   } list;
+
+void LL_AddNode( char *node, char **head, char **tail, int next, int prev );
+void LL_RemoveNode( char *node, char **head, char **tail, int next, int prev );
+void LL_UnlockMemory( void );
+int  LL_LockMemory( void );
+
+#define LL_AddToHead( type, listhead, node )         \
+    LL_AddNode( ( char * )( node ),                  \
+                ( char ** )&( ( listhead )->start ), \
+                ( char ** )&( ( listhead )->end ),   \
+                ( int )&( ( type * ) 0 )->next,      \
+                ( int )&( ( type * ) 0 )->prev )
+
+#define LL_AddToTail( type, listhead, node )         \
+    LL_AddNode( ( char * )( node ),                  \
+                ( char ** )&( ( listhead )->end ),   \
+                ( char ** )&( ( listhead )->start ), \
+                ( int )&( ( type * ) 0 )->prev,      \
+                ( int )&( ( type * ) 0 )->next )
+
+#define LL_Remove( type, listhead, node )               \
+    LL_RemoveNode( ( char * )( node ),                  \
+                   ( char ** )&( ( listhead )->start ), \
+                   ( char ** )&( ( listhead )->end ),   \
+                   ( int )&( ( type * ) 0 )->next,      \
+                   ( int )&( ( type * ) 0 )->prev )
+
+#define LL_NextNode( node )     ( ( node )->next )
+#define LL_PreviousNode( node ) ( ( node )->prev )
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/memcheck.h
@@ -1,0 +1,20 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
--- /dev/null
+++ b/Game/src/audiolib/midi.c
@@ -1,0 +1,2265 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MIDI.C
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdlib.h>
+#include <time.h>
+#include <dos.h>
+#include <string.h>
+#include "sndcards.h"
+#include "interrup.h"
+#include "dpmi.h"
+#include "standard.h"
+#include "task_man.h"
+#include "ll_man.h"
+#include "usrhooks.h"
+#include "music.h"
+#include "_midi.h"
+#include "midi.h"
+#include "debugio.h"
+
+extern int MUSIC_SoundDevice;
+
+static const int _MIDI_CommandLengths[ NUM_MIDI_CHANNELS ] =
+   {
+   0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0
+   };
+
+static int cdecl ( *_MIDI_RerouteFunctions[ NUM_MIDI_CHANNELS ] )
+   (
+   int event,
+   int c1,
+   int c2
+   ) = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+static track *_MIDI_TrackPtr = NULL;
+static int    _MIDI_TrackMemSize;
+static int    _MIDI_NumTracks;
+
+static int _MIDI_SongActive = FALSE;
+static int _MIDI_SongLoaded = FALSE;
+static int _MIDI_Loop = FALSE;
+
+static task *_MIDI_PlayRoutine = NULL;
+
+static int  _MIDI_Division;
+static int  _MIDI_Tick    = 0;
+static int  _MIDI_Beat    = 1;
+static int  _MIDI_Measure = 1;
+static unsigned _MIDI_Time;
+static int  _MIDI_BeatsPerMeasure;
+static int  _MIDI_TicksPerBeat;
+static int  _MIDI_TimeBase;
+static long _MIDI_FPSecondsPerTick;
+static unsigned _MIDI_TotalTime;
+static int  _MIDI_TotalTicks;
+static int  _MIDI_TotalBeats;
+static int  _MIDI_TotalMeasures;
+
+static unsigned long _MIDI_PositionInTicks;
+
+static int  _MIDI_Context;
+
+static int _MIDI_ActiveTracks;
+static int _MIDI_TotalVolume = MIDI_MaxVolume;
+
+static int _MIDI_ChannelVolume[ NUM_MIDI_CHANNELS ];
+static int _MIDI_UserChannelVolume[ NUM_MIDI_CHANNELS ] =
+   {
+   256, 256, 256, 256, 256, 256, 256, 256,
+   256, 256, 256, 256, 256, 256, 256, 256
+   };
+
+static midifuncs *_MIDI_Funcs = NULL;
+
+static int Reset = FALSE;
+
+int MIDI_Tempo = 120;
+
+char MIDI_PatchMap[ 128 ];
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define MIDI_LockStart _MIDI_ReadNumber
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ReadNumber
+
+   Reads a variable length number from a MIDI track.
+---------------------------------------------------------------------*/
+
+static long _MIDI_ReadNumber
+   (
+   void *from,
+   size_t size
+   )
+
+   {
+   unsigned char *FromPtr;
+   long          value;
+
+   if ( size > 4 )
+      {
+      size = 4;
+      }
+
+   FromPtr = ( unsigned char * )from;
+
+   value = 0;
+   while( size-- )
+      {
+      value <<= 8;
+      value += *FromPtr++;
+      }
+
+   return( value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ReadDelta
+
+   Reads a variable length encoded delta delay time from the MIDI data.
+---------------------------------------------------------------------*/
+
+static long _MIDI_ReadDelta
+   (
+   track *ptr
+   )
+
+   {
+   long          value;
+   unsigned char c;
+
+   GET_NEXT_EVENT( ptr, value );
+
+   if ( value & 0x80 )
+      {
+      value &= 0x7f;
+      do
+         {
+         GET_NEXT_EVENT( ptr, c );
+         value = ( value << 7 ) + ( c & 0x7f );
+         }
+      while ( c & 0x80 );
+      }
+
+   return( value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ResetTracks
+
+   Sets the track pointers to the beginning of the song.
+---------------------------------------------------------------------*/
+
+static void _MIDI_ResetTracks
+   (
+   void
+   )
+
+   {
+   int    i;
+   track *ptr;
+
+   _MIDI_Tick = 0;
+   _MIDI_Beat = 1;
+   _MIDI_Measure = 1;
+   _MIDI_Time = 0;
+   _MIDI_BeatsPerMeasure = 4;
+   _MIDI_TicksPerBeat = _MIDI_Division;
+   _MIDI_TimeBase = 4;
+
+   _MIDI_PositionInTicks = 0;
+   _MIDI_ActiveTracks    = 0;
+   _MIDI_Context         = 0;
+
+   ptr = _MIDI_TrackPtr;
+   for( i = 0; i < _MIDI_NumTracks; i++ )
+      {
+      ptr->pos                    = ptr->start;
+      ptr->delay                  = _MIDI_ReadDelta( ptr );
+      ptr->active                 = ptr->EMIDI_IncludeTrack;
+      ptr->RunningStatus          = 0;
+      ptr->currentcontext         = 0;
+      ptr->context[ 0 ].loopstart = ptr->start;
+      ptr->context[ 0 ].loopcount = 0;
+
+      if ( ptr->active )
+         {
+         _MIDI_ActiveTracks++;
+         }
+
+      ptr++;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_AdvanceTick
+
+   Increment tick counters.
+---------------------------------------------------------------------*/
+
+static void _MIDI_AdvanceTick
+   (
+   void
+   )
+
+   {
+   _MIDI_PositionInTicks++;
+   _MIDI_Time += _MIDI_FPSecondsPerTick;
+
+   _MIDI_Tick++;
+   while( _MIDI_Tick > _MIDI_TicksPerBeat )
+      {
+      _MIDI_Tick -= _MIDI_TicksPerBeat;
+      _MIDI_Beat++;
+      }
+   while( _MIDI_Beat > _MIDI_BeatsPerMeasure )
+      {
+      _MIDI_Beat -= _MIDI_BeatsPerMeasure;
+      _MIDI_Measure++;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SysEx
+
+   Interpret SysEx Event.
+---------------------------------------------------------------------*/
+
+static void _MIDI_SysEx
+   (
+   track *Track
+   )
+
+   {
+   int length;
+
+   length = _MIDI_ReadDelta( Track );
+   Track->pos += length;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_MetaEvent
+
+   Interpret Meta Event.
+---------------------------------------------------------------------*/
+
+static void _MIDI_MetaEvent
+   (
+   track *Track
+   )
+
+   {
+   int   command;
+   int   length;
+   int   denominator;
+   long  tempo;
+
+   GET_NEXT_EVENT( Track, command );
+   GET_NEXT_EVENT( Track, length );
+
+   switch( command )
+      {
+      case MIDI_END_OF_TRACK :
+         Track->active = FALSE;
+
+         _MIDI_ActiveTracks--;
+         break;
+
+      case MIDI_TEMPO_CHANGE :
+         tempo = 60000000L / _MIDI_ReadNumber( Track->pos, 3 );
+         MIDI_SetTempo( tempo );
+         break;
+
+      case MIDI_TIME_SIGNATURE :
+         if ( ( _MIDI_Tick > 0 ) || ( _MIDI_Beat > 1 ) )
+            {
+            _MIDI_Measure++;
+            }
+
+         _MIDI_Tick = 0;
+         _MIDI_Beat = 1;
+
+         _MIDI_BeatsPerMeasure = (int)*Track->pos;
+         denominator = (int)*( Track->pos + 1 );
+         _MIDI_TimeBase = 1;
+         while( denominator > 0 )
+            {
+            _MIDI_TimeBase += _MIDI_TimeBase;
+            denominator--;
+            }
+         _MIDI_TicksPerBeat = ( _MIDI_Division * 4 ) / _MIDI_TimeBase;
+         break;
+      }
+
+   Track->pos += length;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_InterpretControllerInfo
+
+   Interprets the MIDI controller info.
+---------------------------------------------------------------------*/
+
+static int _MIDI_InterpretControllerInfo
+   (
+   track *Track,
+   int   TimeSet,
+   int   channel,
+   int   c1,
+   int   c2
+   )
+
+   {
+   track *trackptr;
+   int tracknum;
+   int loopcount;
+
+   switch( c1 )
+      {
+      case MIDI_MONO_MODE_ON :
+         Track->pos++;
+         break;
+
+      case MIDI_VOLUME :
+         if ( !Track->EMIDI_VolumeChange )
+            {
+            _MIDI_SetChannelVolume( channel, c2 );
+            }
+         break;
+
+      case EMIDI_INCLUDE_TRACK :
+      case EMIDI_EXCLUDE_TRACK :
+         break;
+
+      case EMIDI_PROGRAM_CHANGE :
+         if ( Track->EMIDI_ProgramChange )
+            {
+            _MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c2 & 0x7f ] );
+            }
+         break;
+
+      case EMIDI_VOLUME_CHANGE :
+         if ( Track->EMIDI_VolumeChange )
+            {
+            _MIDI_SetChannelVolume( channel, c2 );
+            }
+         break;
+
+      case EMIDI_CONTEXT_START :
+         break;
+
+      case EMIDI_CONTEXT_END :
+         if ( ( Track->currentcontext == _MIDI_Context ) ||
+            ( _MIDI_Context < 0 ) ||
+            ( Track->context[ _MIDI_Context ].pos == NULL ) )
+            {
+            break;
+            }
+
+         Track->currentcontext = _MIDI_Context;
+         Track->context[ 0 ].loopstart = Track->context[ _MIDI_Context ].loopstart;
+         Track->context[ 0 ].loopcount = Track->context[ _MIDI_Context ].loopcount;
+         Track->pos           = Track->context[ _MIDI_Context ].pos;
+         Track->RunningStatus = Track->context[ _MIDI_Context ].RunningStatus;
+
+         if ( TimeSet )
+            {
+            break;
+            }
+
+         _MIDI_Time             = Track->context[ _MIDI_Context ].time;
+         _MIDI_FPSecondsPerTick = Track->context[ _MIDI_Context ].FPSecondsPerTick;
+         _MIDI_Tick             = Track->context[ _MIDI_Context ].tick;
+         _MIDI_Beat             = Track->context[ _MIDI_Context ].beat;
+         _MIDI_Measure          = Track->context[ _MIDI_Context ].measure;
+         _MIDI_BeatsPerMeasure  = Track->context[ _MIDI_Context ].BeatsPerMeasure;
+         _MIDI_TicksPerBeat     = Track->context[ _MIDI_Context ].TicksPerBeat;
+         _MIDI_TimeBase         = Track->context[ _MIDI_Context ].TimeBase;
+         TimeSet = TRUE;
+         break;
+
+      case EMIDI_LOOP_START :
+      case EMIDI_SONG_LOOP_START :
+         if ( c2 == 0 )
+            {
+            loopcount = EMIDI_INFINITE;
+            }
+         else
+            {
+            loopcount = c2;
+            }
+
+         if ( c1 == EMIDI_SONG_LOOP_START )
+            {
+            trackptr = _MIDI_TrackPtr;
+            tracknum = _MIDI_NumTracks;
+            }
+         else
+            {
+            trackptr = Track;
+            tracknum = 1;
+            }
+
+         while( tracknum > 0 )
+            {
+            trackptr->context[ 0 ].loopcount        = loopcount;
+            trackptr->context[ 0 ].pos              = trackptr->pos;
+            trackptr->context[ 0 ].loopstart        = trackptr->pos;
+            trackptr->context[ 0 ].RunningStatus    = trackptr->RunningStatus;
+            trackptr->context[ 0 ].active           = trackptr->active;
+            trackptr->context[ 0 ].delay            = trackptr->delay;
+            trackptr->context[ 0 ].time             = _MIDI_Time;
+            trackptr->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
+            trackptr->context[ 0 ].tick             = _MIDI_Tick;
+            trackptr->context[ 0 ].beat             = _MIDI_Beat;
+            trackptr->context[ 0 ].measure          = _MIDI_Measure;
+            trackptr->context[ 0 ].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
+            trackptr->context[ 0 ].TicksPerBeat     = _MIDI_TicksPerBeat;
+            trackptr->context[ 0 ].TimeBase         = _MIDI_TimeBase;
+            trackptr++;
+            tracknum--;
+            }
+         break;
+
+      case EMIDI_LOOP_END :
+      case EMIDI_SONG_LOOP_END :
+         if ( ( c2 != EMIDI_END_LOOP_VALUE ) ||
+            ( Track->context[ 0 ].loopstart == NULL ) ||
+            ( Track->context[ 0 ].loopcount == 0 ) )
+            {
+            break;
+            }
+
+         if ( c1 == EMIDI_SONG_LOOP_END )
+            {
+            trackptr = _MIDI_TrackPtr;
+            tracknum = _MIDI_NumTracks;
+            _MIDI_ActiveTracks = 0;
+            }
+         else
+            {
+            trackptr = Track;
+            tracknum = 1;
+            _MIDI_ActiveTracks--;
+            }
+
+         while( tracknum > 0 )
+            {
+            if ( trackptr->context[ 0 ].loopcount != EMIDI_INFINITE )
+               {
+               trackptr->context[ 0 ].loopcount--;
+               }
+
+            trackptr->pos           = trackptr->context[ 0 ].loopstart;
+            trackptr->RunningStatus = trackptr->context[ 0 ].RunningStatus;
+            trackptr->delay         = trackptr->context[ 0 ].delay;
+            trackptr->active        = trackptr->context[ 0 ].active;
+            if ( trackptr->active )
+               {
+               _MIDI_ActiveTracks++;
+               }
+
+            if ( !TimeSet )
+               {
+               _MIDI_Time             = trackptr->context[ 0 ].time;
+               _MIDI_FPSecondsPerTick = trackptr->context[ 0 ].FPSecondsPerTick;
+               _MIDI_Tick             = trackptr->context[ 0 ].tick;
+               _MIDI_Beat             = trackptr->context[ 0 ].beat;
+               _MIDI_Measure          = trackptr->context[ 0 ].measure;
+               _MIDI_BeatsPerMeasure  = trackptr->context[ 0 ].BeatsPerMeasure;
+               _MIDI_TicksPerBeat     = trackptr->context[ 0 ].TicksPerBeat;
+               _MIDI_TimeBase         = trackptr->context[ 0 ].TimeBase;
+               TimeSet = TRUE;
+               }
+
+            trackptr++;
+            tracknum--;
+            }
+         break;
+
+      default :
+         if ( _MIDI_Funcs->ControlChange )
+            {
+            _MIDI_Funcs->ControlChange( channel, c1, c2 );
+            }
+      }
+
+   return TimeSet;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ServiceRoutine
+
+   Task that interperates the MIDI data.
+---------------------------------------------------------------------*/
+// NOTE: We have to use a stack frame here because of a strange bug
+// that occurs with Watcom.  This means that we cannot access Task!
+//Turned off to test if it works with Watcom 10a
+//#pragma aux _MIDI_ServiceRoutine frame;
+/*
+static void test
+   (
+   task *Task
+   )
+   {
+   _MIDI_ServiceRoutine( Task );
+   _MIDI_ServiceRoutine( Task );
+   _MIDI_ServiceRoutine( Task );
+   _MIDI_ServiceRoutine( Task );
+   }
+*/
+static void _MIDI_ServiceRoutine
+   (
+   task *Task
+   )
+
+   {
+   int   event;
+   int   channel;
+   int   command;
+   track *Track;
+   int   tracknum;
+   int   status;
+   int   c1;
+   int   c2;
+   int   TimeSet = FALSE;
+
+   if ( !_MIDI_SongActive )
+      {
+      return;
+      }
+
+   Track = _MIDI_TrackPtr;
+   tracknum = 0;
+   while( tracknum < _MIDI_NumTracks )
+      {
+      while ( ( Track->active ) && ( Track->delay == 0 ) )
+         {
+         GET_NEXT_EVENT( Track, event );
+
+         if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
+            {
+            switch( event )
+               {
+               case MIDI_SYSEX :
+               case MIDI_SYSEX_CONTINUE :
+                  _MIDI_SysEx( Track );
+                  break;
+
+               case MIDI_META_EVENT :
+                  _MIDI_MetaEvent( Track );
+                  break;
+               }
+
+            if ( Track->active )
+               {
+               Track->delay = _MIDI_ReadDelta( Track );
+               }
+
+            continue;
+            }
+
+         if ( event & MIDI_RUNNING_STATUS )
+            {
+            Track->RunningStatus = event;
+            }
+         else
+            {
+            event = Track->RunningStatus;
+            Track->pos--;
+            }
+
+         channel = GET_MIDI_CHANNEL( event );
+         command = GET_MIDI_COMMAND( event );
+
+         if ( _MIDI_CommandLengths[ command ] > 0 )
+            {
+            GET_NEXT_EVENT( Track, c1 );
+            if ( _MIDI_CommandLengths[ command ] > 1 )
+               {
+               GET_NEXT_EVENT( Track, c2 );
+               }
+            }
+
+         if ( _MIDI_RerouteFunctions[ channel ] != NULL )
+            {
+            status = _MIDI_RerouteFunctions[ channel ]( event, c1, c2 );
+
+            if ( status == MIDI_DONT_PLAY )
+               {
+               Track->delay = _MIDI_ReadDelta( Track );
+               continue;
+               }
+            }
+
+         switch ( command )
+            {
+            case MIDI_NOTE_OFF :
+               if ( _MIDI_Funcs->NoteOff )
+                  {
+                  _MIDI_Funcs->NoteOff( channel, c1, c2 );
+                  }
+               break;
+
+            case MIDI_NOTE_ON :
+               if ( _MIDI_Funcs->NoteOn )
+                  {
+                  _MIDI_Funcs->NoteOn( channel, c1, c2 );
+                  }
+               break;
+
+            case MIDI_POLY_AFTER_TCH :
+               if ( _MIDI_Funcs->PolyAftertouch )
+                  {
+                  _MIDI_Funcs->PolyAftertouch( channel, c1, c2 );
+                  }
+               break;
+
+            case MIDI_CONTROL_CHANGE :
+               TimeSet = _MIDI_InterpretControllerInfo( Track, TimeSet,
+                  channel, c1, c2 );
+               break;
+
+            case MIDI_PROGRAM_CHANGE :
+               if ( ( _MIDI_Funcs->ProgramChange ) &&
+                  ( !Track->EMIDI_ProgramChange ) )
+                  {
+                  _MIDI_Funcs->ProgramChange( channel, MIDI_PatchMap[ c1 & 0x7f ] );
+                  }
+               break;
+
+            case MIDI_AFTER_TOUCH :
+               if ( _MIDI_Funcs->ChannelAftertouch )
+                  {
+                  _MIDI_Funcs->ChannelAftertouch( channel, c1 );
+                  }
+               break;
+
+            case MIDI_PITCH_BEND :
+               if ( _MIDI_Funcs->PitchBend )
+                  {
+                  _MIDI_Funcs->PitchBend( channel, c1, c2 );
+                  }
+               break;
+
+            default :
+               break;
+            }
+
+         Track->delay = _MIDI_ReadDelta( Track );
+         }
+
+      Track->delay--;
+      Track++;
+      tracknum++;
+
+      if ( _MIDI_ActiveTracks == 0 )
+         {
+         _MIDI_ResetTracks();
+         if ( _MIDI_Loop )
+            {
+            tracknum = 0;
+            Track = _MIDI_TrackPtr;
+            }
+         else
+            {
+            _MIDI_SongActive = FALSE;
+            break;
+            }
+         }
+      }
+
+   _MIDI_AdvanceTick();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SendControlChange
+
+   Sends a control change to the proper device
+---------------------------------------------------------------------*/
+
+static int _MIDI_SendControlChange
+   (
+   int channel,
+   int c1,
+   int c2
+   )
+
+   {
+   int status;
+
+   if ( _MIDI_RerouteFunctions[ channel ] != NULL )
+      {
+      status = _MIDI_RerouteFunctions[ channel ]( 0xB0 + channel,
+         c1, c2 );
+      if ( status == MIDI_DONT_PLAY )
+         {
+         return( MIDI_Ok );
+         }
+      }
+
+   if ( _MIDI_Funcs == NULL )
+      {
+      return( MIDI_Error );
+      }
+
+   if ( _MIDI_Funcs->ControlChange == NULL )
+      {
+      return( MIDI_Error );
+      }
+
+   _MIDI_Funcs->ControlChange( channel, c1, c2 );
+
+   return( MIDI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_RerouteMidiChannel
+
+   Sets callback function to reroute MIDI commands from specified
+   function.
+---------------------------------------------------------------------*/
+
+void MIDI_RerouteMidiChannel
+   (
+   int channel,
+   int cdecl ( *function )( int event, int c1, int c2 )
+   )
+
+   {
+   if ( ( channel >= 1 ) && ( channel <= 16 ) )
+      {
+      _MIDI_RerouteFunctions[ channel - 1 ] = function;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_AllNotesOff
+
+   Sends all notes off commands on all midi channels.
+---------------------------------------------------------------------*/
+
+int MIDI_AllNotesOff
+   (
+   void
+   )
+
+   {
+   int channel;
+
+   for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
+      {
+      _MIDI_SendControlChange( channel, 0x40, 0 );
+      _MIDI_SendControlChange( channel, MIDI_ALL_NOTES_OFF, 0 );
+      _MIDI_SendControlChange( channel, 0x78, 0 );
+      }
+
+   return( MIDI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SetChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+static void _MIDI_SetChannelVolume
+   (
+   int channel,
+   int volume
+   )
+
+   {
+   int status;
+   int remotevolume;
+
+   _MIDI_ChannelVolume[ channel ] = volume;
+
+   if ( _MIDI_RerouteFunctions[ channel ] != NULL )
+      {
+      remotevolume = volume * _MIDI_TotalVolume;
+      remotevolume *= _MIDI_UserChannelVolume[ channel ];
+      remotevolume /= MIDI_MaxVolume;
+      remotevolume >>= 8;
+
+      status = _MIDI_RerouteFunctions[ channel ]( 0xB0 + channel,
+         MIDI_VOLUME, remotevolume );
+      if ( status == MIDI_DONT_PLAY )
+         {
+         return;
+         }
+      }
+
+   if ( _MIDI_Funcs == NULL )
+      {
+      return;
+      }
+
+   if ( _MIDI_Funcs->ControlChange == NULL )
+      {
+      return;
+      }
+
+   // For user volume
+   volume *= _MIDI_UserChannelVolume[ channel ];
+
+   if ( _MIDI_Funcs->SetVolume == NULL )
+      {
+      volume *= _MIDI_TotalVolume;
+      volume /= MIDI_MaxVolume;
+      }
+
+   // For user volume
+   volume >>= 8;
+
+   _MIDI_Funcs->ControlChange( channel, MIDI_VOLUME, volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetUserChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+void MIDI_SetUserChannelVolume
+   (
+   int channel,
+   int volume
+   )
+
+   {
+   // Convert channel from 1-16 to 0-15
+   channel--;
+
+   volume = max( 0, volume );
+   volume = min( volume, 256 );
+
+   if ( ( channel >= 0 ) && ( channel < NUM_MIDI_CHANNELS ) )
+      {
+      _MIDI_UserChannelVolume[ channel ] = volume;
+      _MIDI_SetChannelVolume( channel, _MIDI_ChannelVolume[ channel ] );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_ResetUserChannelVolume
+
+   Sets the volume of the specified midi channel.
+---------------------------------------------------------------------*/
+
+void MIDI_ResetUserChannelVolume
+   (
+   void
+   )
+
+   {
+   int channel;
+
+   for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
+      {
+      _MIDI_UserChannelVolume[ channel ] = 256;
+      }
+
+   _MIDI_SendChannelVolumes();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_SendChannelVolumes
+
+   Sets the volume on all the midi channels.
+---------------------------------------------------------------------*/
+
+static void _MIDI_SendChannelVolumes
+   (
+   void
+   )
+
+   {
+   int channel;
+
+   for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
+      {
+      _MIDI_SetChannelVolume( channel, _MIDI_ChannelVolume[ channel ] );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_Reset
+
+   Resets the MIDI device to General Midi defaults.
+---------------------------------------------------------------------*/
+
+int MIDI_Reset
+   (
+   void
+   )
+
+   {
+   int channel;
+   long time;
+   unsigned flags;
+
+   MIDI_AllNotesOff();
+
+   flags = DisableInterrupts();
+   _enable();
+   time = clock() + CLOCKS_PER_SEC/24;
+   while(clock() < time)
+      ;
+
+   RestoreInterrupts( flags );
+
+   for( channel = 0; channel < NUM_MIDI_CHANNELS; channel++ )
+      {
+      _MIDI_SendControlChange( channel, MIDI_RESET_ALL_CONTROLLERS, 0 );
+      _MIDI_SendControlChange( channel, MIDI_RPN_MSB, MIDI_PITCHBEND_MSB );
+      _MIDI_SendControlChange( channel, MIDI_RPN_LSB, MIDI_PITCHBEND_LSB );
+      _MIDI_SendControlChange( channel, MIDI_DATAENTRY_MSB, 2 ); /* Pitch Bend Sensitivity MSB */
+      _MIDI_SendControlChange( channel, MIDI_DATAENTRY_LSB, 0 ); /* Pitch Bend Sensitivity LSB */
+      _MIDI_ChannelVolume[ channel ] = GENMIDI_DefaultVolume;
+      }
+
+   _MIDI_SendChannelVolumes();
+
+   Reset = TRUE;
+
+   return( MIDI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetVolume
+
+   Sets the total volume of the music.
+---------------------------------------------------------------------*/
+
+int MIDI_SetVolume
+   (
+   int volume
+   )
+
+   {
+   int i;
+
+   if ( _MIDI_Funcs == NULL )
+      {
+      return( MIDI_NullMidiModule );
+      }
+
+   volume = min( MIDI_MaxVolume, volume );
+   volume = max( 0, volume );
+
+   _MIDI_TotalVolume = volume;
+
+   if ( _MIDI_Funcs->SetVolume )
+      {
+      _MIDI_Funcs->SetVolume( volume );
+
+      for( i = 0; i < NUM_MIDI_CHANNELS; i++ )
+         {
+         if ( _MIDI_RerouteFunctions[ i ] != NULL )
+            {
+            _MIDI_SetChannelVolume( i, _MIDI_ChannelVolume[ i ] );
+            }
+         }
+      }
+   else
+      {
+      _MIDI_SendChannelVolumes();
+      }
+
+   return( MIDI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetVolume
+
+   Returns the total volume of the music.
+---------------------------------------------------------------------*/
+
+int MIDI_GetVolume
+   (
+   void
+   )
+
+   {
+   int volume;
+
+   if ( _MIDI_Funcs == NULL )
+      {
+      return( MIDI_NullMidiModule );
+      }
+
+   if ( _MIDI_Funcs->GetVolume )
+      {
+      volume = _MIDI_Funcs->GetVolume();
+      }
+   else
+      {
+      volume = _MIDI_TotalVolume;
+      }
+
+   return( volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetContext
+
+   Sets the song context.
+---------------------------------------------------------------------*/
+
+void MIDI_SetContext
+   (
+   int context
+   )
+
+   {
+   if ( ( context > 0 ) && ( context < EMIDI_NUM_CONTEXTS ) )
+      {
+      _MIDI_Context = context;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetContext
+
+   Returns the current song context.
+---------------------------------------------------------------------*/
+
+int MIDI_GetContext
+   (
+   void
+   )
+
+   {
+   return _MIDI_Context;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetLoopFlag
+
+   Sets whether the song should loop when finished or not.
+---------------------------------------------------------------------*/
+
+void MIDI_SetLoopFlag
+   (
+   int loopflag
+   )
+
+   {
+   _MIDI_Loop = loopflag;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_ContinueSong
+
+   Continues playback of a paused song.
+---------------------------------------------------------------------*/
+
+void MIDI_ContinueSong
+   (
+   void
+   )
+
+   {
+   if ( _MIDI_SongLoaded )
+      {
+      _MIDI_SongActive = TRUE;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_PauseSong
+
+   Pauses playback of the current song.
+---------------------------------------------------------------------*/
+
+void MIDI_PauseSong
+   (
+   void
+   )
+
+   {
+   if ( _MIDI_SongLoaded )
+      {
+      _MIDI_SongActive = FALSE;
+      MIDI_AllNotesOff();
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SongPlaying
+
+   Returns whether a song is playing or not.
+---------------------------------------------------------------------*/
+
+int MIDI_SongPlaying
+   (
+   void
+   )
+
+   {
+   return( _MIDI_SongActive );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetMidiFuncs
+
+   Selects the routines that send the MIDI data to the music device.
+---------------------------------------------------------------------*/
+
+void MIDI_SetMidiFuncs
+   (
+   midifuncs *funcs
+   )
+
+   {
+   _MIDI_Funcs = funcs;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_StopSong
+
+   Stops playback of the currently playing song.
+---------------------------------------------------------------------*/
+
+void MIDI_StopSong
+   (
+   void
+   )
+
+   {
+   if ( _MIDI_SongLoaded )
+      {
+      TS_Terminate( _MIDI_PlayRoutine );
+
+      _MIDI_PlayRoutine = NULL;
+      _MIDI_SongActive = FALSE;
+      _MIDI_SongLoaded = FALSE;
+
+      MIDI_Reset();
+      _MIDI_ResetTracks();
+
+      if ( _MIDI_Funcs->ReleasePatches )
+         {
+         _MIDI_Funcs->ReleasePatches();
+         }
+
+      DPMI_UnlockMemory( _MIDI_TrackPtr, _MIDI_TrackMemSize );
+      USRHOOKS_FreeMem( _MIDI_TrackPtr );
+
+      _MIDI_TrackPtr     = NULL;
+      _MIDI_NumTracks    = 0;
+      _MIDI_TrackMemSize = 0;
+
+      _MIDI_TotalTime     = 0;
+      _MIDI_TotalTicks    = 0;
+      _MIDI_TotalBeats    = 0;
+      _MIDI_TotalMeasures = 0;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_PlaySong
+
+   Begins playback of a MIDI song.
+---------------------------------------------------------------------*/
+
+int MIDI_PlaySong
+   (
+   unsigned char *song,
+   int loopflag
+   )
+
+   {
+   int    numtracks;
+   int    format;
+   long   headersize;
+   long   tracklength;
+   track *CurrentTrack;
+   unsigned char *ptr;
+   int    status;
+
+   if ( _MIDI_SongLoaded )
+      {
+      MIDI_StopSong();
+      }
+
+   _MIDI_Loop = loopflag;
+
+   if ( _MIDI_Funcs == NULL )
+      {
+      return( MIDI_NullMidiModule );
+      }
+
+   if ( *( unsigned long * )song != MIDI_HEADER_SIGNATURE )
+      {
+      return( MIDI_InvalidMidiFile );
+      }
+
+   song += 4;
+
+   headersize      = _MIDI_ReadNumber( song, 4 );
+   song += 4;
+   format          = _MIDI_ReadNumber( song, 2 );
+   _MIDI_NumTracks = _MIDI_ReadNumber( song + 2, 2 );
+   _MIDI_Division  = _MIDI_ReadNumber( song + 4, 2 );
+   if ( _MIDI_Division < 0 )
+      {
+      // If a SMPTE time division is given, just set to 96 so no errors occur
+      _MIDI_Division = 96;
+      }
+
+   if ( format > MAX_FORMAT )
+      {
+      return( MIDI_UnknownMidiFormat );
+      }
+
+   ptr = song + headersize;
+
+   if ( _MIDI_NumTracks == 0 )
+      {
+      return( MIDI_NoTracks );
+      }
+
+   _MIDI_TrackMemSize = _MIDI_NumTracks  * sizeof( track );
+   status = USRHOOKS_GetMem( &_MIDI_TrackPtr, _MIDI_TrackMemSize );
+   if ( status != USRHOOKS_Ok )
+      {
+      return( MIDI_NoMemory );
+      }
+
+   status = DPMI_LockMemory( _MIDI_TrackPtr, _MIDI_TrackMemSize );
+   if ( status != DPMI_Ok )
+      {
+      USRHOOKS_FreeMem( _MIDI_TrackPtr );
+
+      _MIDI_TrackPtr     = NULL;
+      _MIDI_TrackMemSize = 0;
+      _MIDI_NumTracks    = 0;
+//      MIDI_SetErrorCode( MIDI_DPMI_Error );
+      return( MIDI_Error );
+      }
+
+   CurrentTrack = _MIDI_TrackPtr;
+   numtracks    = _MIDI_NumTracks;
+   while( numtracks-- )
+      {
+      if ( *( unsigned long * )ptr != MIDI_TRACK_SIGNATURE )
+         {
+         DPMI_UnlockMemory( _MIDI_TrackPtr, _MIDI_TrackMemSize );
+
+         USRHOOKS_FreeMem( _MIDI_TrackPtr );
+
+         _MIDI_TrackPtr = NULL;
+         _MIDI_TrackMemSize = 0;
+
+         return( MIDI_InvalidTrack );
+         }
+
+      tracklength = _MIDI_ReadNumber( ptr + 4, 4 );
+      ptr += 8;
+      CurrentTrack->start = ptr;
+      ptr += tracklength;
+      CurrentTrack++;
+      }
+
+   if ( _MIDI_Funcs->GetVolume != NULL )
+      {
+      _MIDI_TotalVolume = _MIDI_Funcs->GetVolume();
+      }
+
+   _MIDI_InitEMIDI();
+
+   if ( _MIDI_Funcs->LoadPatch )
+      {
+      MIDI_LoadTimbres();
+      }
+
+   _MIDI_ResetTracks();
+
+   if ( !Reset )
+      {
+      MIDI_Reset();
+      }
+
+   Reset = FALSE;
+
+   _MIDI_PlayRoutine = TS_ScheduleTask( _MIDI_ServiceRoutine, 100, 1, NULL );
+//   _MIDI_PlayRoutine = TS_ScheduleTask( test, 100, 1, NULL );
+   MIDI_SetTempo( 120 );
+   TS_Dispatch();
+
+   _MIDI_SongLoaded = TRUE;
+   _MIDI_SongActive = TRUE;
+
+   return( MIDI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetTempo
+
+   Sets the song tempo.
+---------------------------------------------------------------------*/
+
+void MIDI_SetTempo
+   (
+   int tempo
+   )
+
+   {
+   long tickspersecond;
+
+   MIDI_Tempo = tempo;
+   tickspersecond = ( tempo * _MIDI_Division ) / 60;
+   if ( _MIDI_PlayRoutine != NULL )
+      {
+      TS_SetTaskRate( _MIDI_PlayRoutine, tickspersecond );
+//      TS_SetTaskRate( _MIDI_PlayRoutine, tickspersecond / 4 );
+      }
+   _MIDI_FPSecondsPerTick = ( 1 << TIME_PRECISION ) / tickspersecond;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetTempo
+
+   Returns the song tempo.
+---------------------------------------------------------------------*/
+
+int MIDI_GetTempo
+   (
+   void
+   )
+
+   {
+   return( MIDI_Tempo );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: _MIDI_ProcessNextTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+static int _MIDI_ProcessNextTick
+   (
+   void
+   )
+
+   {
+   int   event;
+   int   channel;
+   int   command;
+   track *Track;
+   int   tracknum;
+   int   status;
+   int   c1;
+   int   c2;
+   int   TimeSet = FALSE;
+
+   Track = _MIDI_TrackPtr;
+   tracknum = 0;
+   while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
+      {
+      while ( ( Track->active ) && ( Track->delay == 0 ) )
+         {
+         GET_NEXT_EVENT( Track, event );
+
+         if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
+            {
+            switch( event )
+               {
+               case MIDI_SYSEX :
+               case MIDI_SYSEX_CONTINUE :
+                  _MIDI_SysEx( Track );
+                  break;
+
+               case MIDI_META_EVENT :
+                  _MIDI_MetaEvent( Track );
+                  break;
+               }
+
+            if ( Track->active )
+               {
+               Track->delay = _MIDI_ReadDelta( Track );
+               }
+
+            continue;
+            }
+
+         if ( event & MIDI_RUNNING_STATUS )
+            {
+            Track->RunningStatus = event;
+            }
+         else
+            {
+            event = Track->RunningStatus;
+            Track->pos--;
+            }
+
+         channel = GET_MIDI_CHANNEL( event );
+         command = GET_MIDI_COMMAND( event );
+
+         if ( _MIDI_CommandLengths[ command ] > 0 )
+            {
+            GET_NEXT_EVENT( Track, c1 );
+            if ( _MIDI_CommandLengths[ command ] > 1 )
+               {
+               GET_NEXT_EVENT( Track, c2 );
+               }
+            }
+
+         if ( _MIDI_RerouteFunctions[ channel ] != NULL )
+            {
+            status = _MIDI_RerouteFunctions[ channel ]( event, c1, c2 );
+
+            if ( status == MIDI_DONT_PLAY )
+               {
+               Track->delay = _MIDI_ReadDelta( Track );
+               continue;
+               }
+            }
+
+         switch ( command )
+            {
+            case MIDI_NOTE_OFF :
+               break;
+
+            case MIDI_NOTE_ON :
+               break;
+
+            case MIDI_POLY_AFTER_TCH :
+               if ( _MIDI_Funcs->PolyAftertouch )
+                  {
+                  _MIDI_Funcs->PolyAftertouch( channel, c1, c2 );
+                  }
+               break;
+
+            case MIDI_CONTROL_CHANGE :
+               TimeSet = _MIDI_InterpretControllerInfo( Track, TimeSet,
+                  channel, c1, c2 );
+               break;
+
+            case MIDI_PROGRAM_CHANGE :
+               if ( ( _MIDI_Funcs->ProgramChange ) &&
+                  ( !Track->EMIDI_ProgramChange ) )
+                  {
+                  _MIDI_Funcs->ProgramChange( channel, c1 );
+                  }
+               break;
+
+            case MIDI_AFTER_TOUCH :
+               if ( _MIDI_Funcs->ChannelAftertouch )
+                  {
+                  _MIDI_Funcs->ChannelAftertouch( channel, c1 );
+                  }
+               break;
+
+            case MIDI_PITCH_BEND :
+               if ( _MIDI_Funcs->PitchBend )
+                  {
+                  _MIDI_Funcs->PitchBend( channel, c1, c2 );
+                  }
+               break;
+
+            default :
+               break;
+            }
+
+         Track->delay = _MIDI_ReadDelta( Track );
+         }
+
+      Track->delay--;
+      Track++;
+      tracknum++;
+
+      if ( _MIDI_ActiveTracks == 0 )
+         {
+         break;
+         }
+      }
+
+   _MIDI_AdvanceTick();
+
+   return( TimeSet );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongTick
+   (
+   unsigned long PositionInTicks
+   )
+
+   {
+   if ( !_MIDI_SongLoaded )
+      {
+      return;
+      }
+
+   MIDI_PauseSong();
+
+   if ( PositionInTicks < _MIDI_PositionInTicks )
+      {
+      _MIDI_ResetTracks();
+      MIDI_Reset();
+      }
+
+   while( _MIDI_PositionInTicks < PositionInTicks )
+      {
+      if ( _MIDI_ProcessNextTick() )
+         {
+         break;
+         }
+      if ( _MIDI_ActiveTracks == 0 )
+         {
+         _MIDI_ResetTracks();
+         if ( !_MIDI_Loop )
+            {
+            return;
+            }
+         break;
+         }
+      }
+
+   MIDI_SetVolume( _MIDI_TotalVolume );
+   MIDI_ContinueSong();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongTime
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongTime
+   (
+   unsigned long milliseconds
+   )
+
+   {
+   unsigned long mil;
+   unsigned long sec;
+   unsigned long newtime;
+
+   if ( !_MIDI_SongLoaded )
+      {
+      return;
+      }
+
+   MIDI_PauseSong();
+
+   mil = ( ( milliseconds % 1000 ) << TIME_PRECISION ) / 1000;
+   sec = ( milliseconds / 1000 ) << TIME_PRECISION;
+   newtime = sec + mil;
+
+   if ( newtime < _MIDI_Time )
+      {
+      _MIDI_ResetTracks();
+      MIDI_Reset();
+      }
+
+   while( _MIDI_Time < newtime )
+      {
+      if ( _MIDI_ProcessNextTick() )
+         {
+         break;
+         }
+      if ( _MIDI_ActiveTracks == 0 )
+         {
+         _MIDI_ResetTracks();
+         if ( !_MIDI_Loop )
+            {
+            return;
+            }
+         break;
+         }
+      }
+
+   MIDI_SetVolume( _MIDI_TotalVolume );
+   MIDI_ContinueSong();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_SetSongPosition
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MIDI_SetSongPosition
+   (
+   int measure,
+   int beat,
+   int tick
+   )
+
+   {
+   unsigned long pos;
+
+   if ( !_MIDI_SongLoaded )
+      {
+      return;
+      }
+
+   MIDI_PauseSong();
+
+   pos = RELATIVE_BEAT( measure, beat, tick );
+
+   if ( pos < RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) )
+      {
+      _MIDI_ResetTracks();
+      MIDI_Reset();
+      }
+
+   while( RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) < pos )
+      {
+      if ( _MIDI_ProcessNextTick() )
+         {
+         break;
+         }
+      if ( _MIDI_ActiveTracks == 0 )
+         {
+         _MIDI_ResetTracks();
+         if ( !_MIDI_Loop )
+            {
+            return;
+            }
+         break;
+         }
+      }
+
+   MIDI_SetVolume( _MIDI_TotalVolume );
+   MIDI_ContinueSong();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetSongPosition
+
+   Returns the position of the song pointer in Measures, beats, ticks.
+---------------------------------------------------------------------*/
+
+void MIDI_GetSongPosition
+   (
+   songposition *pos
+   )
+
+   {
+   unsigned long mil;
+   unsigned long sec;
+
+   mil = ( _MIDI_Time & ( ( 1 << TIME_PRECISION ) - 1 ) ) * 1000;
+   sec = _MIDI_Time >> TIME_PRECISION;
+   pos->milliseconds = ( mil >> TIME_PRECISION ) + ( sec * 1000 );
+   pos->tickposition = _MIDI_PositionInTicks;
+   pos->measure      = _MIDI_Measure;
+   pos->beat         = _MIDI_Beat;
+   pos->tick         = _MIDI_Tick;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_GetSongLength
+
+   Returns the length of the song.
+---------------------------------------------------------------------*/
+
+void MIDI_GetSongLength
+   (
+   songposition *pos
+   )
+
+   {
+   unsigned long mil;
+   unsigned long sec;
+
+   mil = ( _MIDI_TotalTime & ( ( 1 << TIME_PRECISION ) - 1 ) ) * 1000;
+   sec = _MIDI_TotalTime >> TIME_PRECISION;
+
+   pos->milliseconds = ( mil >> TIME_PRECISION ) + ( sec * 1000 );
+   pos->measure      = _MIDI_TotalMeasures;
+   pos->beat         = _MIDI_TotalBeats;
+   pos->tick         = _MIDI_TotalTicks;
+   pos->tickposition = 0;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_InitEMIDI
+
+   Sets up the EMIDI
+---------------------------------------------------------------------*/
+
+static void _MIDI_InitEMIDI
+   (
+   void
+   )
+
+   {
+   int    event;
+   int    command;
+   int    channel;
+   int    length;
+   int    IncludeFound;
+   track *Track;
+   int    tracknum;
+   int    type;
+   int    c1;
+   int    c2;
+
+   type = EMIDI_GeneralMIDI;
+   switch( MUSIC_SoundDevice )
+      {
+      case SoundBlaster :
+         type = EMIDI_SoundBlaster;
+         break;
+
+      case ProAudioSpectrum :
+         type = EMIDI_ProAudio;
+         break;
+
+      case SoundMan16 :
+         type = EMIDI_SoundMan16;
+         break;
+
+      case Adlib :
+         type = EMIDI_Adlib;
+         break;
+
+      case GenMidi :
+         type = EMIDI_GeneralMIDI;
+         break;
+
+      case SoundCanvas :
+         type = EMIDI_SoundCanvas;
+         break;
+
+      case Awe32 :
+         type = EMIDI_AWE32;
+         break;
+
+      case WaveBlaster :
+         type = EMIDI_WaveBlaster;
+         break;
+
+      case SoundScape :
+         type = EMIDI_Soundscape;
+         break;
+
+      case UltraSound :
+         type = EMIDI_Ultrasound;
+         break;
+      }
+
+   _MIDI_ResetTracks();
+
+   _MIDI_TotalTime     = 0;
+   _MIDI_TotalTicks    = 0;
+   _MIDI_TotalBeats    = 0;
+   _MIDI_TotalMeasures = 0;
+
+   Track = _MIDI_TrackPtr;
+   tracknum = 0;
+   while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
+      {
+      _MIDI_Tick = 0;
+      _MIDI_Beat = 1;
+      _MIDI_Measure = 1;
+      _MIDI_Time = 0;
+      _MIDI_BeatsPerMeasure = 4;
+      _MIDI_TicksPerBeat = _MIDI_Division;
+      _MIDI_TimeBase = 4;
+
+      _MIDI_PositionInTicks = 0;
+      _MIDI_ActiveTracks    = 0;
+      _MIDI_Context         = -1;
+
+      Track->RunningStatus = 0;
+      Track->active        = TRUE;
+
+      Track->EMIDI_ProgramChange = FALSE;
+      Track->EMIDI_VolumeChange  = FALSE;
+      Track->EMIDI_IncludeTrack  = TRUE;
+
+      memset( Track->context, 0, sizeof( Track->context ) );
+
+      while( Track->delay > 0 )
+         {
+         _MIDI_AdvanceTick();
+         Track->delay--;
+         }
+
+      IncludeFound = FALSE;
+      while ( Track->active )
+         {
+         GET_NEXT_EVENT( Track, event );
+
+         if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
+            {
+            switch( event )
+               {
+               case MIDI_SYSEX :
+               case MIDI_SYSEX_CONTINUE :
+                  _MIDI_SysEx( Track );
+                  break;
+
+               case MIDI_META_EVENT :
+                  _MIDI_MetaEvent( Track );
+                  break;
+               }
+
+            if ( Track->active )
+               {
+               Track->delay = _MIDI_ReadDelta( Track );
+               while( Track->delay > 0 )
+                  {
+                  _MIDI_AdvanceTick();
+                  Track->delay--;
+                  }
+               }
+
+            continue;
+            }
+
+         if ( event & MIDI_RUNNING_STATUS )
+            {
+            Track->RunningStatus = event;
+            }
+         else
+            {
+            event = Track->RunningStatus;
+            Track->pos--;
+            }
+
+         channel = GET_MIDI_CHANNEL( event );
+         command = GET_MIDI_COMMAND( event );
+         length = _MIDI_CommandLengths[ command ];
+
+         if ( command == MIDI_CONTROL_CHANGE )
+            {
+            if ( *Track->pos == MIDI_MONO_MODE_ON )
+               {
+               length++;
+               }
+            GET_NEXT_EVENT( Track, c1 );
+            GET_NEXT_EVENT( Track, c2 );
+            length -= 2;
+
+            switch( c1 )
+               {
+               case EMIDI_LOOP_START :
+               case EMIDI_SONG_LOOP_START :
+                  if ( c2 == 0 )
+                     {
+                     Track->context[ 0 ].loopcount = EMIDI_INFINITE;
+                     }
+                  else
+                     {
+                     Track->context[ 0 ].loopcount = c2;
+                     }
+
+                  Track->context[ 0 ].pos              = Track->pos;
+                  Track->context[ 0 ].loopstart        = Track->pos;
+                  Track->context[ 0 ].RunningStatus    = Track->RunningStatus;
+                  Track->context[ 0 ].time             = _MIDI_Time;
+                  Track->context[ 0 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
+                  Track->context[ 0 ].tick             = _MIDI_Tick;
+                  Track->context[ 0 ].beat             = _MIDI_Beat;
+                  Track->context[ 0 ].measure          = _MIDI_Measure;
+                  Track->context[ 0 ].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
+                  Track->context[ 0 ].TicksPerBeat     = _MIDI_TicksPerBeat;
+                  Track->context[ 0 ].TimeBase         = _MIDI_TimeBase;
+                  break;
+
+               case EMIDI_LOOP_END :
+               case EMIDI_SONG_LOOP_END :
+                  if ( c2 == EMIDI_END_LOOP_VALUE )
+                     {
+                     Track->context[ 0 ].loopstart = NULL;
+                     Track->context[ 0 ].loopcount = 0;
+                     }
+                  break;
+
+               case EMIDI_INCLUDE_TRACK :
+                  if ( EMIDI_AffectsCurrentCard( c2, type ) )
+                     {
+                     //printf( "Include track %d on card %d\n", tracknum, c2 );
+                     IncludeFound = TRUE;
+                     Track->EMIDI_IncludeTrack = TRUE;
+                     }
+                  else if ( !IncludeFound )
+                     {
+                     //printf( "Track excluded %d on card %d\n", tracknum, c2 );
+                     IncludeFound = TRUE;
+                     Track->EMIDI_IncludeTrack = FALSE;
+                     }
+                  break;
+
+               case EMIDI_EXCLUDE_TRACK :
+                  if ( EMIDI_AffectsCurrentCard( c2, type ) )
+                     {
+                     //printf( "Exclude track %d on card %d\n", tracknum, c2 );
+                     Track->EMIDI_IncludeTrack = FALSE;
+                     }
+                  break;
+
+               case EMIDI_PROGRAM_CHANGE :
+                  if ( !Track->EMIDI_ProgramChange )
+                     //printf( "Program change on track %d\n", tracknum );
+                  Track->EMIDI_ProgramChange = TRUE;
+                  break;
+
+               case EMIDI_VOLUME_CHANGE :
+                  if ( !Track->EMIDI_VolumeChange )
+                     //printf( "Volume change on track %d\n", tracknum );
+                  Track->EMIDI_VolumeChange = TRUE;
+                  break;
+
+               case EMIDI_CONTEXT_START :
+                  if ( ( c2 > 0 ) && ( c2 < EMIDI_NUM_CONTEXTS ) )
+                     {
+                     Track->context[ c2 ].pos              = Track->pos;
+                     Track->context[ c2 ].loopstart        = Track->context[ 0 ].loopstart;
+                     Track->context[ c2 ].loopcount        = Track->context[ 0 ].loopcount;
+                     Track->context[ c2 ].RunningStatus    = Track->RunningStatus;
+                     Track->context[ c2 ].time             = _MIDI_Time;
+                     Track->context[ c2 ].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
+                     Track->context[ c2 ].tick             = _MIDI_Tick;
+                     Track->context[ c2 ].beat             = _MIDI_Beat;
+                     Track->context[ c2 ].measure          = _MIDI_Measure;
+                     Track->context[ c2 ].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
+                     Track->context[ c2 ].TicksPerBeat     = _MIDI_TicksPerBeat;
+                     Track->context[ c2 ].TimeBase         = _MIDI_TimeBase;
+                     }
+                  break;
+
+               case EMIDI_CONTEXT_END :
+                  break;
+               }
+            }
+
+         Track->pos += length;
+         Track->delay = _MIDI_ReadDelta( Track );
+
+         while( Track->delay > 0 )
+            {
+            _MIDI_AdvanceTick();
+            Track->delay--;
+            }
+         }
+
+      _MIDI_TotalTime = max( _MIDI_TotalTime, _MIDI_Time );
+      if ( RELATIVE_BEAT( _MIDI_Measure, _MIDI_Beat, _MIDI_Tick ) >
+         RELATIVE_BEAT( _MIDI_TotalMeasures, _MIDI_TotalBeats,
+         _MIDI_TotalTicks ) )
+         {
+         _MIDI_TotalTicks    = _MIDI_Tick;
+         _MIDI_TotalBeats    = _MIDI_Beat;
+         _MIDI_TotalMeasures = _MIDI_Measure;
+         }
+
+      Track++;
+      tracknum++;
+      }
+
+   _MIDI_ResetTracks();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_LoadTimbres
+
+   Preloads the timbres on cards that use patch-caching.
+---------------------------------------------------------------------*/
+
+void MIDI_LoadTimbres
+   (
+   void
+   )
+
+   {
+   int    event;
+   int    command;
+   int    channel;
+   int    length;
+   int    Finished;
+   track *Track;
+   int    tracknum;
+
+   Track = _MIDI_TrackPtr;
+   tracknum = 0;
+   while( ( tracknum < _MIDI_NumTracks ) && ( Track != NULL ) )
+      {
+      Finished = FALSE;
+      while ( !Finished )
+         {
+         GET_NEXT_EVENT( Track, event );
+
+         if ( GET_MIDI_COMMAND( event ) == MIDI_SPECIAL )
+            {
+            switch( event )
+               {
+               case MIDI_SYSEX :
+               case MIDI_SYSEX_CONTINUE :
+                  length = _MIDI_ReadDelta( Track );
+                  Track->pos += length;
+                  break;
+
+               case MIDI_META_EVENT :
+                  GET_NEXT_EVENT( Track, command );
+                  GET_NEXT_EVENT( Track, length );
+
+                  if ( command == MIDI_END_OF_TRACK )
+                     {
+                     Finished = TRUE;
+                     }
+
+                  Track->pos += length;
+                  break;
+               }
+
+            if ( !Finished )
+               {
+               _MIDI_ReadDelta( Track );
+               }
+
+            continue;
+            }
+
+         if ( event & MIDI_RUNNING_STATUS )
+            {
+            Track->RunningStatus = event;
+            }
+         else
+            {
+            event = Track->RunningStatus;
+            Track->pos--;
+            }
+
+         channel = GET_MIDI_CHANNEL( event );
+         command = GET_MIDI_COMMAND( event );
+         length = _MIDI_CommandLengths[ command ];
+
+         if ( command == MIDI_CONTROL_CHANGE )
+            {
+            if ( *Track->pos == MIDI_MONO_MODE_ON )
+               {
+               length++;
+               }
+
+            if ( *Track->pos == EMIDI_PROGRAM_CHANGE )
+               {
+               _MIDI_Funcs->LoadPatch( *( Track->pos + 1 ) );
+               }
+            }
+
+         if ( channel == MIDI_RHYTHM_CHANNEL )
+            {
+            if ( command == MIDI_NOTE_ON )
+               {
+               _MIDI_Funcs->LoadPatch( 128 + *Track->pos );
+               }
+            }
+         else
+            {
+            if ( command == MIDI_PROGRAM_CHANGE )
+               {
+               _MIDI_Funcs->LoadPatch( *Track->pos );
+               }
+            }
+         Track->pos += length;
+         _MIDI_ReadDelta( Track );
+         }
+      Track++;
+      tracknum++;
+      }
+
+   _MIDI_ResetTracks();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void MIDI_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void MIDI_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( MIDI_LockStart, MIDI_LockEnd );
+   DPMI_UnlockMemory( ( void * )&_MIDI_CommandLengths[ 0 ],
+      sizeof( _MIDI_CommandLengths ) );
+   DPMI_Unlock( _MIDI_TrackPtr );
+   DPMI_Unlock( _MIDI_NumTracks );
+   DPMI_Unlock( _MIDI_SongActive );
+   DPMI_Unlock( _MIDI_SongLoaded );
+   DPMI_Unlock( _MIDI_Loop );
+   DPMI_Unlock( _MIDI_PlayRoutine );
+   DPMI_Unlock( _MIDI_Division );
+   DPMI_Unlock( _MIDI_ActiveTracks );
+   DPMI_Unlock( _MIDI_TotalVolume );
+   DPMI_Unlock( _MIDI_ChannelVolume );
+   DPMI_Unlock( _MIDI_Funcs );
+   DPMI_Unlock( _MIDI_PositionInTicks );
+   DPMI_Unlock( _MIDI_Division );
+   DPMI_Unlock( _MIDI_Tick );
+   DPMI_Unlock( _MIDI_Beat );
+   DPMI_Unlock( _MIDI_Measure );
+   DPMI_Unlock( _MIDI_Time );
+   DPMI_Unlock( _MIDI_BeatsPerMeasure );
+   DPMI_Unlock( _MIDI_TicksPerBeat );
+   DPMI_Unlock( _MIDI_TimeBase );
+   DPMI_Unlock( _MIDI_FPSecondsPerTick );
+   DPMI_Unlock( _MIDI_Context );
+   DPMI_Unlock( _MIDI_TotalTime );
+   DPMI_Unlock( _MIDI_TotalTicks );
+   DPMI_Unlock( _MIDI_TotalBeats );
+   DPMI_Unlock( _MIDI_TotalMeasures );
+   DPMI_Unlock( MIDI_Tempo );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MIDI_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int MIDI_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( MIDI_LockStart, MIDI_LockEnd );
+   status |= DPMI_LockMemory( ( void * )&_MIDI_CommandLengths[ 0 ],
+      sizeof( _MIDI_CommandLengths ) );
+   status |= DPMI_Lock( _MIDI_TrackPtr );
+   status |= DPMI_Lock( _MIDI_NumTracks );
+   status |= DPMI_Lock( _MIDI_SongActive );
+   status |= DPMI_Lock( _MIDI_SongLoaded );
+   status |= DPMI_Lock( _MIDI_Loop );
+   status |= DPMI_Lock( _MIDI_PlayRoutine );
+   status |= DPMI_Lock( _MIDI_Division );
+   status |= DPMI_Lock( _MIDI_ActiveTracks );
+   status |= DPMI_Lock( _MIDI_TotalVolume );
+   status |= DPMI_Lock( _MIDI_ChannelVolume );
+   status |= DPMI_Lock( _MIDI_Funcs );
+   status |= DPMI_Lock( _MIDI_PositionInTicks );
+   status |= DPMI_Lock( _MIDI_Division );
+   status |= DPMI_Lock( _MIDI_Tick );
+   status |= DPMI_Lock( _MIDI_Beat );
+   status |= DPMI_Lock( _MIDI_Measure );
+   status |= DPMI_Lock( _MIDI_Time );
+   status |= DPMI_Lock( _MIDI_BeatsPerMeasure );
+   status |= DPMI_Lock( _MIDI_TicksPerBeat );
+   status |= DPMI_Lock( _MIDI_TimeBase );
+   status |= DPMI_Lock( _MIDI_FPSecondsPerTick );
+   status |= DPMI_Lock( _MIDI_Context );
+   status |= DPMI_Lock( _MIDI_TotalTime );
+   status |= DPMI_Lock( _MIDI_TotalTicks );
+   status |= DPMI_Lock( _MIDI_TotalBeats );
+   status |= DPMI_Lock( _MIDI_TotalMeasures );
+   status |= DPMI_Lock( MIDI_Tempo );
+
+   if ( status != DPMI_Ok )
+      {
+      MIDI_UnlockMemory();
+//      MIDI_SetErrorCode( MIDI_DPMI_Error );
+      return( MIDI_Error );
+      }
+
+   return( MIDI_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/midi.h
@@ -1,0 +1,98 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MIDI.H
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Public header for MIDI.C.  Midi song file playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __MIDI_H
+#define __MIDI_H
+
+enum MIDI_Errors
+   {
+   MIDI_Warning = -2,
+   MIDI_Error   = -1,
+   MIDI_Ok      = 0,
+   MIDI_NullMidiModule,
+   MIDI_InvalidMidiFile,
+   MIDI_UnknownMidiFormat,
+   MIDI_NoTracks,
+   MIDI_InvalidTrack,
+   MIDI_NoMemory,
+   MIDI_DPMI_Error
+   };
+
+
+#define MIDI_PASS_THROUGH 1
+#define MIDI_DONT_PLAY    0
+
+#define MIDI_MaxVolume 255
+
+extern char MIDI_PatchMap[ 128 ];
+
+typedef struct
+   {
+   void ( *NoteOff )( int channel, int key, int velocity );
+   void ( *NoteOn )( int channel, int key, int velocity );
+   void ( *PolyAftertouch )( int channel, int key, int pressure );
+   void ( *ControlChange )( int channel, int number, int value );
+   void ( *ProgramChange )( int channel, int program );
+   void ( *ChannelAftertouch )( int channel, int pressure );
+   void ( *PitchBend )( int channel, int lsb, int msb );
+   void ( *ReleasePatches )( void );
+   void ( *LoadPatch )( int number );
+   void ( *SetVolume )( int volume );
+   int  ( *GetVolume )( void );
+   } midifuncs;
+
+void MIDI_RerouteMidiChannel( int channel, int cdecl ( *function )( int event, int c1, int c2 ) );
+int  MIDI_AllNotesOff( void );
+void MIDI_SetUserChannelVolume( int channel, int volume );
+void MIDI_ResetUserChannelVolume( void );
+int  MIDI_Reset( void );
+int  MIDI_SetVolume( int volume );
+int  MIDI_GetVolume( void );
+void MIDI_SetMidiFuncs( midifuncs *funcs );
+void MIDI_SetContext( int context );
+int  MIDI_GetContext( void );
+void MIDI_SetLoopFlag( int loopflag );
+void MIDI_ContinueSong( void );
+void MIDI_PauseSong( void );
+int  MIDI_SongPlaying( void );
+void MIDI_StopSong( void );
+int  MIDI_PlaySong( unsigned char *song, int loopflag );
+void MIDI_SetTempo( int tempo );
+int  MIDI_GetTempo( void );
+void MIDI_SetSongTick( unsigned long PositionInTicks );
+void MIDI_SetSongTime( unsigned long milliseconds );
+void MIDI_SetSongPosition( int measure, int beat, int tick );
+void MIDI_GetSongPosition( songposition *pos );
+void MIDI_GetSongLength( songposition *pos );
+void MIDI_LoadTimbres( void );
+void MIDI_UnlockMemory( void );
+int  MIDI_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/mpu401.c
@@ -1,0 +1,451 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MPU401.C
+
+   author: James R. Dose
+   date:   January 1, 1994
+
+   Low level routines to support sending of MIDI data to MPU401
+   compatible MIDI interfaces.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <conio.h>
+#include <dos.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "dpmi.h"
+#include "user.h"
+#include "mpu401.h"
+
+#define MIDI_NOTE_OFF         0x80
+#define MIDI_NOTE_ON          0x90
+#define MIDI_POLY_AFTER_TCH   0xA0
+#define MIDI_CONTROL_CHANGE   0xB0
+#define MIDI_PROGRAM_CHANGE   0xC0
+#define MIDI_AFTER_TOUCH      0xD0
+#define MIDI_PITCH_BEND       0xE0
+#define MIDI_META_EVENT       0xFF
+#define MIDI_END_OF_TRACK     0x2F
+#define MIDI_TEMPO_CHANGE     0x51
+#define MIDI_MONO_MODE_ON     0x7E
+#define MIDI_ALL_NOTES_OFF    0x7B
+
+int MPU_BaseAddr = MPU_DefaultAddress;
+
+//unsigned MPU_Delay = 500;
+//unsigned MPU_Delay = 5000;
+unsigned MPU_Delay = 0x5000;
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define MPU_LockStart MPU_SendMidi
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_SendMidi
+
+   Sends a byte of MIDI data to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_SendMidi
+   (
+   int data
+   )
+
+   {
+   int      port = MPU_BaseAddr + 1;
+   unsigned count;
+
+   count = MPU_Delay;
+   while( count > 0 )
+      {
+      // check if status port says we're ready for write
+      if ( !( inp( port ) & MPU_ReadyToWrite ) )
+         {
+         break;
+         }
+
+      count--;
+      }
+
+   port--;
+
+   // Send the midi data
+   outp( port, data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_NoteOff
+
+   Sends a full MIDI note off event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_NoteOff
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   MPU_SendMidi( MIDI_NOTE_OFF | channel );
+   MPU_SendMidi( key );
+   MPU_SendMidi( velocity );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_NoteOn
+
+   Sends a full MIDI note on event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_NoteOn
+   (
+   int channel,
+   int key,
+   int velocity
+   )
+
+   {
+   MPU_SendMidi( MIDI_NOTE_ON | channel );
+   MPU_SendMidi( key );
+   MPU_SendMidi( velocity );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_PolyAftertouch
+
+   Sends a full MIDI polyphonic aftertouch event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_PolyAftertouch
+   (
+   int channel,
+   int key,
+   int pressure
+   )
+
+   {
+   MPU_SendMidi( MIDI_POLY_AFTER_TCH | channel );
+   MPU_SendMidi( key );
+   MPU_SendMidi( pressure );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_ControlChange
+
+   Sends a full MIDI control change event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ControlChange
+   (
+   int channel,
+   int number,
+   int value
+   )
+
+   {
+   MPU_SendMidi( MIDI_CONTROL_CHANGE | channel );
+   MPU_SendMidi( number );
+   MPU_SendMidi( value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_ProgramChange
+
+   Sends a full MIDI program change event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ProgramChange
+   (
+   int channel,
+   int program
+   )
+
+   {
+   MPU_SendMidi( MIDI_PROGRAM_CHANGE | channel );
+   MPU_SendMidi( program );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_ChannelAftertouch
+
+   Sends a full MIDI channel aftertouch event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_ChannelAftertouch
+   (
+   int channel,
+   int pressure
+   )
+
+   {
+   MPU_SendMidi( MIDI_AFTER_TOUCH | channel );
+   MPU_SendMidi( pressure );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_PitchBend
+
+   Sends a full MIDI pitch bend event out to the music device.
+---------------------------------------------------------------------*/
+
+void MPU_PitchBend
+   (
+   int channel,
+   int lsb,
+   int msb
+   )
+
+   {
+   MPU_SendMidi( MIDI_PITCH_BEND | channel );
+   MPU_SendMidi( lsb );
+   MPU_SendMidi( msb );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_SendCommand
+
+   Sends a command to the MPU401 card.
+---------------------------------------------------------------------*/
+
+void MPU_SendCommand
+   (
+   int data
+   )
+
+   {
+   int      port = MPU_BaseAddr + 1;
+   unsigned count;
+
+   count = 0xffff;
+   while( count > 0 )
+      {
+      // check if status port says we're ready for write
+      if ( !( inp( port ) & MPU_ReadyToWrite ) )
+         {
+         break;
+         }
+      count--;
+      }
+
+   outp( port, data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_Reset
+
+   Resets the MPU401 card.
+---------------------------------------------------------------------*/
+
+int MPU_Reset
+   (
+   void
+   )
+
+   {
+   int      port = MPU_BaseAddr + 1;
+   unsigned count;
+
+   // Output "Reset" command via Command port
+   MPU_SendCommand( MPU_CmdReset );
+
+   // Wait for status port to be ready for read
+   count = 0xffff;
+   while( count > 0 )
+      {
+      if ( !( inp( port ) & MPU_ReadyToRead ) )
+         {
+         port--;
+
+         // Check for a successful reset
+         if ( inp( port ) == MPU_CmdAcknowledge )
+            {
+            return( MPU_Ok );
+            }
+
+         port++;
+         }
+      count--;
+      }
+
+   // Failed to reset : MPU-401 not detected
+   return( MPU_NotFound );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_EnterUART
+
+   Sets the MPU401 card to operate in UART mode.
+---------------------------------------------------------------------*/
+
+int MPU_EnterUART
+   (
+   void
+   )
+
+   {
+   int      port = MPU_BaseAddr + 1;
+   unsigned count;
+
+   // Output "Enter UART" command via Command port
+   MPU_SendCommand( MPU_CmdEnterUART );
+
+   // Wait for status port to be ready for read
+   count = 0xffff;
+   while( count > 0 )
+      {
+      if ( !( inp( port ) & MPU_ReadyToRead ) )
+         {
+         port--;
+
+         // Check for a successful reset
+         if ( inp( port ) == MPU_CmdAcknowledge )
+            {
+            return( MPU_Ok );
+            }
+
+         port++;
+         }
+      count--;
+      }
+
+   // Failed to reset : MPU-401 not detected
+   return( MPU_UARTFailed );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_Init
+
+   Detects and initializes the MPU401 card.
+---------------------------------------------------------------------*/
+
+int MPU_Init
+   (
+   int addr
+   )
+
+   {
+   int status;
+   int count;
+   char *ptr;
+
+   ptr = USER_GetText( "MPUDELAY" );
+   if ( ptr != NULL )
+      {
+      MPU_Delay = ( unsigned )atol( ptr );
+      }
+
+   MPU_BaseAddr = addr;
+
+   count = 4;
+   while( count > 0 )
+      {
+      status = MPU_Reset();
+      if ( status == MPU_Ok )
+         {
+         return( MPU_EnterUART() );
+         }
+      count--;
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void MPU_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_UnlockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+void MPU_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( MPU_LockStart, MPU_LockEnd );
+   DPMI_Unlock( MPU_BaseAddr );
+   DPMI_Unlock( MPU_Delay );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MPU_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int MPU_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( MPU_LockStart, MPU_LockEnd );
+   status |= DPMI_Lock( MPU_BaseAddr );
+   status |= DPMI_Lock( MPU_Delay );
+
+   if ( status != DPMI_Ok )
+      {
+      MPU_UnlockMemory();
+      return( MPU_Error );
+      }
+
+   return( MPU_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/mpu401.h
@@ -1,0 +1,61 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __MPU401_H
+#define __MPU401_H
+
+#define MPU_DefaultAddress 0x330
+
+enum MPU_ERRORS
+   {
+   MPU_Warning = -2,
+   MPU_Error = -1,
+   MPU_Ok = 0,
+   MPU_DPMI_Error
+   };
+
+#define MPU_NotFound       -1
+#define MPU_UARTFailed     -2
+
+#define MPU_ReadyToWrite   0x40
+#define MPU_ReadyToRead    0x80
+#define MPU_CmdEnterUART   0x3f
+#define MPU_CmdReset       0xff
+#define MPU_CmdAcknowledge 0xfe
+
+extern int MPU_BaseAddr;
+extern unsigned MPU_Delay;
+
+void MPU_SendCommand( int data );
+void MPU_SendMidi( int data );
+int  MPU_Reset( void );
+int  MPU_EnterUART( void );
+int  MPU_Init( int addr );
+void MPU_ResetMidi( void );
+void MPU_NoteOff( int channel, int key, int velocity );
+void MPU_NoteOn( int channel, int key, int velocity );
+void MPU_PolyAftertouch( int channel, int key, int pressure );
+void MPU_ControlChange( int channel, int number, int value );
+void MPU_ProgramChange( int channel, int program );
+void MPU_ChannelAftertouch( int channel, int pressure );
+void MPU_PitchBend( int channel, int lsb, int msb );
+void MPU_UnlockMemory( void );
+int  MPU_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/multivoc.c
@@ -1,0 +1,3470 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MULTIVOC.C
+
+   author: James R. Dose
+   date:   December 20, 1993
+
+   Routines to provide multichannel digitized sound playback for
+   Sound Blaster compatible sound cards.
+
+   (c) Copyright 1993 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef PLAT_DOS
+#include <dos.h>
+#include <conio.h>
+#endif
+
+#include "util.h"
+#include "dpmi.h"
+#include "usrhooks.h"
+#include "interrup.h"
+#include "dma.h"
+#include "linklist.h"
+#include "sndcards.h"
+
+#ifdef PLAT_DOS
+#include "blaster.h"
+#include "sndscape.h"
+#include "sndsrc.h"
+#include "pas16.h"
+#include "guswave.h"
+#else
+#include "dsl.h"
+#endif
+
+#include "pitch.h"
+#include "multivoc.h"
+#include "_multivc.h"
+#include "debugio.h"
+#include "..\sounddebugdefs.h"
+
+// for the Mutex
+#include <SDL.h>
+
+#define RoundFixed( fixedval, bits )            \
+        (                                       \
+          (                                     \
+            (fixedval) + ( 1 << ( (bits) - 1 ) )\
+          ) >> (bits)                           \
+        )
+
+// #define IS_QUIET( ptr )  ( ( void * )( ptr ) == ( void * )&MV_VolumeTable[ 0 ] )
+#define IS_QUIET( vol ) ( ( vol ) == 0 )
+
+static int       MV_ReverbLevel;
+int       MV_ReverbDelay;
+#if 0
+static VOLUME16 *MV_ReverbTable = NULL;
+#else
+static int       MV_ReverbTable = -1;
+#endif
+
+//static signed short MV_VolumeTable[ MV_MaxVolume + 1 ][ 256 ];
+//static signed short MV_VolumeTable[ 63 + 1 ][ 256 ];
+
+//static Pan MV_PanTable[ MV_NumPanPositions ][ MV_MaxVolume + 1 ];
+static Pan MV_PanTable[ MV_NumPanPositions ][ 63 + 1 ];
+
+static int MV_Installed   = FALSE;
+static int MV_SoundCard   = SoundBlaster;
+static int MV_TotalVolume = MV_MaxTotalVolume;
+static int MV_MaxVoices   = 1;
+static int MV_Recording;
+
+int MV_BufferSize = MixBufferSize;
+static int MV_BufferLength;
+
+static int MV_NumberOfBuffers = NumberOfBuffers;
+
+static int MV_MixMode    = MONO_8BIT;
+int MV_Channels   = 1;
+static int MV_Bits       = 8;
+
+static int MV_Silence    = SILENCE_8BIT;
+static int MV_SwapLeftRight = FALSE;
+
+static int MV_RequestedMixRate;
+int MV_MixRate;
+
+static int MV_DMAChannel = -1;
+static int MV_BuffShift;
+
+static int MV_TotalMemory;
+static int MV_FooMemory;
+
+static int   MV_BufferDescriptor;
+static int   MV_BufferEmpty[ NumberOfBuffers ];
+char *MV_MixBuffer[ NumberOfBuffers + 1 ];
+double *MV_FooBuffer = NULL;
+
+static VoiceNode *MV_Voices = NULL;
+
+static volatile VoiceNode VoiceList;
+static volatile VoiceNode VoicePool;
+
+/*static*/ int MV_MixPage      = 0;
+static int MV_VoiceHandle  = MV_MinVoiceHandle;
+
+static void ( *MV_CallBackFunc )( unsigned long ) = NULL;
+static void ( *MV_RecordFunc )( char *ptr, int length ) = NULL;
+static void ( *MV_MixFunction )( VoiceNode *voice/*, int buffer */);
+
+int MV_MaxVolume = 63;
+
+int *MV_GLast, *MV_GPos, *MV_GVal;
+
+// char  *MV_HarshClipTable;
+char  *MV_MixDestination;
+#if 0
+short *MV_LeftVolume;
+short *MV_RightVolume;
+#else
+int    MV_LeftVolume;
+int    MV_RightVolume;
+#endif
+int    MV_SampleSize = 1;
+int    MV_RightChannelOffset;
+
+unsigned long MV_MixPosition;
+
+int MV_ErrorCode = MV_Ok;
+
+#define MV_SetErrorCode( status ) \
+   MV_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: MV_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *MV_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case MV_Warning :
+      case MV_Error :
+         ErrorString = MV_ErrorString( MV_ErrorCode );
+         break;
+
+      case MV_Ok :
+         ErrorString = "Multivoc ok.\n";
+         break;
+
+      case MV_UnsupportedCard :
+         ErrorString = "Selected sound card is not supported by Multivoc.\n";
+         break;
+
+      case MV_NotInstalled :
+         ErrorString = "Multivoc not installed.\n";
+         break;
+
+      case MV_NoVoices :
+         ErrorString = "No free voices available to Multivoc.\n";
+         break;
+
+      case MV_NoMem :
+         ErrorString = "Out of memory in Multivoc.\n";
+         break;
+
+      case MV_VoiceNotFound :
+         ErrorString = "No voice with matching handle found.\n";
+         break;
+
+#ifdef PLAT_DOS
+      case MV_BlasterError :
+         ErrorString = BLASTER_ErrorString( BLASTER_Error );
+         break;
+
+      case MV_PasError :
+         ErrorString = PAS_ErrorString( PAS_Error );
+         break;
+
+      case MV_SoundScapeError :
+         ErrorString = SOUNDSCAPE_ErrorString( SOUNDSCAPE_Error );
+         break;
+
+      #ifndef SOUNDSOURCE_OFF
+      case MV_SoundSourceError :
+         ErrorString = SS_ErrorString( SS_Error );
+         break;
+      #endif
+#endif
+
+      case MV_DPMI_Error :
+         ErrorString = "DPMI Error in Multivoc.\n";
+         break;
+
+      case MV_InvalidVOCFile :
+         ErrorString = "Invalid VOC file passed in to Multivoc.\n";
+         break;
+
+      case MV_InvalidWAVFile :
+         ErrorString = "Invalid WAV file passed in to Multivoc.\n";
+         break;
+
+      case MV_InvalidMixMode :
+         ErrorString = "Invalid mix mode request in Multivoc.\n";
+         break;
+
+      case MV_SoundSourceFailure :
+         ErrorString = "Sound Source playback failed.\n";
+         break;
+
+      case MV_IrqFailure :
+         ErrorString = "Playback failed, possibly due to an invalid or conflicting IRQ.\n";
+         break;
+
+      case MV_DMAFailure :
+         ErrorString = "Playback failed, possibly due to an invalid or conflicting DMA channel.\n";
+         break;
+
+      case MV_DMA16Failure :
+         ErrorString = "Playback failed, possibly due to an invalid or conflicting DMA channel.  \n"
+                       "Make sure the 16-bit DMA channel is correct.\n";
+         break;
+
+      case MV_NullRecordFunction :
+         ErrorString = "Null record function passed to MV_StartRecording.\n";
+         break;
+
+      default :
+         ErrorString = "Unknown Multivoc error code.\n";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define MV_LockStart MV_Mix
+
+
+/*---------------------------------------------------------------------
+   Function: MV_Mix
+
+   Mixes the sound into the buffer.
+---------------------------------------------------------------------*/
+
+static void MV_Mix( VoiceNode *voice )
+{
+   char          *start;
+   int            length;
+   long           voclength;
+   unsigned long  position;
+   unsigned long  rate;
+   unsigned long  FixedPointBufferSize;
+
+   if ( ( voice->length == 0 ) 
+	   && ( voice->GetSound( voice ) != KeepPlaying ) )
+      {
+      return;
+      }
+
+   length               = MixBufferSize;
+   FixedPointBufferSize = voice->FixedPointBufferSize;
+
+   MV_MixDestination    = (char*)MV_FooBuffer;
+   MV_LeftVolume        = voice->LeftVolume;
+   MV_RightVolume       = voice->RightVolume;
+   MV_GLast             = &voice->GLast;
+   MV_GPos              = &voice->GPos;
+   MV_GVal              = (int*)&voice->GVal;
+
+   if ( ( MV_Channels == 2 ) && ( IS_QUIET( MV_LeftVolume ) ) )
+      {
+      MV_LeftVolume      = MV_RightVolume;
+      MV_MixDestination += 8;
+      }
+
+   // Add this voice to the mix
+   while( length > 0 )
+      {
+      start    = voice->sound;
+      rate     = voice->RateScale;
+      position = voice->position;
+
+      // Check if the last sample in this buffer would be
+      // beyond the length of the sample block
+      if ( ( position + FixedPointBufferSize ) >= voice->length )
+         {
+         if ( position < voice->length )
+            {
+            voclength = ( voice->length - position + rate - 1 ) / rate;
+            }
+         else
+            {
+            voice->GetSound( voice );
+            return;
+            }
+         }
+      else
+         {
+         voclength = length;
+         }
+
+      voice->mix( position, rate, start, voclength );
+
+      if ( voclength & 1 )
+         {
+         MV_MixPosition += rate;
+         voclength -= 1;
+         }
+      voice->position = MV_MixPosition;
+
+      length -= voclength;
+
+      if ( voice->position >= voice->length )
+         {
+         // Get the next block of sound
+         if ( voice->GetSound( voice ) != KeepPlaying )
+            {
+            return;
+            }
+
+         if ( length > 0 )
+            {
+            // Get the position of the last sample in the buffer
+            FixedPointBufferSize = voice->RateScale * ( length - 1 );
+            }
+         }
+      }
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayVoice
+
+   Adds a voice to the play list.
+---------------------------------------------------------------------*/
+
+void MV_PlayVoice( VoiceNode *voice )
+{
+   unsigned flags;
+
+   flags = DisableInterrupts();
+   LL_SortedInsertion( &VoiceList, voice, prev, next, VoiceNode, priority );
+
+   ++sounddebugActiveSounds;
+   ++sounddebugAllocateSoundCalls;
+
+   RestoreInterrupts( flags );
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StopVoice
+
+   Removes the voice from the play list and adds it to the free list.
+---------------------------------------------------------------------*/
+
+void MV_StopVoice( VoiceNode *voice )
+{
+   VoiceNode* pPrev;
+   VoiceNode* pNext;
+
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   
+   pPrev = voice->prev;
+   pNext = voice->next;
+
+   // move the voice from the play list to the free list
+   LL_Remove( voice, next, prev );
+   LL_Add( &VoicePool, voice, next, prev );
+
+   if(pPrev == NULL)
+   {
+	   printf("(MV_StopVoice) pPrev is NULL, this could be a problem.\n");
+   }
+
+   if(pNext == NULL)
+   {
+	   printf("(MV_StopVoice) pNext is NULL, this could be a problem.\n");
+   }
+
+   --sounddebugActiveSounds;
+   ++sounddebugDeallocateSoundCalls;
+   
+
+   RestoreInterrupts( flags );
+}
+
+
+/*---------------------------------------------------------------------
+   Function: MV_ServiceVoc
+
+   Starts playback of the waiting buffer and mixes the next one.
+---------------------------------------------------------------------*/
+
+// static int backcolor = 1;
+
+void MV_ServiceVoc
+(
+ void
+ )
+ 
+{
+	VoiceNode *voice;
+	VoiceNode *next;
+	char      *buffer;
+	
+#ifdef PLAT_DOS
+	if ( MV_DMAChannel >= 0 )
+	{
+		// Get the currently playing buffer
+		buffer = ( char * )DMA_GetCurrentPos( MV_DMAChannel );
+		MV_MixPage   = ( unsigned )( buffer - MV_MixBuffer[ 0 ] );
+		MV_MixPage >>= MV_BuffShift;
+	}
+#endif
+	
+	// Toggle which buffer we'll mix next
+	MV_MixPage++;
+	if ( MV_MixPage >= MV_NumberOfBuffers )
+	{
+		MV_MixPage -= MV_NumberOfBuffers;
+	}
+	
+	{
+		ClearBuffer_DW( MV_FooBuffer, 0, sizeof(double) / 4 * MV_BufferSize / MV_SampleSize * MV_Channels);
+		MV_BufferEmpty[ MV_MixPage ] = TRUE;
+	}
+	
+	// Play any waiting voices
+	for( voice = VoiceList.next; voice != &VoiceList; voice = next )
+	{
+		//      if ( ( voice < &MV_Voices[ 0 ] ) || ( voice > &MV_Voices[ 8 ] ) )
+		//         {
+		//         SetBorderColor(backcolor++);
+		//         break;
+		//         }
+
+		if(NULL == voice->GetSound)
+		{
+			#ifdef _DEBUG
+				printf("MV_ServiceVoc() voice->GetSound == NULL, break;\n");
+			#endif
+
+			// This sound is null, early out, or face a nasty crash.
+			break;		
+		}
+		
+		MV_BufferEmpty[ MV_MixPage ] = FALSE;
+		
+		MV_MixFunction( voice );
+	
+		next = voice->next;
+		
+		// Is this voice done?
+		if ( !voice->Playing )
+		{
+			MV_StopVoice( voice );
+			
+			if ( MV_CallBackFunc )
+            {
+				MV_CallBackFunc( voice->callbackval );
+            }
+		}
+	}
+	
+	if ( MV_ReverbLevel > 0)
+	{
+		if (MV_ReverbTable != -1) MV_FPReverb(MV_ReverbTable);
+	}
+
+	{
+		char *dest;
+		int   count;
+		
+		dest = MV_MixBuffer[ MV_MixPage ];
+		count = MV_BufferSize / MV_SampleSize * MV_Channels;
+		if ( MV_Bits == 16 )
+		{
+			MV_16BitDownmix(dest, count);
+		}
+		else
+		{
+			MV_8BitDownmix(dest, count);
+		}
+			
+	}
+}
+
+#ifdef PLAT_DOS
+int leftpage  = -1;
+int rightpage = -1;
+
+void MV_ServiceGus( char **ptr, unsigned long *length )
+   {
+   if ( leftpage == MV_MixPage )
+      {
+      MV_ServiceVoc();
+      }
+
+   leftpage = MV_MixPage;
+
+   *ptr = MV_MixBuffer[ MV_MixPage ];
+   *length = MV_BufferSize;
+   }
+
+void MV_ServiceRightGus( char **ptr, unsigned long *length )
+   {
+   if ( rightpage == MV_MixPage )
+      {
+      MV_ServiceVoc();
+      }
+
+   rightpage = MV_MixPage;
+
+   *ptr = MV_MixBuffer[ MV_MixPage ] + MV_RightChannelOffset;
+   *length = MV_BufferSize;
+   }
+#endif
+
+/*---------------------------------------------------------------------
+   Function: MV_GetNextVOCBlock
+
+   Interperate the information of a VOC format sound file.
+---------------------------------------------------------------------*/
+
+playbackstatus MV_GetNextVOCBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   unsigned char *ptr;
+   int            blocktype;
+   int            lastblocktype;
+   unsigned long  blocklength;
+   unsigned long  samplespeed;
+   unsigned int   tc;
+   int            packtype;
+   int            voicemode;
+   int            done;
+   unsigned       BitsPerSample;
+   unsigned       Channels;
+   unsigned       Format;
+
+   if ( voice->BlockLength > 0 )
+      {
+      voice->position    -= voice->length;
+      voice->sound       += voice->length >> 16;
+      if ( voice->bits == 16 )
+         {
+         voice->sound += voice->length >> 16;
+         }
+      voice->length       = min( voice->BlockLength, 0x8000 );
+      voice->BlockLength -= voice->length;
+      voice->length     <<= 16;
+      return( KeepPlaying );
+      }
+
+   if ( ( voice->length > 0 ) && ( voice->LoopEnd != NULL ) &&
+      ( voice->LoopStart != NULL ) )
+      {
+      voice->BlockLength  = voice->LoopSize;
+      voice->sound        = voice->LoopStart;
+      voice->position     = 0;
+      voice->length       = min( voice->BlockLength, 0x8000 );
+      voice->BlockLength -= voice->length;
+      voice->length     <<= 16;
+      return( KeepPlaying );
+      }
+
+   ptr = ( unsigned char * )voice->NextBlock;
+
+   voice->Playing = TRUE;
+
+   voicemode = 0;
+   lastblocktype = 0;
+   packtype = 0;
+
+   done = FALSE;
+   while( !done )
+      {
+      // Stop playing if we get a NULL pointer
+      if ( ptr == NULL )
+         {
+         voice->Playing = FALSE;
+         done = TRUE;
+         break;
+         }
+
+      blocktype = ( int )*ptr;
+      blocklength = ( *( unsigned long * )( ptr + 1 ) ) & 0x00ffffff;
+      ptr += 4;
+
+      switch( blocktype )
+         {
+         case 0 :
+            // End of data
+            if ( ( voice->LoopStart == NULL ) ||
+               ( voice->LoopStart >= ( ptr - 4 ) ) )
+               {
+               voice->Playing = FALSE;
+               done = TRUE;
+               }
+            else
+               {
+               voice->BlockLength  = ( ptr - 4 ) - (unsigned char *)voice->LoopStart;
+               voice->sound        = voice->LoopStart;
+               voice->position     = 0;
+               voice->length       = min( voice->BlockLength, 0x8000 );
+               voice->BlockLength -= voice->length;
+               voice->length     <<= 16;
+               return( KeepPlaying );
+               }
+            break;
+
+         case 1 :
+            // Sound data block
+            voice->bits  = 8;
+            if ( lastblocktype != 8 )
+               {
+               tc = ( unsigned int )*ptr << 8;
+               packtype = *( ptr + 1 );
+               }
+
+            ptr += 2;
+            blocklength -= 2;
+
+            samplespeed = 256000000L / ( 65536 - tc );
+
+            // Skip packed or stereo data
+            if ( ( packtype != 0 ) || ( voicemode != 0 ) )
+               {
+               ptr += blocklength;
+               }
+            else
+               {
+               done = TRUE;
+               }
+            voicemode = 0;
+            break;
+
+         case 2 :
+            // Sound continuation block
+            samplespeed = voice->SamplingRate;
+            done = TRUE;
+            break;
+
+         case 3 :
+            // Silence
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 4 :
+            // Marker
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 5 :
+            // ASCII string
+            // Not implimented.
+            ptr += blocklength;
+            break;
+
+         case 6 :
+            // Repeat begin
+            if ( voice->LoopEnd == NULL )
+               {
+               voice->LoopCount = *( unsigned short * )ptr;
+               voice->LoopStart = ptr + blocklength;
+               }
+            ptr += blocklength;
+            break;
+
+         case 7 :
+            // Repeat end
+            ptr += blocklength;
+            if ( lastblocktype == 6 )
+               {
+               voice->LoopCount = 0;
+               }
+            else
+               {
+               if ( ( voice->LoopCount > 0 ) && ( voice->LoopStart != NULL ) )
+                  {
+                  ptr = voice->LoopStart;
+                  if ( voice->LoopCount < 0xffff )
+                     {
+                     voice->LoopCount--;
+                     if ( voice->LoopCount == 0 )
+                        {
+                        voice->LoopStart = NULL;
+                        }
+                     }
+                  }
+               }
+            break;
+
+         case 8 :
+            // Extended block
+            voice->bits  = 8;
+            tc = *( unsigned short * )ptr;
+            packtype = *( ptr + 2 );
+            voicemode = *( ptr + 3 );
+            ptr += blocklength;
+            break;
+
+         case 9 :
+            // New sound data block
+
+            samplespeed = *( unsigned long * )ptr;
+            BitsPerSample = ( unsigned )*( ptr + 4 );
+            Channels = ( unsigned )*( ptr + 5 );
+            Format = ( unsigned )*( unsigned short * )( ptr + 6 );
+
+            if ( ( BitsPerSample == 8 ) && ( Channels == 1 ) &&
+               ( Format == VOC_8BIT ) )
+               {
+               ptr         += 12;
+               blocklength -= 12;
+               voice->bits  = 8;
+               done         = TRUE;
+               }
+            else if ( ( BitsPerSample == 16 ) && ( Channels == 1 ) &&
+               ( Format == VOC_16BIT ) )
+               {
+               ptr         += 12;
+               blocklength -= 12;
+               voice->bits  = 16;
+               done         = TRUE;
+               }
+            else
+               {
+               ptr += blocklength;
+               }
+            break;
+
+         default :
+            // Unknown data.  Probably not a VOC file.
+            voice->Playing = FALSE;
+            done = TRUE;
+            break;
+         }
+
+      lastblocktype = blocktype;
+      }
+
+   if ( voice->Playing )
+      {
+      voice->NextBlock    = ptr + blocklength;
+      voice->sound        = ptr;
+
+      voice->SamplingRate = samplespeed;
+      voice->RateScale    = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate;
+
+      // Multiply by MixBufferSize - 1
+      voice->FixedPointBufferSize = ( voice->RateScale * MixBufferSize ) -
+         voice->RateScale;
+
+      if ( voice->LoopEnd != NULL )
+         {
+         if ( blocklength > ( unsigned long )voice->LoopEnd )
+            {
+            blocklength = ( unsigned long )voice->LoopEnd;
+            }
+         else
+            {
+            voice->LoopEnd = ( char * )blocklength;
+            }
+
+         voice->LoopStart = voice->sound + ( unsigned long )voice->LoopStart;
+         voice->LoopEnd   = voice->sound + ( unsigned long )voice->LoopEnd;
+         voice->LoopSize  = voice->LoopEnd - voice->LoopStart;
+         }
+
+      if ( voice->bits == 16 )
+         {
+         blocklength /= 2;
+         }
+
+      voice->position     = 0;
+      voice->length       = min( blocklength, 0x8000 );
+      voice->BlockLength  = blocklength - voice->length;
+      voice->length     <<= 16;
+
+      MV_SetVoiceMixMode( voice );
+
+      return( KeepPlaying );
+      }
+
+   return( NoMoreData );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetNextDemandFeedBlock
+
+   Controls playback of demand fed data.
+---------------------------------------------------------------------*/
+
+playbackstatus MV_GetNextDemandFeedBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   if ( voice->BlockLength > 0 )
+      {
+      voice->position    -= voice->length;
+      voice->sound       += voice->length >> 16;
+      voice->length       = min( voice->BlockLength, 0x8000 );
+      voice->BlockLength -= voice->length;
+      voice->length     <<= 16;
+
+      return( KeepPlaying );
+      }
+
+   if ( voice->DemandFeed == NULL )
+      {
+      return( NoMoreData );
+      }
+
+   voice->position     = 0;
+   ( voice->DemandFeed )( &voice->sound, &voice->BlockLength );
+   voice->length       = min( voice->BlockLength, 0x8000 );
+   voice->BlockLength -= voice->length;
+   voice->length     <<= 16;
+
+   if ( ( voice->length > 0 ) && ( voice->sound != NULL ) )
+      {
+      return( KeepPlaying );
+      }
+   return( NoMoreData );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetNextRawBlock
+
+   Controls playback of demand fed data.
+---------------------------------------------------------------------*/
+
+playbackstatus MV_GetNextRawBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   if ( voice->BlockLength <= 0 )
+      {
+      if ( voice->LoopStart == NULL )
+         {
+         voice->Playing = FALSE;
+         return( NoMoreData );
+         }
+
+      voice->BlockLength = voice->LoopSize;
+      voice->NextBlock   = voice->LoopStart;
+      voice->length = 0;
+      voice->position = 0;
+      }
+
+   voice->sound        = voice->NextBlock;
+   voice->position    -= voice->length;
+   voice->length       = min( voice->BlockLength, 0x8000 );
+   voice->NextBlock   += voice->length;
+   if ( voice->bits == 16 )
+      {
+      voice->NextBlock += voice->length;
+      }
+   voice->BlockLength -= voice->length;
+   voice->length     <<= 16;
+
+   return( KeepPlaying );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetNextWAVBlock
+
+   Controls playback of demand fed data.
+---------------------------------------------------------------------*/
+
+playbackstatus MV_GetNextWAVBlock
+   (
+   VoiceNode *voice
+   )
+
+   {
+   if ( voice->BlockLength <= 0 )
+      {
+      if ( voice->LoopStart == NULL )
+         {
+         voice->Playing = FALSE;
+         return( NoMoreData );
+         }
+
+      voice->BlockLength = voice->LoopSize;
+      voice->NextBlock   = voice->LoopStart;
+      voice->length      = 0;
+      voice->position    = 0;
+      }
+
+   voice->sound        = voice->NextBlock;
+   voice->position    -= voice->length;
+   voice->length       = min( voice->BlockLength, 0x8000 );
+   voice->NextBlock   += voice->length;
+   if ( voice->bits == 16 )
+      {
+      voice->NextBlock += voice->length;
+      }
+   voice->BlockLength -= voice->length;
+   voice->length     <<= 16;
+
+   return( KeepPlaying );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_ServiceRecord
+
+   Starts recording of the waiting buffer.
+---------------------------------------------------------------------*/
+
+static void MV_ServiceRecord
+   (
+   void
+   )
+
+   {
+   if ( MV_RecordFunc )
+      {
+      MV_RecordFunc( MV_MixBuffer[ 0 ] + MV_MixPage * MixBufferSize,
+         MixBufferSize );
+      }
+
+   // Toggle which buffer we'll mix next
+   MV_MixPage++;
+   if ( MV_MixPage >= NumberOfBuffers )
+      {
+      MV_MixPage = 0;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetVoice
+
+   Locates the voice with the specified handle.
+---------------------------------------------------------------------*/
+
+VoiceNode *MV_GetVoice
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   for( voice = VoiceList.next; voice != &VoiceList; voice = voice->next )
+      {
+      if ( handle == voice->handle )
+         {
+         break;
+         }
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( voice == &VoiceList )
+      {
+      MV_SetErrorCode( MV_VoiceNotFound );
+      
+      // SBF - should this return null?
+      return NULL;
+      }
+
+   return( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_VoicePlaying
+
+   Checks if the voice associated with the specified handle is
+   playing.
+---------------------------------------------------------------------*/
+
+int MV_VoicePlaying
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( FALSE );
+      }
+
+   voice = MV_GetVoice( handle );
+
+   if ( voice == NULL )
+      {
+      return( FALSE );
+      }
+
+   return( TRUE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_KillAllVoices
+
+   Stops output of all currently active voices.
+---------------------------------------------------------------------*/
+
+int MV_KillAllVoices
+   (
+   void
+   )
+
+   {
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   // Remove all the voices from the list
+   while( VoiceList.next != &VoiceList )
+      {
+      MV_Kill( VoiceList.next->handle );
+      }
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_Kill
+
+   Stops output of the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int MV_Kill
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned  flags;
+   unsigned  long callbackval;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   flags = DisableInterrupts();
+
+   voice = MV_GetVoice( handle );
+   if ( voice == NULL )
+      {
+      RestoreInterrupts( flags );
+      MV_SetErrorCode( MV_VoiceNotFound );
+      return( MV_Error );
+      }
+
+   callbackval = voice->callbackval;
+
+   MV_StopVoice( voice );
+
+   RestoreInterrupts( flags );
+
+   if ( MV_CallBackFunc )
+      {
+      MV_CallBackFunc( callbackval );
+      }
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_VoicesPlaying
+
+   Determines the number of currently active voices.
+---------------------------------------------------------------------*/
+
+int MV_VoicesPlaying
+   (
+   void
+   )
+
+   {
+   VoiceNode   *voice;
+   int         NumVoices = 0;
+   unsigned    flags;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( 0 );
+      }
+
+   flags = DisableInterrupts();
+
+   for( voice = VoiceList.next; voice != &VoiceList; voice = voice->next )
+      {
+      NumVoices++;
+      }
+
+   RestoreInterrupts( flags );
+
+   return( NumVoices );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_AllocVoice
+
+   Retrieve an inactive or lower priority voice for output.
+---------------------------------------------------------------------*/
+
+VoiceNode *MV_AllocVoice
+   (
+   int priority
+   )
+
+   {
+   VoiceNode   *voice;
+   VoiceNode   *node;
+   unsigned    flags;
+
+//return( NULL );
+   if ( MV_Recording )
+      {
+      return( NULL );
+      }
+
+   flags = DisableInterrupts();
+
+   // Check if we have any free voices
+   if ( LL_Empty( &VoicePool, next, prev ) )
+      {
+      // check if we have a higher priority than a voice that is playing.
+      voice = VoiceList.next;
+      for( node = voice->next; node != &VoiceList; node = node->next )
+         {
+         if ( node->priority < voice->priority )
+            {
+            voice = node;
+            }
+         }
+
+      if ( priority >= voice->priority )
+         {
+         MV_Kill( voice->handle );
+         }
+      }
+
+   // Check if any voices are in the voice pool
+   if ( LL_Empty( &VoicePool, next, prev ) )
+      {
+      // No free voices
+      RestoreInterrupts( flags );
+      return( NULL );
+      }
+
+   voice = VoicePool.next;
+   LL_Remove( voice, next, prev );
+   RestoreInterrupts( flags );
+
+   // Find a free voice handle
+   do
+      {
+      MV_VoiceHandle++;
+      if ( MV_VoiceHandle < MV_MinVoiceHandle )
+         {
+         MV_VoiceHandle = MV_MinVoiceHandle;
+         }
+      }
+   while( MV_VoicePlaying( MV_VoiceHandle ) );
+
+   voice->handle = MV_VoiceHandle;
+
+   return( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_VoiceAvailable
+
+   Checks if a voice can be play at the specified priority.
+---------------------------------------------------------------------*/
+
+int MV_VoiceAvailable
+   (
+   int priority
+   )
+
+   {
+   VoiceNode   *voice;
+   VoiceNode   *node;
+   unsigned    flags;
+
+   // Check if we have any free voices
+   if ( !LL_Empty( &VoicePool, next, prev ) )
+      {
+      return( TRUE );
+      }
+
+   flags = DisableInterrupts();
+
+   // check if we have a higher priority than a voice that is playing.
+   voice = VoiceList.next;
+   for( node = VoiceList.next; node != &VoiceList; node = node->next )
+      {
+      if ( node->priority < voice->priority )
+         {
+         voice = node;
+         }
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( ( voice != &VoiceList ) && ( priority >= voice->priority ) )
+      {
+      return( TRUE );
+      }
+
+   return( FALSE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetVoicePitch
+
+   Sets the pitch for the specified voice.
+---------------------------------------------------------------------*/
+
+void MV_SetVoicePitch
+   (
+   VoiceNode *voice,
+   unsigned long rate,
+   int pitchoffset
+   )
+
+   {
+   voice->SamplingRate = rate;
+   voice->PitchScale   = PITCH_GetScale( pitchoffset );
+   voice->RateScale    = ( rate * voice->PitchScale ) / MV_MixRate;
+
+   // Multiply by MixBufferSize - 1
+   voice->FixedPointBufferSize = ( voice->RateScale * MixBufferSize ) -
+      voice->RateScale;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetPitch
+
+   Sets the pitch for the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int MV_SetPitch
+   (
+   int handle,
+   int pitchoffset
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   voice = MV_GetVoice( handle );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_VoiceNotFound );
+      return( MV_Error );
+      }
+
+   MV_SetVoicePitch( voice, voice->SamplingRate, pitchoffset );
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetFrequency
+
+   Sets the frequency for the voice associated with the specified handle.
+---------------------------------------------------------------------*/
+
+int MV_SetFrequency
+   (
+   int handle,
+   int frequency
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   voice = MV_GetVoice( handle );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_VoiceNotFound );
+      return( MV_Error );
+      }
+
+   MV_SetVoicePitch( voice, frequency, 0 );
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetVolumeTable
+
+   Returns a pointer to the volume table associated with the specified
+   volume.
+---------------------------------------------------------------------*/
+
+#if 0
+static short *MV_GetVolumeTable
+   (
+   int vol
+   )
+
+   {
+   int volume;
+   short *table;
+
+   volume = MIX_VOLUME( vol );
+
+   table = &MV_VolumeTable[ volume ];
+
+   return( table );
+   }
+#else
+static int MV_GetVolumeTable
+   (
+   int vol
+   )
+
+   {
+   int volume;
+
+   volume = MIX_VOLUME( vol );
+
+   return ( volume * MV_TotalVolume ) / MV_MaxTotalVolume;
+   }
+#endif
+
+/*---------------------------------------------------------------------
+   Function: MV_SetVoiceMixMode
+
+   Selects which method should be used to mix the voice.
+---------------------------------------------------------------------*/
+
+static void MV_SetVoiceMixMode
+   (
+   VoiceNode *voice
+   )
+
+   {
+   unsigned flags;
+   int test;
+
+   flags = DisableInterrupts();
+
+   test = T_DEFAULT;
+   if ( voice->bits == 16 )
+      {
+      test |= T_16BITSOURCE;
+      }
+
+   if ( MV_Channels == 1 )
+      {
+      test |= T_MONO;
+      }
+   else
+      {
+      if ( IS_QUIET( voice->RightVolume ) )
+         {
+         test |= T_RIGHTQUIET;
+         }
+      else if ( IS_QUIET( voice->LeftVolume ) )
+         {
+         test |= T_LEFTQUIET;
+         }
+      }
+
+   switch( test )
+      {
+      case T_MONO | T_16BITSOURCE :
+         voice->mix = MV_MixFPMono16;
+         break;
+
+      case T_MONO :
+         voice->mix = MV_MixFPMono8;
+         break;
+
+      case T_16BITSOURCE | T_LEFTQUIET :
+         MV_LeftVolume = MV_RightVolume;
+         voice->mix = MV_MixFPMono16;
+         break;
+
+      case T_LEFTQUIET :
+         MV_LeftVolume = MV_RightVolume;
+         voice->mix = MV_MixFPMono8;
+         break;
+
+      case T_16BITSOURCE | T_RIGHTQUIET :
+         voice->mix = MV_MixFPMono16;
+         break;
+
+      case T_RIGHTQUIET :
+         voice->mix = MV_MixFPMono8;
+         break;
+
+      case T_16BITSOURCE :
+         voice->mix = MV_MixFPStereo16;
+         break;
+
+      case T_SIXTEENBIT_STEREO :
+         voice->mix = MV_MixFPStereo8;
+         break;
+
+      default :
+         voice->mix = MV_MixFPMono8;
+      }
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetVoiceVolume
+
+   Sets the stereo and mono volume level of the voice associated
+   with the specified handle.
+---------------------------------------------------------------------*/
+
+void MV_SetVoiceVolume
+   (
+   VoiceNode *voice,
+   int vol,
+   int left,
+   int right
+   )
+
+   {
+   if ( MV_Channels == 1 )
+      {
+      left  = vol;
+      right = vol;
+      }
+
+   if ( MV_SwapLeftRight )
+      {
+      // SBPro uses reversed panning
+      voice->LeftVolume  = MV_GetVolumeTable( right );
+      voice->RightVolume = MV_GetVolumeTable( left );
+      }
+   else
+      {
+      voice->LeftVolume  = MV_GetVolumeTable( left );
+      voice->RightVolume = MV_GetVolumeTable( right );
+      }
+
+   MV_SetVoiceMixMode( voice );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_EndLooping
+
+   Stops the voice associated with the specified handle from looping
+   without stoping the sound.
+---------------------------------------------------------------------*/
+
+int MV_EndLooping
+   (
+   int handle
+   )
+
+   {
+   VoiceNode *voice;
+   unsigned flags;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   flags = DisableInterrupts();
+
+   voice = MV_GetVoice( handle );
+   if ( voice == NULL )
+      {
+      RestoreInterrupts( flags );
+      MV_SetErrorCode( MV_VoiceNotFound );
+      return( MV_Warning );
+      }
+
+   voice->LoopCount = 0;
+   voice->LoopStart = NULL;
+   voice->LoopEnd   = NULL;
+
+   RestoreInterrupts( flags );
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetPan
+
+   Sets the stereo and mono volume level of the voice associated
+   with the specified handle.
+---------------------------------------------------------------------*/
+
+int MV_SetPan
+   (
+   int handle,
+   int vol,
+   int left,
+   int right
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   voice = MV_GetVoice( handle );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_VoiceNotFound );
+      return( MV_Warning );
+      }
+
+   MV_SetVoiceVolume( voice, vol, left, right );
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_Pan3D
+
+   Set the angle and distance from the listener of the voice associated
+   with the specified handle.
+---------------------------------------------------------------------*/
+
+int MV_Pan3D
+   (
+   int handle,
+   int angle,
+   int distance
+   )
+
+   {
+   int left;
+   int right;
+   int mid;
+   int volume;
+   int status;
+
+   if ( distance < 0 )
+      {
+      distance  = -distance;
+      angle    += MV_NumPanPositions / 2;
+      }
+
+   volume = MIX_VOLUME( distance );
+
+   // Ensure angle is within 0 - 31
+   angle &= MV_MaxPanPosition;
+
+   left  = MV_PanTable[ angle ][ volume ].left;
+   right = MV_PanTable[ angle ][ volume ].right;
+   mid   = max( 0, 255 - distance );
+
+   status = MV_SetPan( handle, mid, left, right );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetReverb
+
+   Sets the level of reverb to add to mix.
+---------------------------------------------------------------------*/
+
+void MV_SetReverb
+   (
+   int reverb
+   )
+
+   {
+   MV_ReverbLevel = MIX_VOLUME( reverb );
+   MV_ReverbTable = ( MV_ReverbLevel * MV_TotalVolume ) / MV_MaxTotalVolume;// &MV_VolumeTable[ MV_ReverbLevel ];
+   if (!reverb) MV_FPReverbFree();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetFastReverb
+
+   Sets the level of reverb to add to mix.
+---------------------------------------------------------------------*/
+
+void MV_SetFastReverb
+   (
+   int reverb
+   )
+
+   {
+   MV_ReverbLevel = max( 0, min( 16, reverb ) );
+   MV_ReverbTable = -1;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetMaxReverbDelay
+
+   Returns the maximum delay time for reverb.
+---------------------------------------------------------------------*/
+
+int MV_GetMaxReverbDelay
+   (
+   void
+   )
+
+   {
+   int maxdelay;
+
+   maxdelay = 65536; //MixBufferSize * MV_NumberOfBuffers;
+
+   return maxdelay;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetReverbDelay
+
+   Returns the current delay time for reverb.
+---------------------------------------------------------------------*/
+
+int MV_GetReverbDelay
+   (
+   void
+   )
+
+   {
+   return MV_ReverbDelay; // / MV_SampleSize;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetReverbDelay
+
+   Sets the delay level of reverb to add to mix.
+---------------------------------------------------------------------*/
+
+void MV_SetReverbDelay
+   (
+   int delay
+   )
+
+   {
+   int maxdelay;
+
+   if (!delay) // blah, ignore
+      return;
+   maxdelay = MV_GetMaxReverbDelay();
+   MV_ReverbDelay = max( MixBufferSize, min( delay, maxdelay ) );
+   // MV_ReverbDelay *= MV_SampleSize;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetMixMode
+
+   Prepares Multivoc to play stereo of mono digitized sounds.
+---------------------------------------------------------------------*/
+
+int MV_SetMixMode
+   (
+   int numchannels,
+   int samplebits
+   )
+
+   {
+   int mode;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   mode = 0;
+   if ( numchannels == 2 )
+      {
+      mode |= STEREO;
+      }
+   if ( samplebits == 16 )
+      {
+      mode |= SIXTEEN_BIT;
+      }
+
+#ifdef PLAT_DOS
+   switch( MV_SoundCard )
+      {
+      case UltraSound :
+         MV_MixMode = mode;
+         break;
+
+      case SoundBlaster :
+      case Awe32 :
+         MV_MixMode = BLASTER_SetMixMode( mode );
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         MV_MixMode = PAS_SetMixMode( mode );
+         break;
+
+      case SoundScape :
+         MV_MixMode = SOUNDSCAPE_SetMixMode( mode );
+         break;
+
+      #ifndef SOUNDSOURCE_OFF
+      case SoundSource :
+      case TandySoundSource :
+         MV_MixMode = SS_SetMixMode( mode );
+         break;
+      #endif
+      }
+#else
+   MV_MixMode = mode;
+#endif
+
+   MV_Channels = 1;
+   if ( MV_MixMode & STEREO )
+      {
+      MV_Channels = 2;
+      }
+
+   MV_Bits = 8;
+   if ( MV_MixMode & SIXTEEN_BIT )
+      {
+      MV_Bits = 16;
+      }
+
+   MV_BuffShift  = 7 + MV_Channels;
+   MV_SampleSize = sizeof( MONO8 ) * MV_Channels;
+
+   if ( MV_Bits == 8 )
+      {
+      MV_Silence = SILENCE_8BIT;
+      }
+   else
+      {
+      MV_Silence     = SILENCE_16BIT;
+      MV_BuffShift  += 1;
+      MV_SampleSize *= 2;
+      }
+
+   MV_BufferSize = MixBufferSize * MV_SampleSize;
+   MV_NumberOfBuffers = TotalBufferSize / MV_BufferSize;
+   MV_BufferLength = TotalBufferSize;
+
+   MV_RightChannelOffset = MV_SampleSize / 2;
+
+#ifdef PLAT_DOS
+   if ( ( MV_SoundCard == UltraSound ) && ( MV_Channels == 2 ) )
+      {
+      MV_SampleSize         /= 2;
+      MV_BufferSize         /= 2;
+      MV_RightChannelOffset  = MV_BufferSize * MV_NumberOfBuffers;
+      MV_BufferLength       /= 2;
+      }
+#endif
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StartPlayback
+
+   Starts the sound playback engine.
+---------------------------------------------------------------------*/
+
+int MV_StartPlayback()
+
+   {
+   int status;
+   int buffer;
+
+   // Initialize the buffers
+   ClearBuffer_DW( MV_MixBuffer[ 0 ], MV_Silence, TotalBufferSize >> 2 );
+   for( buffer = 0; buffer < MV_NumberOfBuffers; buffer++ )
+      {
+      MV_BufferEmpty[ buffer ] = TRUE;
+      }
+
+   // Set the mix buffer variables
+   MV_MixPage = 1;
+
+   MV_MixFunction = MV_Mix;
+
+//JIM
+//   MV_MixRate = MV_RequestedMixRate;
+//   return( MV_Ok );
+
+   // Start playback
+    status = DSL_BeginBufferedPlayback( MV_MixBuffer[ 0 ],
+										TotalBufferSize, 
+										MV_NumberOfBuffers,
+										MV_RequestedMixRate, 
+										MV_MixMode, 
+										MV_ServiceVoc );
+
+    if ( status != DSL_Ok )
+    {
+		MV_SetErrorCode( MV_BlasterError );
+		return( MV_Error );
+    }
+
+    MV_MixRate = DSL_GetPlaybackRate();
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StopPlayback
+
+   Stops the sound playback engine.
+---------------------------------------------------------------------*/
+
+void MV_StopPlayback
+   (
+   void
+   )
+
+   {
+   VoiceNode   *voice;
+   VoiceNode   *next;
+   unsigned    flags;
+
+#ifdef PLAT_DOS
+   // Stop sound playback
+   switch( MV_SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         BLASTER_StopPlayback();
+         break;
+
+      case UltraSound :
+         GUSWAVE_KillAllVoices();
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         PAS_StopPlayback();
+         break;
+
+      case SoundScape :
+         SOUNDSCAPE_StopPlayback();
+         break;
+
+      #ifndef SOUNDSOURCE_OFF
+      case SoundSource :
+      case TandySoundSource :
+         SS_StopPlayback();
+         break;
+      #endif
+      }
+#else
+   DSL_StopPlayback();
+#endif
+
+   // Make sure all callbacks are done.
+   flags = DisableInterrupts();
+
+   for( voice = VoiceList.next; voice != &VoiceList; voice = next )
+      {
+      next = voice->next;
+
+      MV_StopVoice( voice );
+
+      if ( MV_CallBackFunc )
+         {
+         MV_CallBackFunc( voice->callbackval );
+         }
+      }
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StartRecording
+
+   Starts the sound recording engine.
+---------------------------------------------------------------------*/
+
+int MV_StartRecording
+   (
+   int MixRate,
+   void ( *function )( char *ptr, int length )
+   )
+
+   {
+#ifdef PLAT_DOS
+   int status;
+
+   switch( MV_SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         break;
+
+      default :
+         MV_SetErrorCode( MV_UnsupportedCard );
+         return( MV_Error );
+         break;
+      }
+
+   if ( function == NULL )
+      {
+      MV_SetErrorCode( MV_NullRecordFunction );
+      return( MV_Error );
+      }
+
+   MV_StopPlayback();
+
+   // Initialize the buffers
+   ClearBuffer_DW( MV_MixBuffer[ 0 ], SILENCE_8BIT, TotalBufferSize >> 2 );
+
+   // Set the mix buffer variables
+   MV_MixPage  = 0;
+
+   MV_RecordFunc = function;
+
+   // Start playback
+   switch( MV_SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         status = BLASTER_BeginBufferedRecord( MV_MixBuffer[ 0 ],
+            TotalBufferSize, NumberOfBuffers, MixRate, MONO_8BIT,
+            MV_ServiceRecord );
+
+         if ( status != BLASTER_Ok )
+            {
+            MV_SetErrorCode( MV_BlasterError );
+            return( MV_Error );
+            }
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         status = PAS_BeginBufferedRecord( MV_MixBuffer[ 0 ],
+            TotalBufferSize, NumberOfBuffers, MixRate, MONO_8BIT,
+            MV_ServiceRecord );
+
+         if ( status != PAS_Ok )
+            {
+            MV_SetErrorCode( MV_PasError );
+            return( MV_Error );
+            }
+         break;
+      }
+
+   MV_Recording = TRUE;
+   return( MV_Ok );
+#else
+   MV_SetErrorCode( MV_UnsupportedCard );
+   return( MV_Error );
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StopRecord
+
+   Stops the sound record engine.
+---------------------------------------------------------------------*/
+
+void MV_StopRecord
+   (
+   void
+   )
+
+   {
+#ifdef PLAT_DOS
+   // Stop sound playback
+   switch( MV_SoundCard )
+      {
+      case SoundBlaster :
+      case Awe32 :
+         BLASTER_StopPlayback();
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         PAS_StopPlayback();
+         break;
+      }
+
+   MV_Recording = FALSE;
+   MV_StartPlayback();
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_StartDemandFeedPlayback
+
+   Plays a digitized sound from a user controlled buffering system.
+---------------------------------------------------------------------*/
+
+int MV_StartDemandFeedPlayback
+   (
+   void ( *function )( char **ptr, unsigned long *length ),
+   int rate,
+   int pitchoffset,
+   int vol,
+   int left,
+   int right,
+   int priority,
+   unsigned long callbackval
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = MV_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_NoVoices );
+      return( MV_Error );
+      }
+
+   voice->wavetype    = DemandFeed;
+   voice->bits        = 8;
+   voice->GetSound    = MV_GetNextDemandFeedBlock;
+   voice->NextBlock   = NULL;
+   voice->DemandFeed  = function;
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->GLast       = -1;
+   voice->GPos        = 0;
+   voice->GVal[0]     = 0;
+   voice->GVal[1]     = 0;
+   voice->GVal[2]     = 0;
+   voice->GVal[3]     = 0;
+   voice->BlockLength = 0;
+   voice->position    = 0;
+   voice->sound       = NULL;
+   voice->length      = 0;
+   voice->BlockLength = 0;
+   voice->Playing     = TRUE;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->callbackval = callbackval;
+
+   MV_SetVoicePitch( voice, rate, pitchoffset );
+   MV_SetVoiceVolume( voice, vol, left, right );
+   MV_PlayVoice( voice );
+
+   return( voice->handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayRaw
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayRaw
+   (
+   char *ptr,
+   unsigned long length,
+   unsigned rate,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   int status;
+
+   status = MV_PlayLoopedRaw( ptr, length, NULL, NULL, rate, pitchoffset,
+      vol, left, right, priority, callbackval );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayLoopedRaw
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayLoopedRaw
+   (
+   char *ptr,
+   unsigned long length,
+   char *loopstart,
+   char *loopend,
+   unsigned rate,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   VoiceNode *voice;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = MV_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_NoVoices );
+      return( MV_Error );
+      }
+
+   voice->wavetype    = Raw;
+   voice->bits        = 8;
+   voice->GetSound    = MV_GetNextRawBlock;
+   voice->Playing     = TRUE;
+   voice->NextBlock   = ptr;
+   voice->position    = 0;
+   voice->BlockLength = length;
+   voice->length      = 0;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->GLast       = -1;
+   voice->GPos        = 0;
+   voice->GVal[0]     = 0;
+   voice->GVal[1]     = 0;
+   voice->GVal[2]     = 0;
+   voice->GVal[3]     = 0;
+   voice->callbackval = callbackval;
+   voice->LoopStart   = loopstart;
+   voice->LoopEnd     = loopend;
+   voice->LoopSize    = ( voice->LoopEnd - voice->LoopStart ) + 1;
+
+   MV_SetVoicePitch( voice, rate, pitchoffset );
+   MV_SetVoiceVolume( voice, vol, left, right );
+   MV_PlayVoice( voice );
+
+   return( voice->handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayWAV
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayWAV
+   (
+   char *ptr,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   int status;
+
+   status = MV_PlayLoopedWAV( ptr, -1, -1, pitchoffset, vol, left, right,
+      priority, callbackval );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayWAV3D
+
+   Begin playback of sound data at specified angle and distance
+   from listener.
+---------------------------------------------------------------------*/
+
+int MV_PlayWAV3D
+   (
+   char *ptr,
+   int  pitchoffset,
+   int  angle,
+   int  distance,
+   int  priority,
+   unsigned long callbackval
+   )
+
+   {
+   int left;
+   int right;
+   int mid;
+   int volume;
+   int status;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   if ( distance < 0 )
+      {
+      distance  = -distance;
+      angle    += MV_NumPanPositions / 2;
+      }
+
+   volume = MIX_VOLUME( distance );
+
+   // Ensure angle is within 0 - 31
+   angle &= MV_MaxPanPosition;
+
+   left  = MV_PanTable[ angle ][ volume ].left;
+   right = MV_PanTable[ angle ][ volume ].right;
+   mid   = max( 0, 255 - distance );
+
+   status = MV_PlayWAV( ptr, pitchoffset, mid, left, right, priority,
+      callbackval );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayLoopedWAV
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayLoopedWAV
+   (
+   char *ptr,
+   long  loopstart,
+   long  loopend,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   riff_header   *riff;
+   format_header *format;
+   data_header   *data;
+   VoiceNode     *voice;
+   int length;
+   int absloopend;
+   int absloopstart;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   riff = ( riff_header * )ptr;
+
+   if ( ( strncmp( riff->RIFF, "RIFF", 4 ) != 0 ) ||
+      ( strncmp( riff->WAVE, "WAVE", 4 ) != 0 ) ||
+      ( strncmp( riff->fmt, "fmt ", 4) != 0 ) )
+      {
+      MV_SetErrorCode( MV_InvalidWAVFile );
+      return( MV_Error );
+      }
+
+   format = ( format_header * )( riff + 1 );
+   data   = ( data_header * )( ( ( char * )format ) + riff->format_size );
+
+   // Check if it's PCM data.
+   if ( format->wFormatTag != 1 )
+      {
+      MV_SetErrorCode( MV_InvalidWAVFile );
+      return( MV_Error );
+      }
+
+   if ( format->nChannels != 1 )
+      {
+      MV_SetErrorCode( MV_InvalidWAVFile );
+      return( MV_Error );
+      }
+
+   if ( ( format->nBitsPerSample != 8 ) &&
+      ( format->nBitsPerSample != 16 ) )
+      {
+      MV_SetErrorCode( MV_InvalidWAVFile );
+      return( MV_Error );
+      }
+
+   if ( strncmp( data->DATA, "data", 4 ) != 0 )
+      {
+      MV_SetErrorCode( MV_InvalidWAVFile );
+      return( MV_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = MV_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_NoVoices );
+      return( MV_Error );
+      }
+
+   voice->wavetype    = WAV;
+   voice->bits        = format->nBitsPerSample;
+   voice->GetSound    = MV_GetNextWAVBlock;
+
+   length = data->size;
+   absloopstart = loopstart;
+   absloopend   = loopend;
+   if ( voice->bits == 16 )
+      {
+      loopstart  *= 2;
+      data->size &= ~1;
+      loopend    *= 2;
+      length     /= 2;
+      }
+
+   loopend    = min( loopend, data->size );
+   absloopend = min( absloopend, length );
+
+   voice->Playing     = TRUE;
+   voice->DemandFeed  = NULL;
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->position    = 0;
+   voice->length      = 0;
+   voice->BlockLength = absloopend;
+   voice->NextBlock   = ( char * )( data + 1 );
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->GLast       = -1;
+   voice->GPos        = 0;
+   voice->GVal[0]     = 0;
+   voice->GVal[1]     = 0;
+   voice->GVal[2]     = 0;
+   voice->GVal[3]     = 0;
+   voice->callbackval = callbackval;
+   voice->LoopStart   = voice->NextBlock + loopstart;
+   voice->LoopEnd     = voice->NextBlock + loopend;
+   voice->LoopSize    = absloopend - absloopstart;
+
+   if ( ( loopstart >= data->size ) || ( loopstart < 0 ) )
+      {
+      voice->LoopStart = NULL;
+      voice->LoopEnd   = NULL;
+      voice->BlockLength = length;
+      }
+
+   MV_SetVoicePitch( voice, format->nSamplesPerSec, pitchoffset );
+   MV_SetVoiceVolume( voice, vol, left, right );
+   MV_PlayVoice( voice );
+
+   return( voice->handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayVOC3D
+
+   Begin playback of sound data at specified angle and distance
+   from listener.
+---------------------------------------------------------------------*/
+
+int MV_PlayVOC3D
+   (
+   char *ptr,
+   int  pitchoffset,
+   int  angle,
+   int  distance,
+   int  priority,
+   unsigned long callbackval
+   )
+
+   {
+   int left;
+   int right;
+   int mid;
+   int volume;
+   int status;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   if ( distance < 0 )
+      {
+      distance  = -distance;
+      angle    += MV_NumPanPositions / 2;
+      }
+
+   volume = MIX_VOLUME( distance );
+
+   // Ensure angle is within 0 - 31
+   angle &= MV_MaxPanPosition;
+
+   left  = MV_PanTable[ angle ][ volume ].left;
+   right = MV_PanTable[ angle ][ volume ].right;
+   mid   = max( 0, 255 - distance );
+
+   status = MV_PlayVOC( ptr, pitchoffset, mid, left, right, priority,
+      callbackval );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayVOC
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayVOC
+   (
+   char *ptr,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   int status;
+
+   status = MV_PlayLoopedVOC( ptr, -1, -1, pitchoffset, vol, left, right,
+      priority, callbackval );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_PlayLoopedVOC
+
+   Begin playback of sound data with the given sound levels and
+   priority.
+---------------------------------------------------------------------*/
+
+int MV_PlayLoopedVOC
+   (
+   char *ptr,
+   long  loopstart,
+   long  loopend,
+   int   pitchoffset,
+   int   vol,
+   int   left,
+   int   right,
+   int   priority,
+   unsigned long callbackval
+   )
+
+   {
+   VoiceNode   *voice;
+   int          status;
+
+   if ( !MV_Installed )
+      {
+      MV_SetErrorCode( MV_NotInstalled );
+      return( MV_Error );
+      }
+
+   // Make sure it's a valid VOC file.
+   status = strncmp( ptr, "Creative Voice File", 19 );
+   if ( status != 0 )
+      {
+      MV_SetErrorCode( MV_InvalidVOCFile );
+      return( MV_Error );
+      }
+
+   // Request a voice from the voice pool
+   voice = MV_AllocVoice( priority );
+   if ( voice == NULL )
+      {
+      MV_SetErrorCode( MV_NoVoices );
+      return( MV_Error );
+      }
+
+   voice->wavetype    = VOC;
+   voice->bits        = 8;
+   voice->GetSound    = MV_GetNextVOCBlock;
+   voice->NextBlock   = ptr + *( unsigned short int * )( ptr + 0x14 );
+   voice->DemandFeed  = NULL;
+   voice->LoopStart   = NULL;
+   voice->LoopCount   = 0;
+   voice->BlockLength = 0;
+   voice->PitchScale  = PITCH_GetScale( pitchoffset );
+   voice->length      = 0;
+   voice->next        = NULL;
+   voice->prev        = NULL;
+   voice->priority    = priority;
+   voice->GLast       = -1;
+   voice->GPos        = 0;
+   voice->GVal[0]     = 0;
+   voice->GVal[1]     = 0;
+   voice->GVal[2]     = 0;
+   voice->GVal[3]     = 0;
+   voice->callbackval = callbackval;
+   voice->LoopStart   = ( char * )loopstart;
+   voice->LoopEnd     = ( char * )loopend;
+   voice->LoopSize    = loopend - loopstart + 1;
+
+   if ( loopstart < 0 )
+      {
+      voice->LoopStart = NULL;
+      voice->LoopEnd   = NULL;
+      }
+
+   MV_SetVoiceVolume( voice, vol, left, right );
+   MV_PlayVoice( voice );
+
+   return( voice->handle );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void MV_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+#if 0
+/*---------------------------------------------------------------------
+   Function: MV_CreateVolumeTable
+
+   Create the table used to convert sound data to a specific volume
+   level.
+---------------------------------------------------------------------*/
+
+void MV_CreateVolumeTable
+   (
+   int index,
+   int volume,
+   int MaxVolume
+   )
+
+   {
+   int val;
+   int level;
+   int i;
+
+   level = ( volume * MaxVolume ) / MV_MaxTotalVolume;
+   if ( MV_Bits == 16 )
+      {
+      for( i = 0; i < 65536; i += 256 )
+         {
+         val   = i - 0x8000;
+         val  *= level;
+         val  /= MV_MaxVolume;
+         MV_VolumeTable[ index ][ i / 256 ] = val;
+         }
+      }
+   else
+      {
+      for( i = 0; i < 256; i++ )
+         {
+         val   = i - 0x80;
+         val  *= level;
+         val  /= MV_MaxVolume;
+         MV_VolumeTable[ volume ][ i ] = val;
+         }
+      }
+   }
+#endif
+
+/*---------------------------------------------------------------------
+   Function: MV_CalcVolume
+
+   Create the table used to convert sound data to a specific volume
+   level.
+---------------------------------------------------------------------*/
+
+void MV_CalcVolume
+   (
+   int MaxVolume
+   )
+
+   {
+   int volume;
+
+#if 0
+   for( volume = 0; volume < 128; volume++ )
+      {
+      MV_HarshClipTable[ volume ] = 0;
+      MV_HarshClipTable[ volume + 384 ] = 255;
+      }
+   for( volume = 0; volume < 256; volume++ )
+      {
+      MV_HarshClipTable[ volume + 128 ] = volume;
+      }
+#endif
+
+   // For each volume level, create a translation table with the
+   // appropriate volume calculated.
+   // this is ugly
+#if 0
+   for( volume = 0; volume <= MV_MaxVolume; volume++ )
+      {
+      MV_CreateVolumeTable( volume, volume, MaxVolume );
+      }
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_CalcPanTable
+
+   Create the table used to determine the stereo volume level of
+   a sound located at a specific angle and distance from the listener.
+---------------------------------------------------------------------*/
+
+void MV_CalcPanTable
+   (
+   void
+   )
+
+   {
+   int   level;
+   int   angle;
+   int   distance;
+   int   HalfAngle;
+   int   ramp;
+
+   HalfAngle = ( MV_NumPanPositions / 2 );
+
+   for( distance = 0; distance <= MV_MaxVolume; distance++ )
+      {
+      level = ( 255 * ( MV_MaxVolume - distance ) ) / MV_MaxVolume;
+      for( angle = 0; angle <= HalfAngle / 2; angle++ )
+         {
+         ramp = level - ( ( level * angle ) /
+            ( MV_NumPanPositions / 4 ) );
+
+         MV_PanTable[ angle ][ distance ].left = ramp;
+         MV_PanTable[ HalfAngle - angle ][ distance ].left = ramp;
+         MV_PanTable[ HalfAngle + angle ][ distance ].left = level;
+         MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].left = level;
+
+         MV_PanTable[ angle ][ distance ].right = level;
+         MV_PanTable[ HalfAngle - angle ][ distance ].right = level;
+         MV_PanTable[ HalfAngle + angle ][ distance ].right = ramp;
+         MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].right = ramp;
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetVolume
+
+   Sets the volume of digitized sound playback.
+---------------------------------------------------------------------*/
+
+void MV_SetVolume
+   (
+   int volume
+   )
+
+   {
+   volume = max( 0, volume );
+   volume = min( volume, MV_MaxTotalVolume );
+
+   MV_TotalVolume = volume;
+
+   // Calculate volume table
+   MV_CalcVolume( volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetVolume
+
+   Returns the volume of digitized sound playback.
+---------------------------------------------------------------------*/
+
+int MV_GetVolume
+   (
+   void
+   )
+
+   {
+   return( MV_TotalVolume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetCallBack
+
+   Set the function to call when a voice stops.
+---------------------------------------------------------------------*/
+
+void MV_SetCallBack
+   (
+   void ( *function )( unsigned long )
+   )
+
+   {
+   MV_CallBackFunc = function;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_SetReverseStereo
+
+   Set the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+void MV_SetReverseStereo
+   (
+   int setting
+   )
+
+   {
+   MV_SwapLeftRight = setting;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_GetReverseStereo
+
+   Returns the orientation of the left and right channels.
+---------------------------------------------------------------------*/
+
+int MV_GetReverseStereo
+   (
+   void
+   )
+
+   {
+   return( MV_SwapLeftRight );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_TestPlayback
+
+   Checks if playback has started.
+---------------------------------------------------------------------*/
+
+int MV_TestPlayback
+   (
+   void
+   )
+
+   {
+#ifdef PLAT_DOS
+   unsigned flags;
+   long time;
+   int  start;
+   int  status;
+   int  pos;
+
+   if ( MV_SoundCard == UltraSound )
+      {
+      return( MV_Ok );
+      }
+
+   flags = DisableInterrupts();
+   _enable();
+
+   status = MV_Error;
+   start  = MV_MixPage;
+   time   = clock() + CLOCKS_PER_SEC * 2;
+
+   while( clock() < time )
+      {
+      if ( MV_MixPage != start )
+         {
+         status = MV_Ok;
+         }
+      }
+
+   RestoreInterrupts( flags );
+
+   if ( status != MV_Ok )
+      {
+      // Just in case an error doesn't get reported
+      MV_SetErrorCode( MV_DMAFailure );
+
+      switch( MV_SoundCard )
+         {
+         case SoundBlaster :
+         case Awe32 :
+            pos = BLASTER_GetCurrentPos();
+            break;
+
+         case ProAudioSpectrum :
+         case SoundMan16 :
+            pos = PAS_GetCurrentPos();
+            break;
+
+         case SoundScape :
+            pos = SOUNDSCAPE_GetCurrentPos();
+            break;
+
+         #ifndef SOUNDSOURCE_OFF
+         case SoundSource :
+         case TandySoundSource :
+            MV_SetErrorCode( MV_SoundSourceFailure );
+            pos = -1;
+            break;
+         #endif
+
+         default :
+            MV_SetErrorCode( MV_UnsupportedCard );
+            pos = -2;
+            break;
+         }
+
+      if ( pos > 0 )
+         {
+         MV_SetErrorCode( MV_IrqFailure );
+         }
+      else if ( pos == 0 )
+         {
+         if ( MV_Bits == 16 )
+            {
+            MV_SetErrorCode( MV_DMA16Failure );
+            }
+         else
+            {
+            MV_SetErrorCode( MV_DMAFailure );
+            }
+         }
+      }
+
+   return( status );
+#else
+   return MV_Ok;
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_Init
+
+   Perform the initialization of variables and memory used by
+   Multivoc.
+---------------------------------------------------------------------*/
+
+int MV_Init
+   (
+   int soundcard,
+   int MixRate,
+   int Voices,
+   int numchannels,
+   int samplebits
+   )
+
+   {
+   char *ptr;
+   int  status;
+   int  buffer;
+   int  index;
+
+   if ( MV_Installed )
+      {
+      MV_Shutdown();
+      }
+
+   MV_SetErrorCode( MV_Ok );
+
+   status = MV_LockMemory();
+   if ( status != MV_Ok )
+      {
+      return( status );
+      }
+
+   MV_TotalMemory = Voices * sizeof( VoiceNode ); // + sizeof( HARSH_CLIP_TABLE_8 );
+   status = USRHOOKS_GetMem( ( void ** )&ptr, MV_TotalMemory );
+   if ( status != USRHOOKS_Ok )
+      {
+      MV_UnlockMemory();
+      MV_SetErrorCode( MV_NoMem );
+      return( MV_Error );
+      }
+
+   status = DPMI_LockMemory( ptr, MV_TotalMemory );
+   if ( status != DPMI_Ok )
+      {
+      USRHOOKS_FreeMem( ptr );
+      MV_UnlockMemory();
+      MV_SetErrorCode( MV_DPMI_Error );
+      return( MV_Error );
+      }
+
+   MV_Voices = ( VoiceNode * )ptr;
+//   MV_HarshClipTable = ptr + ( MV_TotalMemory - sizeof( HARSH_CLIP_TABLE_8 ) );
+
+   // Set number of voices before calculating volume table
+   MV_MaxVoices = Voices;
+
+   LL_Reset( &VoiceList, next, prev );
+   LL_Reset( &VoicePool, next, prev );
+
+   for( index = 0; index < Voices; index++ )
+      {
+      LL_Add( &VoicePool, &MV_Voices[ index ], next, prev );
+      }
+
+   // Allocate mix buffer within 1st megabyte
+   status = DPMI_GetDOSMemory( ( void ** )&ptr, &MV_BufferDescriptor,
+      2 * TotalBufferSize);
+
+   if ( status )
+      {
+      DPMI_UnlockMemory( MV_Voices, MV_TotalMemory );
+      USRHOOKS_FreeMem( MV_Voices );
+      MV_Voices      = NULL;
+      MV_TotalMemory = 0;
+      MV_UnlockMemory();
+
+      MV_SetErrorCode( MV_NoMem );
+      return( MV_Error );
+      }
+
+   MV_SetReverseStereo( FALSE );
+
+   // Initialize the sound card
+#ifdef PLAT_DOS
+   switch( soundcard )
+      {
+      case UltraSound :
+         status = GUSWAVE_Init( 2 );
+         if ( status != GUSWAVE_Ok )
+            {
+            //JIM
+            MV_SetErrorCode( MV_BlasterError );
+            }
+         break;
+
+      case SoundBlaster :
+      case Awe32 :
+         status = BLASTER_Init();
+         if ( status != BLASTER_Ok )
+            {
+            MV_SetErrorCode( MV_BlasterError );
+            }
+
+         if ( ( BLASTER_Config.Type == SBPro ) ||
+            ( BLASTER_Config.Type == SBPro2 ) )
+            {
+            MV_SetReverseStereo( TRUE );
+            }
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         status = PAS_Init();
+         if ( status != PAS_Ok )
+            {
+            MV_SetErrorCode( MV_PasError );
+            }
+         break;
+
+      case SoundScape :
+         status = SOUNDSCAPE_Init();
+         if ( status != SOUNDSCAPE_Ok )
+            {
+            MV_SetErrorCode( MV_SoundScapeError );
+            }
+         break;
+
+      #ifndef SOUNDSOURCE_OFF
+      case SoundSource :
+      case TandySoundSource :
+         status = SS_Init( soundcard );
+         if ( status != SS_Ok )
+            {
+            MV_SetErrorCode( MV_SoundSourceError );
+            }
+         break;
+      #endif
+
+      default :
+         MV_SetErrorCode( MV_UnsupportedCard );
+         break;
+      }
+#else
+   status = DSL_Init();
+   if ( status != DSL_Ok )
+      {
+      MV_SetErrorCode( MV_BlasterError );
+      }
+#endif
+
+   if ( MV_ErrorCode != MV_Ok )
+      {
+      status = MV_ErrorCode;
+
+      DPMI_UnlockMemory( MV_Voices, MV_TotalMemory );
+      USRHOOKS_FreeMem( MV_Voices );
+      MV_Voices      = NULL;
+      MV_TotalMemory = 0;
+
+      DPMI_FreeDOSMemory( MV_BufferDescriptor );
+      MV_UnlockMemory();
+
+      MV_SetErrorCode( status );
+      return( MV_Error );
+      }
+
+   MV_SoundCard    = soundcard;
+   MV_Installed    = TRUE;
+   MV_CallBackFunc = NULL;
+   MV_RecordFunc   = NULL;
+   MV_Recording    = FALSE;
+   MV_ReverbLevel  = 0;
+   MV_ReverbTable  = -1;
+
+   // Set the sampling rate
+   MV_RequestedMixRate = MixRate;
+
+   // Set Mixer to play stereo digitized sound
+   MV_SetMixMode( numchannels, samplebits );
+   MV_ReverbDelay = 14320; // MV_BufferSize * 3;
+   //InitializeCriticalSection(&reverbCS);
+   reverbMutex = SDL_CreateMutex();
+
+#ifdef PLAT_DOS
+   // Make sure we don't cross a physical page
+   if ( ( ( unsigned long )ptr & 0xffff ) + TotalBufferSize > 0x10000 )
+      {
+      ptr = ( char * )( ( ( unsigned long )ptr & 0xff0000 ) + 0x10000 );
+      }
+#endif
+
+   MV_MixBuffer[ MV_NumberOfBuffers ] = ptr;
+   for( buffer = 0; buffer < MV_NumberOfBuffers; buffer++ )
+      {
+      MV_MixBuffer[ buffer ] = ptr;
+      ptr += MV_BufferSize;
+      }
+
+   // Calculate pan table
+   MV_CalcPanTable();
+
+   MV_SetVolume( MV_MaxTotalVolume );
+
+
+   MV_FooMemory = sizeof(double) * MixBufferSize * numchannels + 1024;
+   status = USRHOOKS_GetMem( ( void ** )&ptr, MV_FooMemory);
+   if ( status != USRHOOKS_Ok )
+   {
+       DPMI_UnlockMemory( MV_Voices, MV_TotalMemory );
+       USRHOOKS_FreeMem( MV_Voices );
+       MV_Voices      = NULL;
+       MV_TotalMemory = 0;
+	   MV_UnlockMemory();
+	   MV_SetErrorCode( MV_NoMem );
+	   return( MV_Error);
+   }
+
+   status = DPMI_LockMemory( ptr, MV_FooMemory );
+   if ( status != DPMI_Ok )
+   {
+	   USRHOOKS_FreeMem( ptr );
+       DPMI_UnlockMemory( MV_Voices, MV_TotalMemory );
+       USRHOOKS_FreeMem( MV_Voices );
+       MV_Voices      = NULL;
+       MV_TotalMemory = 0;
+	   MV_UnlockMemory();
+	   MV_SetErrorCode( MV_DPMI_Error );
+	   return( MV_Error );
+   }
+
+   MV_FooBuffer = ptr;
+
+// Start the playback engine
+   status = MV_StartPlayback();
+   if ( status != MV_Ok )
+      {
+      // Preserve error code while we shutdown.
+      status = MV_ErrorCode;
+      MV_Shutdown();
+      MV_SetErrorCode( status );
+      return( MV_Error );
+      }
+
+   if ( MV_TestPlayback() != MV_Ok )
+      {
+      status = MV_ErrorCode;
+      MV_Shutdown();
+      MV_SetErrorCode( status );
+      return( MV_Error );
+      }
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_Shutdown
+
+   Restore any resources allocated by Multivoc back to the system.
+---------------------------------------------------------------------*/
+
+int MV_Shutdown
+   (
+   void
+   )
+
+   {
+   int      buffer;
+   unsigned flags;
+
+   if ( !MV_Installed )
+      {
+      return( MV_Ok );
+      }
+
+   flags = DisableInterrupts();
+
+   MV_KillAllVoices();
+
+   MV_Installed = FALSE;
+
+   // Stop the sound recording engine
+   if ( MV_Recording )
+      {
+      MV_StopRecord();
+      }
+
+   // Stop the sound playback engine
+   MV_StopPlayback();
+
+   // Free reverb buffer, if allocated
+   MV_FPReverbFree();
+   //DeleteCriticalSection(&reverbCS);
+   SDL_DestroyMutex(reverbMutex);
+
+   // Shutdown the sound card
+#ifdef PLAT_DOS
+   switch( MV_SoundCard )
+      {
+      case UltraSound :
+         GUSWAVE_Shutdown();
+         break;
+
+      case SoundBlaster :
+      case Awe32 :
+         BLASTER_Shutdown();
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         PAS_Shutdown();
+         break;
+
+      case SoundScape :
+         SOUNDSCAPE_Shutdown();
+         break;
+
+      #ifndef SOUNDSOURCE_OFF
+      case SoundSource :
+      case TandySoundSource :
+         SS_Shutdown();
+         break;
+      #endif
+      }
+#else
+   DSL_Shutdown();
+#endif
+
+   RestoreInterrupts( flags );
+
+   // Free any voices we allocated
+   DPMI_UnlockMemory( MV_FooBuffer, MV_FooMemory );
+   USRHOOKS_FreeMem( MV_FooBuffer );
+   MV_FooBuffer = NULL;
+   MV_FooMemory = 0;
+
+   DPMI_UnlockMemory( MV_Voices, MV_TotalMemory );
+   USRHOOKS_FreeMem( MV_Voices );
+   MV_Voices      = NULL;
+   MV_TotalMemory = 0;
+
+   LL_Reset( &VoiceList, next, prev );
+   LL_Reset( &VoicePool, next, prev );
+
+   MV_MaxVoices = 1;
+
+   // Release the descriptor from our mix buffer
+   DPMI_FreeDOSMemory( MV_BufferDescriptor );
+   for( buffer = 0; buffer < NumberOfBuffers; buffer++ )
+      {
+      MV_MixBuffer[ buffer ] = NULL;
+      }
+
+   return( MV_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void MV_UnlockMemory
+   (
+   void
+   )
+
+   {
+   PITCH_UnlockMemory();
+
+   DPMI_UnlockMemoryRegion( MV_LockStart, MV_LockEnd );
+//   DPMI_Unlock( MV_VolumeTable );
+   DPMI_Unlock( MV_PanTable );
+   DPMI_Unlock( MV_Installed );
+   DPMI_Unlock( MV_SoundCard );
+   DPMI_Unlock( MV_TotalVolume );
+   DPMI_Unlock( MV_MaxVoices );
+   DPMI_Unlock( MV_BufferSize );
+   DPMI_Unlock( MV_BufferLength );
+   DPMI_Unlock( MV_SampleSize );
+   DPMI_Unlock( MV_NumberOfBuffers );
+   DPMI_Unlock( MV_MixMode );
+   DPMI_Unlock( MV_Channels );
+   DPMI_Unlock( MV_Bits );
+   DPMI_Unlock( MV_Silence );
+   DPMI_Unlock( MV_SwapLeftRight );
+   DPMI_Unlock( MV_RequestedMixRate );
+   DPMI_Unlock( MV_MixRate );
+   DPMI_Unlock( MV_BufferDescriptor );
+   DPMI_Unlock( MV_MixBuffer );
+   DPMI_Unlock( MV_BufferEmpty );
+   DPMI_Unlock( MV_Voices );
+   DPMI_Unlock( MV_FooBuffer );
+   DPMI_Unlock( VoiceList );
+   DPMI_Unlock( VoicePool );
+   DPMI_Unlock( MV_MixPage );
+   DPMI_Unlock( MV_VoiceHandle );
+   DPMI_Unlock( MV_CallBackFunc );
+   DPMI_Unlock( MV_RecordFunc );
+   DPMI_Unlock( MV_Recording );
+   DPMI_Unlock( MV_MixFunction );
+//   DPMI_Unlock( MV_HarshClipTable );
+   DPMI_Unlock( MV_MixDestination );
+   DPMI_Unlock( MV_LeftVolume );
+   DPMI_Unlock( MV_RightVolume );
+   DPMI_Unlock( MV_MixPosition );
+   DPMI_Unlock( MV_ErrorCode );
+   DPMI_Unlock( MV_DMAChannel );
+   DPMI_Unlock( MV_BuffShift );
+   DPMI_Unlock( MV_ReverbLevel );
+   DPMI_Unlock( MV_ReverbDelay );
+   DPMI_Unlock( MV_ReverbTable );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MV_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int MV_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+   int pitchstatus;
+
+   status  = DPMI_LockMemoryRegion( MV_LockStart, MV_LockEnd );
+//   status |= DPMI_Lock( MV_VolumeTable );
+   status |= DPMI_Lock( MV_PanTable );
+   status |= DPMI_Lock( MV_Installed );
+   status |= DPMI_Lock( MV_SoundCard );
+   status |= DPMI_Lock( MV_TotalVolume );
+   status |= DPMI_Lock( MV_MaxVoices );
+   status |= DPMI_Lock( MV_BufferSize );
+   status |= DPMI_Lock( MV_BufferLength );
+   status |= DPMI_Lock( MV_SampleSize );
+   status |= DPMI_Lock( MV_NumberOfBuffers );
+   status |= DPMI_Lock( MV_MixMode );
+   status |= DPMI_Lock( MV_Channels );
+   status |= DPMI_Lock( MV_Bits );
+   status |= DPMI_Lock( MV_Silence );
+   status |= DPMI_Lock( MV_SwapLeftRight );
+   status |= DPMI_Lock( MV_RequestedMixRate );
+   status |= DPMI_Lock( MV_MixRate );
+   status |= DPMI_Lock( MV_BufferDescriptor );
+   status |= DPMI_Lock( MV_MixBuffer );
+   status |= DPMI_Lock( MV_BufferEmpty );
+   status |= DPMI_Lock( MV_Voices );
+   status |= DPMI_Lock( MV_FooBuffer );
+   status |= DPMI_Lock( VoiceList );
+   status |= DPMI_Lock( VoicePool );
+   status |= DPMI_Lock( MV_MixPage );
+   status |= DPMI_Lock( MV_VoiceHandle );
+   status |= DPMI_Lock( MV_CallBackFunc );
+   status |= DPMI_Lock( MV_RecordFunc );
+   status |= DPMI_Lock( MV_Recording );
+   status |= DPMI_Lock( MV_MixFunction );
+//   status |= DPMI_Lock( MV_HarshClipTable );
+   status |= DPMI_Lock( MV_MixDestination );
+   status |= DPMI_Lock( MV_LeftVolume );
+   status |= DPMI_Lock( MV_RightVolume );
+   status |= DPMI_Lock( MV_MixPosition );
+   status |= DPMI_Lock( MV_ErrorCode );
+   status |= DPMI_Lock( MV_DMAChannel );
+   status |= DPMI_Lock( MV_BuffShift );
+   status |= DPMI_Lock( MV_ReverbLevel );
+   status |= DPMI_Lock( MV_ReverbDelay );
+   status |= DPMI_Lock( MV_ReverbTable );
+
+   pitchstatus = PITCH_LockMemory();
+   if ( ( pitchstatus != PITCH_Ok ) || ( status != DPMI_Ok ) )
+      {
+      MV_UnlockMemory();
+      MV_SetErrorCode( MV_DPMI_Error );
+      return( MV_Error );
+      }
+
+   return( MV_Ok );
+   }
+
+#ifndef PLAT_DOS
+void ClearBuffer_DW( void *ptr, unsigned data, int length )
+{
+	unsigned *d = (unsigned *)ptr;
+	
+	while (length--) {
+		*d = data;
+		
+		d++;
+	}
+}
+#endif
--- /dev/null
+++ b/Game/src/audiolib/multivoc.h
@@ -1,0 +1,130 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   file:   MULTIVOC.H
+
+   author: James R. Dose
+   date:   December 20, 1993
+
+   Public header for MULTIVOC.C
+
+   (c) Copyright 1993 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __MULTIVOC_H
+#define __MULTIVOC_H
+
+//#include <windows.h>
+#include <SDL.h>
+
+// forward declare
+//struct SDL_mutex;
+
+
+#define MV_MinVoiceHandle  1
+
+extern int MV_ErrorCode;
+
+enum MV_Errors
+   {
+   MV_Warning = -2,
+   MV_Error   = -1,
+   MV_Ok      = 0,
+   MV_UnsupportedCard,
+   MV_NotInstalled,
+   MV_NoVoices,
+   MV_NoMem,
+   MV_VoiceNotFound,
+   MV_BlasterError,
+   MV_PasError,
+   MV_SoundScapeError,
+   MV_SoundSourceError,
+   MV_DPMI_Error,
+   MV_InvalidVOCFile,
+   MV_InvalidWAVFile,
+   MV_InvalidMixMode,
+   MV_SoundSourceFailure,
+   MV_IrqFailure,
+   MV_DMAFailure,
+   MV_DMA16Failure,
+   MV_NullRecordFunction
+   };
+
+char *MV_ErrorString( int ErrorNumber );
+int   MV_VoicePlaying( int handle );
+int   MV_KillAllVoices( void );
+int   MV_Kill( int handle );
+int   MV_VoicesPlaying( void );
+int   MV_VoiceAvailable( int priority );
+int   MV_SetPitch( int handle, int pitchoffset );
+int   MV_SetFrequency( int handle, int frequency );
+int   MV_EndLooping( int handle );
+int   MV_SetPan( int handle, int vol, int left, int right );
+int   MV_Pan3D( int handle, int angle, int distance );
+void  MV_SetReverb( int reverb );
+void  MV_SetFastReverb( int reverb );
+int   MV_GetMaxReverbDelay( void );
+int   MV_GetReverbDelay( void );
+void  MV_SetReverbDelay( int delay );
+int   MV_SetMixMode( int numchannels, int samplebits );
+int   MV_StartPlayback( void );
+void  MV_StopPlayback( void );
+int   MV_StartRecording( int MixRate, void ( *function )( char *ptr, int length ) );
+void  MV_StopRecord( void );
+int   MV_StartDemandFeedPlayback( void ( *function )( char **ptr, unsigned long *length ),
+         int rate, int pitchoffset, int vol, int left, int right,
+         int priority, unsigned long callbackval );
+int   MV_PlayRaw( char *ptr, unsigned long length,
+         unsigned rate, int pitchoffset, int vol, int left,
+         int right, int priority, unsigned long callbackval );
+int   MV_PlayLoopedRaw( char *ptr, unsigned long length,
+         char *loopstart, char *loopend, unsigned rate, int pitchoffset,
+         int vol, int left, int right, int priority,
+         unsigned long callbackval );
+int   MV_PlayWAV( char *ptr, int pitchoffset, int vol, int left,
+         int right, int priority, unsigned long callbackval );
+int   MV_PlayWAV3D( char *ptr, int pitchoffset, int angle, int distance,
+         int priority, unsigned long callbackval );
+int   MV_PlayLoopedWAV( char *ptr, long loopstart, long loopend,
+         int pitchoffset, int vol, int left, int right, int priority,
+         unsigned long callbackval );
+int   MV_PlayVOC3D( char *ptr, int pitchoffset, int angle, int distance,
+         int priority, unsigned long callbackval );
+int   MV_PlayVOC( char *ptr, int pitchoffset, int vol, int left, int right,
+         int priority, unsigned long callbackval );
+int   MV_PlayLoopedVOC( char *ptr, long loopstart, long loopend,
+         int pitchoffset, int vol, int left, int right, int priority,
+         unsigned long callbackval );
+void  MV_CreateVolumeTable( int index, int volume, int MaxVolume );
+void  MV_SetVolume( int volume );
+int   MV_GetVolume( void );
+void  MV_SetCallBack( void ( *function )( unsigned long ) );
+void  MV_SetReverseStereo( int setting );
+int   MV_GetReverseStereo( void );
+int   MV_Init( int soundcard, int MixRate, int Voices, int numchannels,
+         int samplebits );
+int   MV_Shutdown( void );
+void  MV_UnlockMemory( void );
+int   MV_LockMemory( void );
+
+//CRITICAL_SECTION reverbCS;
+SDL_mutex* reverbMutex;
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/music.c
@@ -1,0 +1,1035 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MUSIC.C
+
+   author: James R. Dose
+   date:   March 25, 1994
+
+   Device independant music playback routines.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "task_man.h"
+#include "sndcards.h"
+#include "music.h"
+#include "midi.h"
+#include "al_midi.h"
+#include "pas16.h"
+#include "blaster.h"
+#include "gusmidi.h"
+#include "mpu401.h"
+#include "awe32.h"
+#include "sndscape.h"
+#include "ll_man.h"
+#include "user.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+void TextMode( void );
+#pragma aux TextMode =  \
+    "mov    ax, 0003h", \
+    "int    10h"        \
+    modify [ ax ];
+
+int MUSIC_SoundDevice = -1;
+int MUSIC_ErrorCode = MUSIC_Ok;
+
+static midifuncs MUSIC_MidiFunctions;
+
+static int       MUSIC_FadeLength;
+static int       MUSIC_FadeRate;
+static unsigned  MUSIC_CurrentFadeVolume;
+static unsigned  MUSIC_LastFadeVolume;
+static int       MUSIC_EndingFadeVolume;
+static task     *MUSIC_FadeTask = NULL;
+
+int MUSIC_InitAWE32( midifuncs *Funcs );
+int MUSIC_InitFM( int card, midifuncs *Funcs );
+int MUSIC_InitMidi( int card, midifuncs *Funcs, int Address );
+int MUSIC_InitGUS( midifuncs *Funcs );
+
+#define MUSIC_SetErrorCode( status ) \
+   MUSIC_ErrorCode = ( status );
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *MUSIC_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case MUSIC_Warning :
+      case MUSIC_Error :
+         ErrorString = MUSIC_ErrorString( MUSIC_ErrorCode );
+         break;
+
+      case MUSIC_Ok :
+         ErrorString = "Music ok.";
+         break;
+
+      case MUSIC_ASSVersion :
+         ErrorString = "Apogee Sound System Version " ASS_VERSION_STRING "  "
+            "Programmed by Jim Dose\n"
+            "(c) Copyright 1996 James R. Dose.  All Rights Reserved.\n";
+         break;
+
+      case MUSIC_SoundCardError :
+         switch( MUSIC_SoundDevice )
+         {
+            case SoundBlaster :
+            case WaveBlaster :
+               ErrorString = BLASTER_ErrorString( BLASTER_Error );
+               break;
+
+            case ProAudioSpectrum :
+            case SoundMan16 :
+               ErrorString = PAS_ErrorString( PAS_Error );
+               break;
+
+            case Adlib :
+               ErrorString = "Adlib error.";
+               break;
+
+            case GenMidi :
+            case SoundCanvas :
+               ErrorString = "Could not detect MPU-401.";
+               break;
+
+            case SoundScape :
+               ErrorString = SOUNDSCAPE_ErrorString( SOUNDSCAPE_Error );
+               break;
+
+            case Awe32 :
+               ErrorString = AWE32_ErrorString( AWE32_Error );
+               break;
+
+            case UltraSound :
+               ErrorString = GUS_ErrorString( GUS_Error );
+               break;
+
+            default :
+               ErrorString = MUSIC_ErrorString( MUSIC_InvalidCard );
+               break;
+            }
+         break;
+
+      case MUSIC_MPU401Error :
+         ErrorString = "Could not detect MPU-401.";
+         break;
+
+      case MUSIC_InvalidCard :
+         ErrorString = "Invalid Music device.";
+         break;
+
+      case MUSIC_MidiError :
+         ErrorString = "Error playing MIDI file.";
+         break;
+
+      case MUSIC_TaskManError :
+         ErrorString = "TaskMan error.";
+         break;
+
+      case MUSIC_FMNotDetected :
+         ErrorString = "Could not detect FM chip.";
+         break;
+
+      case MUSIC_DPMI_Error :
+         ErrorString = "DPMI Error in MUSIC.";
+         break;
+
+      default :
+         ErrorString = "Unknown Music error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Init
+
+   Selects which sound device to use.
+---------------------------------------------------------------------*/
+
+int MUSIC_Init
+   (
+   int SoundCard,
+   int Address
+   )
+
+   {
+   int i;
+   int status;
+
+   if ( USER_CheckParameter( "ASSVER" ) )
+      {
+      MUSIC_SetErrorCode( MUSIC_ASSVersion );
+      return( MUSIC_Error );
+      }
+
+   status = LL_LockMemory();
+   if ( status != LL_Ok )
+      {
+      MUSIC_SetErrorCode( MUSIC_DPMI_Error );
+      return( MUSIC_Error );
+      }
+
+   for( i = 0; i < 128; i++ )
+      {
+      MIDI_PatchMap[ i ] = i;
+      }
+
+   status = MUSIC_Ok;
+   MUSIC_SoundDevice = SoundCard;
+
+   switch( SoundCard )
+      {
+      case SoundBlaster :
+      case Adlib :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         status = MUSIC_InitFM( SoundCard, &MUSIC_MidiFunctions );
+         break;
+
+      case GenMidi :
+      case SoundCanvas :
+      case WaveBlaster :
+      case SoundScape :
+         status = MUSIC_InitMidi( SoundCard, &MUSIC_MidiFunctions, Address );
+         break;
+
+      case Awe32 :
+         status = MUSIC_InitAWE32( &MUSIC_MidiFunctions );
+         break;
+
+      case UltraSound :
+         status = MUSIC_InitGUS( &MUSIC_MidiFunctions );
+         break;
+
+      case SoundSource :
+      case PC :
+      default :
+         MUSIC_SetErrorCode( MUSIC_InvalidCard );
+         status = MUSIC_Error;
+      }
+
+   if ( status != MUSIC_Ok )
+      {
+      LL_UnlockMemory();
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Shutdown
+
+   Terminates use of sound device.
+---------------------------------------------------------------------*/
+
+int MUSIC_Shutdown
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status = MUSIC_Ok;
+
+   MIDI_StopSong();
+
+   if ( MUSIC_FadeTask != NULL )
+      {
+      MUSIC_StopFade();
+      }
+
+   switch ( MUSIC_SoundDevice )
+      {
+      case Adlib :
+         AL_Shutdown();
+         break;
+
+      case SoundBlaster :
+         AL_Shutdown();
+         BLASTER_RestoreMidiVolume();
+         break;
+
+      case GenMidi :
+      case SoundCanvas :
+      case SoundScape :
+         MPU_Reset();
+         break;
+
+      case WaveBlaster :
+         BLASTER_ShutdownWaveBlaster();
+         MPU_Reset();
+         BLASTER_RestoreMidiVolume();
+         break;
+
+      case Awe32 :
+         AWE32_Shutdown();
+         BLASTER_RestoreMidiVolume();
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         AL_Shutdown();
+         PAS_RestoreMusicVolume();
+         break;
+
+      case UltraSound :
+         GUSMIDI_Shutdown();
+         break;
+      }
+
+   LL_UnlockMemory();
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetMaxFMMidiChannel
+
+   Sets the maximum MIDI channel that FM cards respond to.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetMaxFMMidiChannel
+   (
+   int channel
+   )
+
+   {
+   AL_SetMaxMidiChannel( channel );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetVolume
+
+   Sets the volume of music playback.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetVolume
+   (
+   int volume
+   )
+
+   {
+   volume = max( 0, volume );
+   volume = min( volume, 255 );
+
+   if ( MUSIC_SoundDevice != -1 )
+      {
+      MIDI_SetVolume( volume );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetMidiChannelVolume
+
+   Sets the volume of music playback on the specified MIDI channel.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetMidiChannelVolume
+   (
+   int channel,
+   int volume
+   )
+
+   {
+   MIDI_SetUserChannelVolume( channel, volume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_ResetMidiChannelVolumes
+
+   Sets the volume of music playback on all MIDI channels to full volume.
+---------------------------------------------------------------------*/
+
+void MUSIC_ResetMidiChannelVolumes
+   (
+   void
+   )
+
+   {
+   MIDI_ResetUserChannelVolume();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetVolume
+
+   Returns the volume of music playback.
+---------------------------------------------------------------------*/
+
+int MUSIC_GetVolume
+   (
+   void
+   )
+
+   {
+   if ( MUSIC_SoundDevice == -1 )
+      {
+      return( 0 );
+      }
+   return( MIDI_GetVolume() );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetLoopFlag
+
+   Set whether the music will loop or end when it reaches the end of
+   the song.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetLoopFlag
+   (
+   int loopflag
+   )
+
+   {
+   MIDI_SetLoopFlag( loopflag );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SongPlaying
+
+   Returns whether there is a song playing.
+---------------------------------------------------------------------*/
+
+int MUSIC_SongPlaying
+   (
+   void
+   )
+
+   {
+   return( MIDI_SongPlaying() );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Continue
+
+   Continues playback of a paused song.
+---------------------------------------------------------------------*/
+
+void MUSIC_Continue
+   (
+   void
+   )
+
+   {
+   MIDI_ContinueSong();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_Pause
+
+   Pauses playback of a song.
+---------------------------------------------------------------------*/
+
+void MUSIC_Pause
+   (
+   void
+   )
+
+   {
+   MIDI_PauseSong();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_StopSong
+
+   Stops playback of current song.
+---------------------------------------------------------------------*/
+
+int MUSIC_StopSong
+   (
+   void
+   )
+
+   {
+   MUSIC_StopFade();
+   MIDI_StopSong();
+   MUSIC_SetErrorCode( MUSIC_Ok );
+   return( MUSIC_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_PlaySong
+
+   Begins playback of MIDI song.
+---------------------------------------------------------------------*/
+
+int MUSIC_PlaySong
+   (
+   unsigned char *song,
+   int loopflag
+   )
+
+   {
+   int status;
+
+   switch( MUSIC_SoundDevice )
+      {
+      case SoundBlaster :
+      case Adlib :
+      case ProAudioSpectrum :
+      case SoundMan16 :
+      case GenMidi :
+      case SoundCanvas :
+      case WaveBlaster :
+      case SoundScape :
+      case Awe32 :
+      case UltraSound :
+         MIDI_StopSong();
+         status = MIDI_PlaySong( song, loopflag );
+         if ( status != MIDI_Ok )
+            {
+            MUSIC_SetErrorCode( MUSIC_MidiError );
+            return( MUSIC_Warning );
+            }
+         break;
+
+      case SoundSource :
+      case PC :
+      default :
+         MUSIC_SetErrorCode( MUSIC_InvalidCard );
+         return( MUSIC_Warning );
+         break;
+      }
+
+   return( MUSIC_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetContext
+
+   Sets the song context.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetContext
+   (
+   int context
+   )
+
+   {
+   MIDI_SetContext( context );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetContext
+
+   Returns the current song context.
+---------------------------------------------------------------------*/
+
+int MUSIC_GetContext
+   (
+   void
+   )
+
+   {
+   return MIDI_GetContext();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongTick
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongTick
+   (
+   unsigned long PositionInTicks
+   )
+
+   {
+   MIDI_SetSongTick( PositionInTicks );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongTime
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongTime
+   (
+   unsigned long milliseconds
+   )
+
+   {
+   MIDI_SetSongTime( milliseconds );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_SetSongPosition
+
+   Sets the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_SetSongPosition
+   (
+   int measure,
+   int beat,
+   int tick
+   )
+
+   {
+   MIDI_SetSongPosition( measure, beat, tick );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetSongPosition
+
+   Returns the position of the song pointer.
+---------------------------------------------------------------------*/
+
+void MUSIC_GetSongPosition
+   (
+   songposition *pos
+   )
+
+   {
+   MIDI_GetSongPosition( pos );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_GetSongLength
+
+   Returns the length of the song.
+---------------------------------------------------------------------*/
+
+void MUSIC_GetSongLength
+   (
+   songposition *pos
+   )
+
+   {
+   MIDI_GetSongLength( pos );
+   }
+
+
+int MUSIC_InitAWE32
+   (
+   midifuncs *Funcs
+   )
+
+   {
+   int status;
+
+   status = AWE32_Init();
+   if ( status != AWE32_Ok )
+      {
+      MUSIC_SetErrorCode( MUSIC_SoundCardError );
+      return( MUSIC_Error );
+      }
+
+   Funcs->NoteOff           = AWE32_NoteOff;
+   Funcs->NoteOn            = AWE32_NoteOn;
+   Funcs->PolyAftertouch    = AWE32_PolyAftertouch;
+   Funcs->ControlChange     = AWE32_ControlChange;
+   Funcs->ProgramChange     = AWE32_ProgramChange;
+   Funcs->ChannelAftertouch = AWE32_ChannelAftertouch;
+   Funcs->PitchBend         = AWE32_PitchBend;
+   Funcs->ReleasePatches    = NULL;
+   Funcs->LoadPatch         = NULL;
+   Funcs->SetVolume         = NULL;
+   Funcs->GetVolume         = NULL;
+
+   if ( BLASTER_CardHasMixer() )
+      {
+      BLASTER_SaveMidiVolume();
+      Funcs->SetVolume = BLASTER_SetMidiVolume;
+      Funcs->GetVolume = BLASTER_GetMidiVolume;
+      }
+
+   status = MUSIC_Ok;
+   MIDI_SetMidiFuncs( Funcs );
+
+   return( status );
+   }
+
+
+int MUSIC_InitFM
+   (
+   int card,
+   midifuncs *Funcs
+   )
+
+   {
+   int status;
+   int passtatus;
+
+   status = MIDI_Ok;
+
+   if ( !AL_DetectFM() )
+      {
+      MUSIC_SetErrorCode( MUSIC_FMNotDetected );
+      return( MUSIC_Error );
+      }
+
+   // Init the fm routines
+   AL_Init( card );
+
+   Funcs->NoteOff           = AL_NoteOff;
+   Funcs->NoteOn            = AL_NoteOn;
+   Funcs->PolyAftertouch    = NULL;
+   Funcs->ControlChange     = AL_ControlChange;
+   Funcs->ProgramChange     = AL_ProgramChange;
+   Funcs->ChannelAftertouch = NULL;
+   Funcs->PitchBend         = AL_SetPitchBend;
+   Funcs->ReleasePatches    = NULL;
+   Funcs->LoadPatch         = NULL;
+   Funcs->SetVolume         = NULL;
+   Funcs->GetVolume         = NULL;
+
+   switch( card )
+      {
+      case SoundBlaster :
+         if ( BLASTER_CardHasMixer() )
+            {
+            BLASTER_SaveMidiVolume();
+            Funcs->SetVolume = BLASTER_SetMidiVolume;
+            Funcs->GetVolume = BLASTER_GetMidiVolume;
+            }
+         else
+            {
+            Funcs->SetVolume = NULL;
+            Funcs->GetVolume = NULL;
+            }
+         break;
+
+      case Adlib :
+         Funcs->SetVolume = NULL;
+         Funcs->GetVolume = NULL;
+         break;
+
+      case ProAudioSpectrum :
+      case SoundMan16 :
+         Funcs->SetVolume = NULL;
+         Funcs->GetVolume = NULL;
+
+         passtatus = PAS_SaveMusicVolume();
+         if ( passtatus == PAS_Ok )
+            {
+            Funcs->SetVolume = PAS_SetFMVolume;
+            Funcs->GetVolume = PAS_GetFMVolume;
+            }
+         break;
+      }
+
+   MIDI_SetMidiFuncs( Funcs );
+
+   return( status );
+   }
+
+int MUSIC_InitMidi
+   (
+   int        card,
+   midifuncs *Funcs,
+   int        Address
+   )
+
+   {
+   int status;
+
+   status = MUSIC_Ok;
+
+   if ( ( card == WaveBlaster ) || ( card == SoundCanvas ) ||
+      ( card == GenMidi ) )
+      {
+      // Setup WaveBlaster Daughterboard clone
+      // (ie. SoundCanvas DB, TurtleBeach Rio)
+      BLASTER_SetupWaveBlaster();
+      }
+
+   if ( card == SoundScape )
+      {
+      Address = SOUNDSCAPE_GetMIDIPort();
+      if ( Address < SOUNDSCAPE_Ok )
+         {
+         MUSIC_SetErrorCode( MUSIC_SoundCardError );
+         return( MUSIC_Error );
+         }
+      }
+
+   if ( MPU_Init( Address ) != MPU_Ok )
+      {
+      MUSIC_SetErrorCode( MUSIC_MPU401Error );
+      return( MUSIC_Error );
+      }
+
+   Funcs->NoteOff           = MPU_NoteOff;
+   Funcs->NoteOn            = MPU_NoteOn;
+   Funcs->PolyAftertouch    = MPU_PolyAftertouch;
+   Funcs->ControlChange     = MPU_ControlChange;
+   Funcs->ProgramChange     = MPU_ProgramChange;
+   Funcs->ChannelAftertouch = MPU_ChannelAftertouch;
+   Funcs->PitchBend         = MPU_PitchBend;
+   Funcs->ReleasePatches    = NULL;
+   Funcs->LoadPatch         = NULL;
+   Funcs->SetVolume         = NULL;
+   Funcs->GetVolume         = NULL;
+
+   if ( card == WaveBlaster )
+      {
+      if ( BLASTER_CardHasMixer() )
+         {
+         BLASTER_SaveMidiVolume();
+         Funcs->SetVolume = BLASTER_SetMidiVolume;
+         Funcs->GetVolume = BLASTER_GetMidiVolume;
+         }
+      }
+
+   MIDI_SetMidiFuncs( Funcs );
+
+   return( status );
+   }
+
+int MUSIC_InitGUS
+   (
+   midifuncs *Funcs
+   )
+
+   {
+   int status;
+
+   status = MUSIC_Ok;
+
+   if ( GUSMIDI_Init() != GUS_Ok )
+      {
+      MUSIC_SetErrorCode( MUSIC_SoundCardError );
+      return( MUSIC_Error );
+      }
+
+   Funcs->NoteOff           = GUSMIDI_NoteOff;
+   Funcs->NoteOn            = GUSMIDI_NoteOn;
+   Funcs->PolyAftertouch    = NULL;
+   Funcs->ControlChange     = GUSMIDI_ControlChange;
+   Funcs->ProgramChange     = GUSMIDI_ProgramChange;
+   Funcs->ChannelAftertouch = NULL;
+   Funcs->PitchBend         = GUSMIDI_PitchBend;
+   Funcs->ReleasePatches    = NULL;//GUSMIDI_ReleasePatches;
+   Funcs->LoadPatch         = NULL;//GUSMIDI_LoadPatch;
+   Funcs->SetVolume         = GUSMIDI_SetVolume;
+   Funcs->GetVolume         = GUSMIDI_GetVolume;
+
+   MIDI_SetMidiFuncs( Funcs );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_FadeRoutine
+
+   Fades music volume from current level to another over a specified
+   period of time.
+---------------------------------------------------------------------*/
+
+static void MUSIC_FadeRoutine
+   (
+   task *Task
+   )
+
+   {
+   int volume;
+
+   MUSIC_CurrentFadeVolume += MUSIC_FadeRate;
+   if ( MUSIC_FadeLength == 0 )
+      {
+      MIDI_SetVolume( MUSIC_EndingFadeVolume );
+      TS_Terminate( Task );
+      MUSIC_FadeTask = NULL;
+      }
+   else
+      {
+      MUSIC_FadeLength--;
+//      if ( ( MUSIC_SoundDevice == GenMidi ) &&
+//         ( ( MUSIC_FadeLength % 12 ) != 0 ) )
+//         {
+//         return;
+//         }
+
+      volume = MUSIC_CurrentFadeVolume >> 7;
+      if ( MUSIC_LastFadeVolume != volume )
+         {
+         MUSIC_LastFadeVolume = volume;
+         MIDI_SetVolume( volume );
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_FadeVolume
+
+   Fades music volume from current level to another over a specified
+   period of time.
+---------------------------------------------------------------------*/
+
+int MUSIC_FadeVolume
+   (
+   int tovolume,
+   int milliseconds
+   )
+
+   {
+   int fromvolume;
+
+   if ( ( MUSIC_SoundDevice == ProAudioSpectrum ) ||
+      ( MUSIC_SoundDevice == SoundMan16 ) ||
+      ( MUSIC_SoundDevice == GenMidi ) ||
+      ( MUSIC_SoundDevice == SoundScape ) ||
+      ( MUSIC_SoundDevice == SoundCanvas ) )
+      {
+      MIDI_SetVolume( tovolume );
+      return( MUSIC_Ok );
+      }
+
+   if ( MUSIC_FadeTask != NULL )
+      {
+      MUSIC_StopFade();
+      }
+
+   tovolume = max( 0, tovolume );
+   tovolume = min( 255, tovolume );
+   fromvolume = MUSIC_GetVolume();
+
+   MUSIC_FadeLength = milliseconds / 25;
+   MUSIC_FadeRate   = ( ( tovolume - fromvolume ) << 7 ) / MUSIC_FadeLength;
+   MUSIC_LastFadeVolume = fromvolume;
+   MUSIC_CurrentFadeVolume = fromvolume << 7;
+   MUSIC_EndingFadeVolume = tovolume;
+
+   MUSIC_FadeTask = TS_ScheduleTask( MUSIC_FadeRoutine, 40, 1, NULL );
+   if ( MUSIC_FadeTask == NULL )
+      {
+      MUSIC_SetErrorCode( MUSIC_TaskManError );
+      return( MUSIC_Warning );
+      }
+
+   TS_Dispatch();
+   return( MUSIC_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_FadeActive
+
+   Returns whether the fade routine is active.
+---------------------------------------------------------------------*/
+
+int MUSIC_FadeActive
+   (
+   void
+   )
+
+   {
+   return( MUSIC_FadeTask != NULL );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_StopFade
+
+   Stops fading the music.
+---------------------------------------------------------------------*/
+
+void MUSIC_StopFade
+   (
+   void
+   )
+
+   {
+   if ( MUSIC_FadeTask != NULL )
+      {
+      TS_Terminate( MUSIC_FadeTask );
+      MUSIC_FadeTask = NULL;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_RerouteMidiChannel
+
+   Sets callback function to reroute MIDI commands from specified
+   function.
+---------------------------------------------------------------------*/
+
+void MUSIC_RerouteMidiChannel
+   (
+   int channel,
+   int cdecl ( *function )( int event, int c1, int c2 )
+   )
+
+   {
+   MIDI_RerouteMidiChannel( channel, function );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: MUSIC_RegisterTimbreBank
+
+   Halts playback of all sounds.
+---------------------------------------------------------------------*/
+
+void MUSIC_RegisterTimbreBank
+   (
+   unsigned char *timbres
+   )
+
+   {
+   AL_RegisterTimbreBank( timbres );
+   }
--- /dev/null
+++ b/Game/src/audiolib/music.h
@@ -1,0 +1,96 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: MUSIC.H
+
+   author: James R. Dose
+   date:   March 25, 1994
+
+   Public header for MUSIC.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __MUSIC_H
+#define __MUSIC_H
+
+#include "sndcards.h"
+
+#ifndef PLAT_DOS
+#define cdecl
+#endif
+
+extern int MUSIC_ErrorCode;
+
+enum MUSIC_ERRORS
+   {
+   MUSIC_Warning = -2,
+   MUSIC_Error   = -1,
+   MUSIC_Ok      = 0,
+   MUSIC_ASSVersion,
+   MUSIC_SoundCardError,
+   MUSIC_MPU401Error,
+   MUSIC_InvalidCard,
+   MUSIC_MidiError,
+   MUSIC_TaskManError,
+   MUSIC_FMNotDetected,
+   MUSIC_DPMI_Error
+   };
+
+typedef struct
+   {
+   unsigned long tickposition;
+   unsigned long milliseconds;
+   unsigned int  measure;
+   unsigned int  beat;
+   unsigned int  tick;
+   } songposition;
+
+#define MUSIC_LoopSong ( 1 == 1 )
+#define MUSIC_PlayOnce ( !MUSIC_LoopSong )
+
+char *MUSIC_ErrorString( int ErrorNumber );
+int   MUSIC_Init( int SoundCard, int Address );
+int   MUSIC_Shutdown( void );
+void  MUSIC_SetMaxFMMidiChannel( int channel );
+void  MUSIC_SetVolume( int volume );
+void  MUSIC_SetMidiChannelVolume( int channel, int volume );
+void  MUSIC_ResetMidiChannelVolumes( void );
+int   MUSIC_GetVolume( void );
+void  MUSIC_SetLoopFlag( int loopflag );
+int   MUSIC_SongPlaying( void );
+void  MUSIC_Continue( void );
+void  MUSIC_Pause( void );
+int   MUSIC_StopSong( void );
+int   MUSIC_PlaySong( unsigned char *song, int loopflag );
+void  MUSIC_SetContext( int context );
+int   MUSIC_GetContext( void );
+void  MUSIC_SetSongTick( unsigned long PositionInTicks );
+void  MUSIC_SetSongTime( unsigned long milliseconds );
+void  MUSIC_SetSongPosition( int measure, int beat, int tick );
+void  MUSIC_GetSongPosition( songposition *pos );
+void  MUSIC_GetSongLength( songposition *pos );
+int   MUSIC_FadeVolume( int tovolume, int milliseconds );
+int   MUSIC_FadeActive( void );
+void  MUSIC_StopFade( void );
+void  MUSIC_RerouteMidiChannel( int channel, int cdecl ( *function )( int event, int c1, int c2 ) );
+void  MUSIC_RegisterTimbreBank( unsigned char *timbres );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/mv_mix.asm
@@ -1,0 +1,505 @@
+        IDEAL
+
+        p386
+        MODEL  flat
+
+        dataseg
+        CODESEG
+
+        MASM
+        ALIGN 4
+
+EXTRN   _MV_HarshClipTable:DWORD
+EXTRN   _MV_MixDestination:DWORD
+EXTRN   _MV_MixPosition:DWORD
+EXTRN   _MV_LeftVolume:DWORD
+EXTRN   _MV_RightVolume:DWORD
+EXTRN   _MV_SampleSize:DWORD
+EXTRN   _MV_RightChannelOffset:DWORD
+
+;================
+;
+; MV_Mix8BitMono
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix8BitMono_
+PUBLIC  MV_Mix8BitMono_
+; Two at once
+        pushad
+
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET apatch7+2            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET apatch8+2            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET apatch9+3            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume             ; Since we're mono, use left volume
+        mov     eax,OFFSET apatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET apatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Harsh Clip table ptr
+        mov     ebx, _MV_HarshClipTable
+        add     ebx, 128
+        mov     eax,OFFSET apatch3+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET apatch4+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET apatch5+2            ; convice tasm to modify code...
+        mov     [eax],edx
+        mov     eax,OFFSET apatch6+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        shr     ecx, 1                          ; double sample count
+        cmp     ecx, 0
+        je      short exit8m
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; apatch1 - volume table
+; apatch2 - volume table
+; apatch3 - harsh clip table
+; apatch4 - harsh clip table
+; apatch5 - sample rate
+; apatch6 - sample rate
+
+        mov     eax,ebp                         ; begin calculating first sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     eax,16                          ; finish calculation for first sample
+
+        mov     ebx,ebp                         ; begin calculating second sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     ebx,16                          ; finish calculation for second sample
+
+        movzx   eax, byte ptr [esi+eax]         ; get first sample
+        movzx   ebx, byte ptr [esi+ebx]         ; get second sample
+
+        ALIGN 4
+mix8Mloop:
+        movzx   edx, byte ptr [edi]             ; get current sample from destination
+apatch1:
+        movsx   eax, byte ptr [2*eax+12345678h] ; volume translate first sample
+apatch2:
+        movsx   ebx, byte ptr [2*ebx+12345678h] ; volume translate second sample
+        add     eax, edx                        ; mix first sample
+apatch9:
+        movzx   edx, byte ptr [edi + 1]         ; get current sample from destination
+apatch3:
+        mov     eax, [eax + 12345678h]          ; harsh clip new sample
+        add     ebx, edx                        ; mix second sample
+        mov     [edi], al                       ; write new sample to destination
+        mov     edx, ebp                        ; begin calculating third sample
+apatch4:
+        mov     ebx, [ebx + 12345678h]          ; harsh clip new sample
+apatch5:
+        add     ebp,12345678h                   ; advance frac pointer
+        shr     edx, 16                         ; finish calculation for third sample
+        mov     eax, ebp                        ; begin calculating fourth sample
+apatch7:
+        add     edi, 1                          ; move destination to second sample
+        shr     eax, 16                         ; finish calculation for fourth sample
+        mov     [edi], bl                       ; write new sample to destination
+apatch6:
+        add     ebp,12345678h                   ; advance frac pointer
+        movzx   ebx, byte ptr [esi+eax]         ; get fourth sample
+        movzx   eax, byte ptr [esi+edx]         ; get third sample
+apatch8:
+        add     edi, 2                          ; move destination to third sample
+        dec     ecx                             ; decrement count
+        jnz     mix8Mloop                       ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+exit8m:
+        popad
+        ret
+ENDP    MV_Mix8BitMono_
+
+;================
+;
+; MV_Mix8BitStereo
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix8BitStereo_
+PUBLIC  MV_Mix8BitStereo_
+
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET bpatch8+2            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Right channel offset
+        mov     ebx, _MV_RightChannelOffset
+        mov     eax,OFFSET bpatch6+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET bpatch7+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET bpatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        mov     ebx, _MV_RightVolume
+        mov     eax,OFFSET bpatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET bpatch3+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        ; Harsh Clip table ptr
+        mov     ebx, _MV_HarshClipTable
+        add     ebx,128
+        mov     eax,OFFSET bpatch4+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET bpatch5+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        cmp     ecx, 0
+        je      short exit8S
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; bpatch1 - left volume table
+; bpatch2 - right volume table
+; bpatch3 - sample rate
+; bpatch4 - harsh clip table
+; bpatch5 - harsh clip table
+
+        mov     eax,ebp                         ; begin calculating first sample
+        shr     eax,16                          ; finish calculation for first sample
+
+        movzx   ebx, byte ptr [esi+eax]         ; get first sample
+
+        ALIGN 4
+mix8Sloop:
+bpatch1:
+        movsx   eax, byte ptr [2*ebx+12345678h] ; volume translate left sample
+        movzx   edx, byte ptr [edi]             ; get current sample from destination
+bpatch2:
+        movsx   ebx, byte ptr [2*ebx+12345678h] ; volume translate right sample
+        add     eax, edx                        ; mix left sample
+bpatch3:
+        add     ebp,12345678h                   ; advance frac pointer
+bpatch6:
+        movzx   edx, byte ptr [edi+12345678h]   ; get current sample from destination
+bpatch4:
+        mov     eax, [eax + 12345678h]          ; harsh clip left sample
+        add     ebx, edx                        ; mix right sample
+        mov     [edi], al                       ; write left sample to destination
+bpatch5:
+        mov     ebx, [ebx + 12345678h]          ; harsh clip right sample
+        mov     edx, ebp                        ; begin calculating second sample
+bpatch7:
+        mov     [edi+12345678h], bl             ; write right sample to destination
+        shr     edx, 16                         ; finish calculation for second sample
+bpatch8:
+        add     edi, 2                          ; move destination to second sample
+        movzx   ebx, byte ptr [esi+edx]         ; get second sample
+        dec     ecx                             ; decrement count
+        jnz     mix8Sloop                       ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+
+EXIT8S:
+        popad
+        ret
+ENDP    MV_Mix8BitStereo_
+
+;================
+;
+; MV_Mix16BitMono
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix16BitMono_
+PUBLIC  MV_Mix16BitMono_
+; Two at once
+        pushad
+
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET cpatch5+3            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET cpatch6+3            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET cpatch7+2            ; convice tasm to modify code...
+        add     bl,bl
+        mov     [eax],bl
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET cpatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET cpatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET cpatch3+2            ; convice tasm to modify code...
+        mov     [eax],edx
+        mov     eax,OFFSET cpatch4+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        shr     ecx, 1                          ; double sample count
+        cmp     ecx, 0
+        je      exit16M
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; cpatch1 - volume table
+; cpatch2 - volume table
+; cpatch3 - sample rate
+; cpatch4 - sample rate
+
+        mov     eax,ebp                         ; begin calculating first sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     eax,16                          ; finish calculation for first sample
+
+        mov     ebx,ebp                         ; begin calculating second sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     ebx,16                          ; finish calculation for second sample
+
+        movzx   eax, byte ptr [esi+eax]         ; get first sample
+        movzx   ebx, byte ptr [esi+ebx]         ; get second sample
+
+        ALIGN 4
+mix16Mloop:
+        movsx   edx, word ptr [edi]             ; get current sample from destination
+cpatch1:
+        movsx   eax, word ptr [2*eax+12345678h] ; volume translate first sample
+cpatch2:
+        movsx   ebx, word ptr [2*ebx+12345678h] ; volume translate second sample
+        add     eax, edx                        ; mix first sample
+cpatch5:
+        movsx   edx, word ptr [edi + 2]         ; get current sample from destination
+
+        cmp     eax, -32768                     ; Harsh clip sample
+        jge     short m16skip1
+        mov     eax, -32768
+        jmp     short m16skip2
+m16skip1:
+        cmp     eax, 32767
+        jle     short m16skip2
+        mov     eax, 32767
+m16skip2:
+        add     ebx, edx                        ; mix second sample
+        mov     [edi], ax                       ; write new sample to destination
+        mov     edx, ebp                        ; begin calculating third sample
+
+        cmp     ebx, -32768                     ; Harsh clip sample
+        jge     short m16skip3
+        mov     ebx, -32768
+        jmp     short m16skip4
+m16skip3:
+        cmp     ebx, 32767
+        jle     short m16skip4
+        mov     ebx, 32767
+m16skip4:
+cpatch3:
+        add     ebp,12345678h                   ; advance frac pointer
+        shr     edx, 16                         ; finish calculation for third sample
+        mov     eax, ebp                        ; begin calculating fourth sample
+cpatch6:
+        mov     [edi + 2], bx                   ; write new sample to destination
+        shr     eax, 16                         ; finish calculation for fourth sample
+
+cpatch4:
+        add     ebp,12345678h                   ; advance frac pointer
+        movzx   ebx, byte ptr [esi+eax]         ; get fourth sample
+cpatch7:
+        add     edi, 4                          ; move destination to third sample
+        movzx   eax, byte ptr [esi+edx]         ; get third sample
+        dec     ecx                             ; decrement count
+        jnz     mix16Mloop                      ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+EXIT16M:
+        popad
+        ret
+ENDP    MV_Mix16BitMono_
+
+;================
+;
+; MV_Mix16BitStereo
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix16BitStereo_
+PUBLIC  MV_Mix16BitStereo_
+
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET dpatch6+2            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Right channel offset
+        mov     ebx, _MV_RightChannelOffset
+        mov     eax,OFFSET dpatch4+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET dpatch5+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET dpatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        mov     ebx, _MV_RightVolume
+        mov     eax,OFFSET dpatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET dpatch3+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        cmp     ecx, 0
+        je      exit16S
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; dpatch1 - left volume table
+; dpatch2 - right volume table
+; dpatch3 - sample rate
+
+        mov     eax,ebp                         ; begin calculating first sample
+        shr     eax,16                          ; finish calculation for first sample
+
+        movzx   ebx, byte ptr [esi+eax]         ; get first sample
+
+        ALIGN 4
+mix16Sloop:
+dpatch1:
+        movsx   eax, word ptr [2*ebx+12345678h] ; volume translate left sample
+        movsx   edx, word ptr [edi]             ; get current sample from destination
+dpatch2:
+        movsx   ebx, word ptr [2*ebx+12345678h] ; volume translate right sample
+        add     eax, edx                        ; mix left sample
+dpatch3:
+        add     ebp,12345678h                   ; advance frac pointer
+dpatch4:
+        movsx   edx, word ptr [edi+12345678h]   ; get current sample from destination
+
+        cmp     eax, -32768                     ; Harsh clip sample
+        jge     short s16skip1
+        mov     eax, -32768
+        jmp     short s16skip2
+s16skip1:
+        cmp     eax, 32767
+        jle     short s16skip2
+        mov     eax, 32767
+s16skip2:
+        add     ebx, edx                        ; mix right sample
+        mov     [edi], ax                       ; write left sample to destination
+
+        cmp     ebx, -32768                     ; Harsh clip sample
+        jge     short s16skip3
+        mov     ebx, -32768
+        jmp     short s16skip4
+s16skip3:
+        cmp     ebx, 32767
+        jle     short s16skip4
+        mov     ebx, 32767
+s16skip4:
+
+        mov     edx, ebp                        ; begin calculating second sample
+dpatch5:
+        mov     [edi+12345678h], bx             ; write right sample to destination
+        shr     edx, 16                         ; finish calculation for second sample
+dpatch6:
+        add     edi, 4                          ; move destination to second sample
+        movzx   ebx, byte ptr [esi+edx]         ; get second sample
+        dec     ecx                             ; decrement count
+        jnz     mix16Sloop                      ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+exit16S:
+        popad
+        ret
+ENDP    MV_Mix16BitStereo_
+
+        ENDS
+
+        END
--- /dev/null
+++ b/Game/src/audiolib/mv_mix.c
@@ -1,0 +1,474 @@
+#include "multivoc.h"
+
+extern char  *MV_MixDestination;
+extern unsigned long MV_MixPosition;
+extern int *MV_GLast, *MV_GPos, *MV_GVal;
+
+extern int MV_LeftVolume;
+extern int MV_RightVolume;
+extern int MV_MaxVolume;
+
+// extern unsigned char *MV_HarshClipTable;
+
+extern int MV_RightChannelOffset;
+extern int MV_SampleSize;
+extern int MV_Channels;
+
+//int do_cubic = 1;
+
+extern int g_CV_CubicInterpolation;
+
+#define do_cubic (g_CV_CubicInterpolation)
+
+#define gval0 MV_GVal[*MV_GPos&3]
+#define gval(x) MV_GVal[(*MV_GPos+x)&3]
+
+_inline int MV_cubic(int position)
+{
+	int xd, fa;
+	xd = (position >> 1) & 0x7FFF;
+
+	fa  = gval(3) - 3*gval(2) + 3*gval(1) - gval0;
+	fa *= (xd - (2<<15)) / 6;
+	fa >>= 15;
+	fa += gval(2) - gval(1) - gval(1) + gval0;
+	fa *= (xd - (1<<15)) >> 1;
+	fa >>= 15;
+	fa += gval(1) - gval0;
+	fa *= xd;
+	fa >>= 15;
+	fa += gval0;
+	return fa;
+}
+
+static int MV_cubic8(const unsigned char *src, int position, int rate)
+{
+	int temp, hpos = position >> 16;
+
+	if (abs(hpos - *MV_GLast) > 3) *MV_GLast = hpos;
+
+	temp = hpos;
+
+	while (hpos > *MV_GLast)
+	{
+		gval0 = ((int)src[temp++] - 0x80) << 8;
+		*MV_GPos = (*MV_GPos + 1) & 3;
+		(*MV_GLast)++;
+	}
+
+	return do_cubic ? (MV_cubic(position) >> 8) + 0x80 : (gval(3) >> 8) + 0x80;
+}
+
+static int MV_cubic16(const short *src, int position, int rate)
+{
+	int temp, hpos = position >> 16;
+
+	if (abs(hpos - *MV_GLast) > 3) *MV_GLast = hpos;
+
+	temp = hpos;
+
+	while (hpos > *MV_GLast)
+	{
+		gval0 = src[temp++];
+		*MV_GPos = (*MV_GPos + 1) & 3;
+		(*MV_GLast)++;
+	}
+
+	return do_cubic ? MV_cubic(position) : gval(3);
+}
+
+static int MV_cubic8to16(const unsigned char *src, int position, int rate)
+{
+	int temp, hpos = position >> 16;
+
+	if (abs(hpos - *MV_GLast) > 3) *MV_GLast = hpos;
+
+	temp = hpos;
+
+	while (hpos > *MV_GLast)
+	{
+		gval0 = ((int)src[temp++] - 0x80) << 8;
+		*MV_GPos = (*MV_GPos + 1) & 3;
+		(*MV_GLast)++;
+	}
+
+	return do_cubic ? MV_cubic(position) : gval(3);
+}
+
+static int MV_cubic16to8(const short *src, int position, int rate)
+{
+	int temp, hpos = position >> 16;
+
+	if (abs(hpos - *MV_GLast) > 3) *MV_GLast = hpos;
+
+	temp = hpos;
+
+	while (hpos > *MV_GLast)
+	{
+		gval0 = src[temp++];
+		*MV_GPos = (*MV_GPos + 1) & 3;
+		(*MV_GLast)++;
+	}
+
+	return do_cubic ? (MV_cubic(position) >> 8) + 0x80 : (gval(3) >> 8) + 0x80;
+}
+
+void MV_Mix8BitMono( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	unsigned char *dest;
+	unsigned int i;
+
+	src = (const unsigned char *)start;
+	dest = (unsigned char *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		int d = (*dest - 0x80) << 8;
+		
+		d += (s * MV_LeftVolume) / MV_MaxVolume;
+
+		if (d < -32768) *dest = 0;
+		else if (d > 32767) *dest = 255;
+		else *dest = (d >> 8) + 128;
+		
+		position += rate;
+		dest += MV_SampleSize;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix8BitStereo( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	unsigned char *dest;
+	unsigned int i;
+	
+	src = (const unsigned char *)start;
+	dest = (unsigned char *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		int dl = (dest[0] - 0x80) << 8;
+		int dr = (dest[MV_RightChannelOffset] - 0x80) << 8;
+		
+		dl += (MV_LeftVolume * s) / MV_MaxVolume;
+		dr += (MV_RightVolume * s) / MV_MaxVolume;
+
+		if (dl < -32768) dest[0] = 0;
+		else if (dl > 32767) dest[0] = 255;
+		else dest[0] = (dl >> 8) + 128;
+
+		if (dr < -32768) dest[MV_RightChannelOffset] = 0;
+		else if (dr > 32767) dest[MV_RightChannelOffset] = 255;
+		else dest[MV_RightChannelOffset] = (dl >> 8) + 128;
+		
+		position += rate;
+		dest += MV_SampleSize;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix16BitMono( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	short *dest;
+	unsigned int i;
+
+	src = (const unsigned char *)start;
+	dest = (short *)MV_MixDestination;
+	
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		int d = dest[0];
+
+		d += (MV_LeftVolume * s) / MV_MaxVolume;
+
+		if (d < -32768) *dest = -32768;
+		else if (d >  32767) *dest =  32767;
+		else *dest = d;
+		
+		position += rate;
+		dest += MV_SampleSize/2;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix16BitStereo( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	short *dest;
+	unsigned int i;
+
+	src = (unsigned char *)start;
+	dest = (short *)MV_MixDestination;
+	
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		int dl = dest[0];
+		int dr = dest[MV_RightChannelOffset/2];
+		
+		dl += (MV_LeftVolume * s) / MV_MaxVolume;
+		dr += (MV_RightVolume * s) / MV_MaxVolume;
+		
+		if (dl < -32768) dest[0] = -32768;
+		else if (dl >  32767) dest[0] =  32767;
+		else dest[0] = dl;
+
+		if (dr < -32768) dest[MV_RightChannelOffset/2] = -32768;
+		else if (dr >  32767) dest[MV_RightChannelOffset/2] =  32767;
+		else dest[MV_RightChannelOffset/2] = dr;
+		
+		position += rate;
+		dest += MV_SampleSize/2;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix8BitMono16( unsigned long position, unsigned long rate,
+   const char *start, unsigned long length )
+{
+	const short *src;
+	unsigned char *dest;
+	unsigned int i;
+
+	src = (const short *)start;
+	dest = (unsigned char *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		int d = (*dest - 0x80) << 8;
+
+		d += (MV_LeftVolume * s) / MV_MaxVolume;
+
+		if (d < -32768) *dest = 0;
+		else if (d > 32767) *dest = 255;
+		else *dest = (d >> 8) + 128;
+		
+		position += rate;
+		dest += MV_SampleSize;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix8BitStereo16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const short *src;
+	unsigned char *dest;
+	unsigned int i;
+	
+	src = (const short *)start;
+	dest = (unsigned char *)MV_MixDestination;
+	
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		int dl = (dest[0] - 0x80) << 8;
+		int dr = (dest[MV_RightChannelOffset/2] - 0x80) << 8;
+		
+		dl += (MV_LeftVolume * s) / MV_MaxVolume;
+		dr += (MV_RightVolume * s) / MV_MaxVolume;
+
+		if (dl < -32768) dest[0] = 0;
+		else if (dl > 32767) dest[0] = 255;
+		else dest[0] = (dl >> 8) + 128;
+
+		if (dr < -32768) dest[MV_RightChannelOffset] = 0;
+		else if (dr > 32767) dest[MV_RightChannelOffset] = 255;
+		else dest[MV_RightChannelOffset] = (dl >> 8) + 128;
+		
+		position += rate;
+		dest += MV_SampleSize;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix16BitMono16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const short *src;
+	short *dest;
+	unsigned int i;
+	
+	src = (const short *)start;
+	dest = (short *)MV_MixDestination;
+	
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		int d = *dest;
+		
+		d += (MV_LeftVolume * s) / MV_MaxVolume;
+		
+		if (d < -32768) *dest = -32768;
+		else if (d >  32767) *dest =  32767;
+		else *dest = d;
+		
+		position += rate;
+		dest += MV_SampleSize/2;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_Mix16BitStereo16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const short *src;
+	short *dest;
+	unsigned int i;
+
+	src = (const short *)start;
+	dest = (short *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		int dl = dest[0];
+		int dr = dest[MV_RightChannelOffset/2];
+
+		dl += (MV_LeftVolume * s) / MV_MaxVolume;
+		dr += (MV_RightVolume * s) / MV_MaxVolume;
+
+		if (dl < -32768) dest[0] = -32768;
+		else if (dl > 32767) dest[0] = 32767;
+		else dest[0] = dl;
+
+		if (dr < -32768) dest[MV_RightChannelOffset/2] = -32768;
+		else if (dr > 32767) dest[MV_RightChannelOffset/2] = 32767;
+		else dest[MV_RightChannelOffset/2] = dl;
+		
+		position += rate;
+		dest += MV_SampleSize/2;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_MixFPMono8( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	double *dest;
+	unsigned int i;
+
+	src = (const unsigned char *)start;
+	dest = (double *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		double out;
+		
+		out = (double)s * (double)MV_LeftVolume / (double)MV_MaxVolume;
+		out = out / ((double)0x8000);
+		*dest += out;
+
+		position += rate;
+		dest += MV_Channels;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_MixFPStereo8( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const unsigned char *src;
+	double *dest;
+	unsigned int i;
+	
+	src = (const unsigned char *)start;
+	dest = (double *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic8to16(src, position, rate);
+		double left, right;
+		
+		left = (double)MV_LeftVolume * (double)s / (double)MV_MaxVolume;
+		left = left / ((double)0x8000);
+		right = (double)(MV_RightVolume * s) / MV_MaxVolume;
+		right = right / ((double)0x8000);
+		dest[0] += left;
+		dest[1] += right;
+
+		position += rate;
+		dest += MV_Channels;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+
+}
+
+void MV_MixFPMono16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const short *src;
+	double *dest;
+	unsigned int i;
+
+	src = (const short *)start;
+	dest = (double *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		double out;
+		
+		out = (double)s * (double)MV_LeftVolume / (double)MV_MaxVolume;
+		out = out / ((double)0x8000);
+		*dest += out;
+
+		position += rate;
+		dest += MV_Channels;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+}
+
+void MV_MixFPStereo16( unsigned long position,
+   unsigned long rate, const char *start, unsigned long length )
+{
+	const short *src;
+	double *dest;
+	unsigned int i;
+	
+	src = (const short *)start;
+	dest = (double *)MV_MixDestination;
+
+	for (i = 0; i < length; i++) {
+		int s = MV_cubic16(src, position, rate);
+		double left, right;
+		
+		left = (double)MV_LeftVolume * (double)s / (double)MV_MaxVolume;
+		left = left / ((double)0x8000);
+		right = (double)(MV_RightVolume * s) / MV_MaxVolume;
+		right = right / ((double)0x8000);
+		dest[0] += left;
+		dest[1] += right;
+
+		position += rate;
+		dest += MV_Channels;
+	}
+	
+	MV_MixPosition = position;
+	MV_MixDestination = (char *)dest;
+
+}
--- /dev/null
+++ b/Game/src/audiolib/mv_mix16.asm
@@ -1,0 +1,524 @@
+        IDEAL
+
+        p386
+        MODEL  flat
+
+        dataseg
+        CODESEG
+
+        MASM
+        ALIGN 4
+
+EXTRN   _MV_HarshClipTable:DWORD
+EXTRN   _MV_MixDestination:DWORD
+EXTRN   _MV_MixPosition:DWORD
+EXTRN   _MV_LeftVolume:DWORD
+EXTRN   _MV_RightVolume:DWORD
+EXTRN   _MV_SampleSize:DWORD
+EXTRN   _MV_RightChannelOffset:DWORD
+
+;================
+;
+; MV_Mix8BitMono16
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix8BitMono16_
+PUBLIC  MV_Mix8BitMono16_
+; Two at once
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+        inc     esi
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET apatch7+2            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET apatch8+2            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET apatch9+3            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume             ; Since we're mono, use left volume
+        mov     eax,OFFSET apatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET apatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Harsh Clip table ptr
+        mov     ebx, _MV_HarshClipTable
+        add     ebx, 128
+        mov     eax,OFFSET apatch3+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET apatch4+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET apatch5+2            ; convice tasm to modify code...
+        mov     [eax],edx
+        mov     eax,OFFSET apatch6+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        shr     ecx, 1                          ; double sample count
+        cmp     ecx, 0
+        je      exit8m
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; apatch1 - volume table
+; apatch2 - volume table
+; apatch3 - harsh clip table
+; apatch4 - harsh clip table
+; apatch5 - sample rate
+; apatch6 - sample rate
+
+        mov     eax,ebp                         ; begin calculating first sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     eax,16                          ; finish calculation for first sample
+
+        mov     ebx,ebp                         ; begin calculating second sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     ebx,16                          ; finish calculation for second sample
+
+        movsx   eax, byte ptr [esi+2*eax]       ; get first sample
+        movsx   ebx, byte ptr [esi+2*ebx]       ; get second sample
+        add     eax, 80h
+        add     ebx, 80h
+
+        ALIGN 4
+mix8Mloop:
+        movzx   edx, byte ptr [edi]             ; get current sample from destination
+apatch1:
+        movsx   eax, byte ptr [2*eax+12345678h] ; volume translate first sample
+apatch2:
+        movsx   ebx, byte ptr [2*ebx+12345678h] ; volume translate second sample
+        add     eax, edx                        ; mix first sample
+apatch9:
+        movzx   edx, byte ptr [edi + 1]         ; get current sample from destination
+apatch3:
+        mov     eax, [eax + 12345678h]          ; harsh clip new sample
+        add     ebx, edx                        ; mix second sample
+        mov     [edi], al                       ; write new sample to destination
+        mov     edx, ebp                        ; begin calculating third sample
+apatch4:
+        mov     ebx, [ebx + 12345678h]          ; harsh clip new sample
+apatch5:
+        add     ebp,12345678h                   ; advance frac pointer
+        shr     edx, 16                         ; finish calculation for third sample
+        mov     eax, ebp                        ; begin calculating fourth sample
+apatch7:
+        add     edi, 2                          ; move destination to second sample
+        shr     eax, 16                         ; finish calculation for fourth sample
+        mov     [edi], bl                       ; write new sample to destination
+apatch6:
+        add     ebp,12345678h                   ; advance frac pointer
+        movsx   ebx, byte ptr [esi+2*eax]         ; get fourth sample
+        movsx   eax, byte ptr [esi+2*edx]         ; get third sample
+        add     ebx, 80h
+        add     eax, 80h
+apatch8:
+        add     edi, 2                          ; move destination to third sample
+        dec     ecx                             ; decrement count
+        jnz     mix8Mloop                       ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+exit8m:
+        popad
+        ret
+ENDP    MV_Mix8BitMono16_
+
+;================
+;
+; MV_Mix8BitStereo16
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix8BitStereo16_
+PUBLIC  MV_Mix8BitStereo16_
+
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+        inc     esi
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET bpatch8+2            ; convice tasm to modify code...
+        mov     [eax],bl
+ ;       mov     eax,OFFSET bpatch9+2            ; convice tasm to modify code...
+ ;       mov     [eax],bl
+
+        ; Right channel offset
+        mov     ebx, _MV_RightChannelOffset
+        mov     eax,OFFSET bpatch6+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET bpatch7+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET bpatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        mov     ebx, _MV_RightVolume
+        mov     eax,OFFSET bpatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET bpatch3+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        ; Harsh Clip table ptr
+        mov     ebx, _MV_HarshClipTable
+        add     ebx,128
+        mov     eax,OFFSET bpatch4+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET bpatch5+2            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        cmp     ecx, 0
+        je      short exit8S
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; bpatch1 - left volume table
+; bpatch2 - right volume table
+; bpatch3 - sample rate
+; bpatch4 - harsh clip table
+; bpatch5 - harsh clip table
+
+        mov     eax,ebp                         ; begin calculating first sample
+        shr     eax,16                          ; finish calculation for first sample
+
+        movsx   ebx, byte ptr [esi+2*eax]       ; get first sample
+        add     ebx, 80h
+
+        ALIGN 4
+mix8Sloop:
+bpatch1:
+        movsx   eax, byte ptr [2*ebx+12345678h] ; volume translate left sample
+        movzx   edx, byte ptr [edi]             ; get current sample from destination
+bpatch2:
+        movsx   ebx, byte ptr [2*ebx+12345678h] ; volume translate right sample
+        add     eax, edx                        ; mix left sample
+bpatch3:
+        add     ebp,12345678h                   ; advance frac pointer
+bpatch6:
+        movzx   edx, byte ptr [edi+12345678h]   ; get current sample from destination
+bpatch4:
+        mov     eax, [eax + 12345678h]          ; harsh clip left sample
+        add     ebx, edx                        ; mix right sample
+        mov     [edi], al                       ; write left sample to destination
+bpatch5:
+        mov     ebx, [ebx + 12345678h]          ; harsh clip right sample
+        mov     edx, ebp                        ; begin calculating second sample
+bpatch7:
+        mov     [edi+12345678h], bl             ; write right sample to destination
+        shr     edx, 16                         ; finish calculation for second sample
+bpatch8:
+        add     edi, 1                          ; move destination to second sample
+        movsx   ebx, byte ptr [esi+2*edx]       ; get second sample
+        add     ebx, 80h
+        dec     ecx                             ; decrement count
+        jnz     mix8Sloop                       ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+
+EXIT8S:
+        popad
+        ret
+ENDP    MV_Mix8BitStereo16_
+
+;================
+;
+; MV_Mix16BitMono16
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix16BitMono16_
+PUBLIC  MV_Mix16BitMono16_
+
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET cpatch4+2            ; convice tasm to modify code...
+        mov     [eax],bl
+        mov     eax,OFFSET cpatch5+3            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET cpatch2+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET cpatch1+4            ; convice tasm to modify code...
+        inc     ebx
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET cpatch3+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        cmp     ecx, 0
+        je exit16M
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     ecx - count
+;     edi - destination
+;     esi - source
+;     ebp - frac pointer
+; cpatch1 - volume table
+; cpatch2 - volume table
+; cpatch3 - sample rate
+; cpatch4 - sample rate
+
+        mov     ebx,ebp                         ; begin calculating first sample
+        add     ebp,edx                         ; advance frac pointer
+        shr     ebx,16                          ; finish calculation for first sample
+        movzx   eax, word ptr [esi+2*ebx]       ; get low byte of sample
+        xor     eax, 8000h
+        movzx   ebx, ah
+        sub     ah, ah
+
+        movsx   edx, word ptr [edi]             ; get current sample from destination
+
+        ALIGN 4
+mix16Mloop:
+cpatch1:
+        movsx   eax, byte ptr [2*eax+12345678h] ; volume translate low byte of sample
+cpatch2:
+        movsx   ebx, word ptr [2*ebx+12345678h] ; volume translate high byte of sample
+        lea     eax, [ eax + ebx + 80h ]        ; mix high byte of sample
+        add     eax, edx                        ; mix low byte of sample
+cpatch5:
+        movsx   edx, word ptr [edi + 2]         ; get current sample from destination
+
+        cmp     eax, -32768                     ; Harsh clip sample
+        jge     short m16skip1
+        mov     eax, -32768
+        jmp     short m16skip2
+m16skip1:
+        cmp     eax, 32767
+        jle     short m16skip2
+        mov     eax, 32767
+m16skip2:
+        mov     ebx, ebp                        ; begin calculating second sample
+        mov     [edi], ax                       ; write new sample to destination
+
+        shr     ebx, 16                         ; finish calculation for second sample
+cpatch3:
+        add     ebp, 12345678h                  ; advance frac pointer
+
+        movzx   eax, word ptr [esi+2*ebx]       ; get second sample
+cpatch4:
+        add     edi, 2                          ; move destination to second sample
+        xor     eax, 8000h
+        movzx   ebx, ah
+        sub     ah, ah
+
+        dec     ecx                             ; decrement count
+        jnz     mix16Mloop                      ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+EXIT16M:
+        popad
+        ret
+ENDP    MV_Mix16BitMono16_
+
+;================
+;
+; MV_Mix16BitStereo16
+;
+;================
+
+; eax - position
+; edx - rate
+; ebx - start
+; ecx - number of samples to mix
+
+PROC    MV_Mix16BitStereo16_
+PUBLIC  MV_Mix16BitStereo16_
+
+        pushad
+        mov     ebp, eax
+
+        mov     esi, ebx                        ; Source pointer
+
+        ; Sample size
+        mov     ebx, _MV_SampleSize
+        mov     eax,OFFSET dpatch9+2            ; convice tasm to modify code...
+        mov     [eax],bl
+
+        ; Right channel offset
+        mov     ebx, _MV_RightChannelOffset
+        mov     eax,OFFSET dpatch7+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET dpatch8+3            ; convice tasm to modify code...
+        mov     [eax],ebx
+
+        ; Volume table ptr
+        mov     ebx, _MV_LeftVolume
+        mov     eax,OFFSET dpatch1+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET dpatch2+4            ; convice tasm to modify code...
+        inc     ebx
+        mov     [eax],ebx
+
+        mov     ebx, _MV_RightVolume
+        mov     eax,OFFSET dpatch3+4            ; convice tasm to modify code...
+        mov     [eax],ebx
+        mov     eax,OFFSET dpatch4+4            ; convice tasm to modify code...
+        inc     ebx
+        mov     [eax],ebx
+
+        ; Rate scale ptr
+        mov     eax,OFFSET dpatch5+2            ; convice tasm to modify code...
+        mov     [eax],edx
+
+        ; Source ptr
+        mov     eax,OFFSET dpatch6+4            ; convice tasm to modify code...
+        mov     [eax],esi
+
+        mov     edi, _MV_MixDestination         ; Get the position to write to
+
+        ; Number of samples to mix
+        cmp     ecx, 0
+        je      exit16S
+
+;     eax - scratch
+;     ebx - scratch
+;     edx - scratch
+;     esi - scratch
+;     ecx - count
+;     edi - destination
+;     ebp - frac pointer
+; dpatch1 - left volume table
+; dpatch2 - right volume table
+; dpatch3 - sample rate
+
+        mov     ebx,ebp                         ; begin calculating first sample
+        shr     ebx,16                          ; finish calculation for first sample
+
+        movzx   edx, word ptr [esi+2*ebx]       ; get first sample
+        xor     edx, 8000h                      ; Change from signed to unsigned
+        movzx   esi, dh                         ; put high byte in esi
+        sub     dh, dh                          ; lo byte in edx
+
+        ALIGN 4
+mix16Sloop:
+        ; Left channel
+dpatch1:
+        movsx   eax, word ptr [2*esi+12345678h] ; volume translate high byte of sample
+dpatch2:
+        movsx   ebx, byte ptr [2*edx+12345678h] ; volume translate low byte of sample
+        lea     eax, [ eax + ebx + 80h ]        ; mix high byte of sample
+
+        ; Right channel
+dpatch3:
+        movsx   esi, word ptr [2*esi+12345678h] ; volume translate high byte of sample
+dpatch4:
+        movsx   ebx, byte ptr [2*edx+12345678h] ; volume translate low byte of sample
+        lea     ebx, [ esi + ebx + 80h ]        ; mix high byte of sample
+
+dpatch7:
+        movsx   edx, word ptr [edi+12345678h]   ; get current sample from destination
+dpatch5:
+        add     ebp,12345678h                   ; advance frac pointer
+
+        add     eax, edx                        ; mix left sample
+
+        cmp     eax, -32768                     ; Harsh clip sample
+        jge     short s16skip1
+        mov     eax, -32768
+        jmp     short s16skip2
+s16skip1:
+        cmp     eax, 32767
+        jle     short s16skip2
+        mov     eax, 32767
+s16skip2:
+        movsx   edx, word ptr [edi+2]           ; get current sample from destination
+        mov     [edi], ax                       ; write left sample to destination
+        add     ebx, edx                        ; mix right sample
+
+        cmp     ebx, -32768                     ; Harsh clip sample
+        jge     short s16skip3
+        mov     ebx, -32768
+        jmp     short s16skip4
+s16skip3:
+        cmp     ebx, 32767
+        jle     short s16skip4
+        mov     ebx, 32767
+s16skip4:
+
+        mov     edx, ebp                        ; begin calculating second sample
+dpatch8:
+        mov     [edi+12345678h], bx             ; write right sample to destination
+        shr     edx, 16                         ; finish calculation for second sample
+dpatch9:
+        add     edi, 4                          ; move destination to second sample
+
+dpatch6:
+        movzx   edx, word ptr [2*edx+12345678h] ; get second sample
+        xor     edx, 8000h                      ; Change from signed to unsigned
+        movzx   esi, dh                         ; put high byte in esi
+        sub     dh, dh                          ; lo byte in edx
+
+        dec     ecx                             ; decrement count
+        jnz     mix16Sloop                      ; loop
+
+        mov     _MV_MixDestination, edi         ; Store the current write position
+        mov     _MV_MixPosition, ebp            ; return position
+exit16S:
+        popad
+        ret
+ENDP    MV_Mix16BitStereo16_
+
+        ENDS
+
+        END
--- /dev/null
+++ b/Game/src/audiolib/mvreverb.asm
@@ -1,0 +1,181 @@
+        IDEAL
+
+        p386
+        MODEL  flat
+
+        dataseg
+        CODESEG
+
+        MASM
+        ALIGN 4
+
+;================
+;
+; MV_16BitReverb
+;
+;================
+
+; eax - source position
+; edx - destination position
+; ebx - Volume table
+; ecx - number of samples
+
+PROC    MV_16BitReverb_
+PUBLIC  MV_16BitReverb_
+
+        mov     esi, eax
+        lea     edi, [edx - 2]
+
+        ALIGN 4
+rev16loop:
+        movzx   eax, word ptr [esi]             ; get sample
+        add     edi, 2
+
+        movzx   edx, ah
+        sub     ah, ah
+
+        movsx   eax, byte ptr [2*eax+ebx+1]     ; volume translate low byte of sample
+        xor     edx, 80h
+
+        movsx   edx, word ptr [2*edx+ebx]       ; volume translate high byte of sample
+        add     esi, 2
+
+        lea     eax, [ eax + edx + 80h ]        ; mix high byte of sample
+        dec     ecx                             ; decrement count
+
+        mov     [edi], ax                       ; write new sample to destination
+        jnz     rev16loop                       ; loop
+
+        ret
+ENDP    MV_16BitReverb_
+
+;================
+;
+; MV_8BitReverb
+;
+;================
+
+; eax - source position
+; edx - destination position
+; ebx - Volume table
+; ecx - number of samples
+
+PROC    MV_8BitReverb_
+PUBLIC  MV_8BitReverb_
+
+        mov     esi, eax
+        lea     edi, [edx - 1]
+
+        xor     eax, eax
+
+        ALIGN 4
+rev8loop:
+;        movzx   eax, byte ptr [esi]             ; get sample
+        mov     al, byte ptr [esi]              ; get sample
+        inc     edi
+
+;        movsx   eax, byte ptr [2*eax+ebx]       ; volume translate sample
+        mov     al, byte ptr [2*eax+ebx]        ; volume translate sample
+        inc     esi
+
+;        add     eax, 80h
+        add     al, 80h
+        dec     ecx                             ; decrement count
+
+        mov     [edi], al                       ; write new sample to destination
+        jnz     rev8loop                        ; loop
+
+        ret
+ENDP    MV_8BitReverb_
+
+;================
+;
+; MV_16BitReverbFast
+;
+;================
+
+; eax - source position
+; edx - destination position
+; ebx - number of samples
+; ecx - shift
+
+PROC    MV_16BitReverbFast_
+PUBLIC  MV_16BitReverbFast_
+
+        mov     esi, eax
+        mov     eax,OFFSET rpatch16+3
+
+        mov     [eax],cl
+        lea     edi, [edx - 2]
+
+        ALIGN 4
+frev16loop:
+        mov     ax, word ptr [esi]             ; get sample
+        add     edi, 2
+
+rpatch16:
+        sar     ax, 5    ;;;;Add 1 before shift
+        add     esi, 2
+
+        mov     [edi], ax                       ; write new sample to destination
+        dec     ebx                             ; decrement count
+
+        jnz     frev16loop                      ; loop
+
+        ret
+ENDP    MV_16BITREVERBFAST_
+
+;================
+;
+; MV_8BitReverbFast
+;
+;================
+
+; eax - source position
+; edx - destination position
+; ebx - number of samples
+; ecx - shift
+
+PROC    MV_8BitReverbFast_
+PUBLIC  MV_8BitReverbFast_
+        mov     esi, eax
+        mov     eax,OFFSET rpatch8+2
+
+        mov     edi, edx
+        mov     edx, 80h
+
+        mov     [eax],cl
+        mov     eax, 80h
+
+        shr     eax, cl
+
+        dec     edi
+        sub     edx, eax
+
+        ALIGN 4
+frev8loop:
+        mov     al, byte ptr [esi]             ; get sample
+        inc     esi
+
+        mov     ecx, eax
+        inc     edi
+
+rpatch8:
+        shr     eax, 3
+        xor     ecx, 80h                        ; flip the sign bit
+
+        shr     ecx, 7                          ; shift the sign down to 1
+        add     eax, edx
+
+        add     eax, ecx                        ; add sign bit to round to 0
+        dec     ebx                             ; decrement count
+
+        mov     [edi], al                       ; write new sample to destination
+        jnz     frev8loop                       ; loop
+
+        ret
+ENDP    MV_8BITREVERBFAST_
+
+        ENDS
+
+        END
--- /dev/null
+++ b/Game/src/audiolib/mvreverb.c
@@ -1,0 +1,312 @@
+#include "multivoc.h"
+#include "_multivc.h"
+
+extern double *MV_FooBuffer;
+extern int MV_BufferSize;
+extern int MV_SampleSize;
+extern int MV_MaxVolume;
+extern int MV_ReverbDelay;
+extern int MV_MixRate;
+extern int MV_Channels;
+
+static double * reverbBuffer = 0;
+static int delay = 0, CurrAddr;
+
+static	int FB_SRC_A, FB_SRC_B, IIR_DEST_A0, IIR_DEST_A1, ACC_SRC_A0, ACC_SRC_A1, ACC_SRC_B0,
+		ACC_SRC_B1, IIR_SRC_A0, IIR_SRC_A1, IIR_DEST_B0, IIR_DEST_B1, ACC_SRC_C0,
+		ACC_SRC_C1, ACC_SRC_D0, ACC_SRC_D1, IIR_SRC_B1, IIR_SRC_B0, MIX_DEST_A0,
+		MIX_DEST_A1, MIX_DEST_B0, MIX_DEST_B1;
+	
+static	double IIR_ALPHA, ACC_COEF_A, ACC_COEF_B, ACC_COEF_C, ACC_COEF_D, IIR_COEF, FB_ALPHA, FB_X,
+		IN_COEF_L, IN_COEF_R;
+
+static  double iRVBLeft, iRVBRight;
+
+static int cnv_offset(int src)
+{
+	__int64 temp = ((__int64)src * (__int64)MV_MixRate) / 22050;
+	return (int)temp;
+}
+
+// static char err[256];
+
+// extern __stdcall OutputDebugStringA(char *);
+
+static void check_buffer()
+{
+	int new_delay = cnv_offset(MV_ReverbDelay);
+
+	if (!delay || new_delay != delay)
+	{
+//		sprintf(err + strlen(err), ", new_delay: %d, MV_ReverbDelay: %d", new_delay, MV_ReverbDelay);
+		FB_SRC_A = cnv_offset(0xE3);
+		FB_SRC_B = cnv_offset(0xA9);
+		IIR_DEST_A0 = cnv_offset(0xDFB);
+		IIR_DEST_A1 = cnv_offset(0xB58);
+		ACC_SRC_A0 = cnv_offset(0xD09);
+		ACC_SRC_A1 = cnv_offset(0xA3C);
+		ACC_SRC_B0 = cnv_offset(0xBD9);
+		ACC_SRC_B1 = cnv_offset(0x973);
+		IIR_SRC_A0 = cnv_offset(0xB59);
+		IIR_SRC_A1 = cnv_offset(0x8DA);
+		IIR_DEST_B0 = cnv_offset(0x8D9);
+		IIR_DEST_B1 = cnv_offset(0x5E9);
+		ACC_SRC_C0 = cnv_offset(0x7EC);
+		ACC_SRC_C1 = cnv_offset(0x4B0);
+		ACC_SRC_D0 = cnv_offset(0x6EF);
+		ACC_SRC_D1 = cnv_offset(0x3D2);
+		IIR_SRC_B1 = cnv_offset(0x5EA);
+		IIR_SRC_B0 = cnv_offset(0x31D);
+		MIX_DEST_A0 = cnv_offset(0x31C);
+		MIX_DEST_A1 = cnv_offset(0x238);
+		MIX_DEST_B0 = cnv_offset(0x154);
+		MIX_DEST_B1 = cnv_offset(0xAA);
+		IIR_ALPHA = 0.8701171875;
+		ACC_COEF_A = 0.622314453125;
+		ACC_COEF_B = -0.5244140625;
+		ACC_COEF_C = 0.53955078125;
+		ACC_COEF_D = -0.50830078125;
+		IIR_COEF = -0.69921875;
+		FB_ALPHA = 0.67578125;
+		FB_X = 0.646484375;
+		IN_COEF_L = -2.;
+		IN_COEF_R = -2.;
+		if (reverbBuffer) reverbBuffer = (double*) realloc(reverbBuffer, new_delay * sizeof(double));
+		else reverbBuffer = (double*) malloc(new_delay * sizeof(double));
+		memset(reverbBuffer, 0, new_delay * sizeof(double));
+		delay = new_delay;
+		CurrAddr = 0;
+	}
+
+}
+
+__inline double g_buffer(int iOff, double *ptr)                          // get_buffer content helper: takes care about wraps
+{
+	int correctDelay = delay;
+	if(!correctDelay)
+	{
+		printf("Error! Reverb race on g_buffer\n");
+		correctDelay = cnv_offset(14320);
+	}
+
+	iOff=(iOff*4)+CurrAddr;
+	while(iOff>correctDelay-1)	
+	{
+		iOff=iOff-correctDelay;
+	}
+	while(iOff<0)
+	{
+		iOff=correctDelay-(0-iOff);
+	}
+	return (double)*(ptr+iOff);
+}
+
+__inline void s_buffer(int iOff,double iVal, double *ptr)                // set_buffer content helper: takes care about wraps and clipping
+{
+	int correctDelay = delay;
+	if(!correctDelay)
+	{
+		printf("Error! Reverb race on s_buffer\n");
+		correctDelay = cnv_offset(14320);
+	}
+
+	iOff=(iOff*4)+CurrAddr;
+	while(iOff>correctDelay-1)
+	{
+		iOff=iOff-correctDelay;
+	}
+	while(iOff<0)
+	{
+		iOff=correctDelay-(0-iOff);
+	}
+	*(ptr+iOff)=iVal;
+}
+
+__inline void s_buffer1(int iOff,double iVal, double *ptr)                // set_buffer (+1 sample) content helper: takes care about wraps and clipping
+{
+	int correctDelay = delay;
+	if(!correctDelay)
+	{
+		printf("Error! Reverb race on s_buffer1\n");
+		correctDelay = cnv_offset(14320);
+	}
+
+	iOff=(iOff*4)+CurrAddr+1;
+	while(iOff>correctDelay-1)	
+	{
+		iOff=iOff-correctDelay;
+	}
+	while(iOff<0)			
+	{
+		iOff=correctDelay-(0-iOff);
+	}
+	*(ptr+iOff)=iVal;
+}
+
+__inline double MixREVERBLeft(double INPUT_SAMPLE_L, double INPUT_SAMPLE_R, double *ptr)
+{
+	double ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
+	
+	const double IIR_INPUT_A0 = (g_buffer(IIR_SRC_A0, ptr) * IIR_COEF) + (INPUT_SAMPLE_L * IN_COEF_L);
+	const double IIR_INPUT_A1 = (g_buffer(IIR_SRC_A1, ptr) * IIR_COEF) + (INPUT_SAMPLE_R * IN_COEF_R);
+	const double IIR_INPUT_B0 = (g_buffer(IIR_SRC_B0, ptr) * IIR_COEF) + (INPUT_SAMPLE_L * IN_COEF_L);
+	const double IIR_INPUT_B1 = (g_buffer(IIR_SRC_B1, ptr) * IIR_COEF) + (INPUT_SAMPLE_R * IN_COEF_R);
+	
+	const double IIR_A0 = (IIR_INPUT_A0 * IIR_ALPHA) + (g_buffer(IIR_DEST_A0, ptr) * (1.f - IIR_ALPHA));
+	const double IIR_A1 = (IIR_INPUT_A1 * IIR_ALPHA) + (g_buffer(IIR_DEST_A1, ptr) * (1.f - IIR_ALPHA));
+	const double IIR_B0 = (IIR_INPUT_B0 * IIR_ALPHA) + (g_buffer(IIR_DEST_B0, ptr) * (1.f - IIR_ALPHA));
+	const double IIR_B1 = (IIR_INPUT_B1 * IIR_ALPHA) + (g_buffer(IIR_DEST_B1, ptr) * (1.f - IIR_ALPHA));
+	
+	s_buffer1(IIR_DEST_A0, IIR_A0, ptr);
+	s_buffer1(IIR_DEST_A1, IIR_A1, ptr);
+	s_buffer1(IIR_DEST_B0, IIR_B0, ptr);
+	s_buffer1(IIR_DEST_B1, IIR_B1, ptr);
+	
+	ACC0 = (g_buffer(ACC_SRC_A0, ptr) * ACC_COEF_A) +
+		(g_buffer(ACC_SRC_B0, ptr) * ACC_COEF_B) +
+		(g_buffer(ACC_SRC_C0, ptr) * ACC_COEF_C) +
+		(g_buffer(ACC_SRC_D0, ptr) * ACC_COEF_D);
+	ACC1 = (g_buffer(ACC_SRC_A1, ptr) * ACC_COEF_A) +
+		(g_buffer(ACC_SRC_B1, ptr) * ACC_COEF_B) +
+		(g_buffer(ACC_SRC_C1, ptr) * ACC_COEF_C) +
+		(g_buffer(ACC_SRC_D1, ptr) * ACC_COEF_D);
+	
+	FB_A0 = g_buffer(MIX_DEST_A0 - FB_SRC_A, ptr);
+	FB_A1 = g_buffer(MIX_DEST_A1 - FB_SRC_A, ptr);
+	FB_B0 = g_buffer(MIX_DEST_B0 - FB_SRC_B, ptr);
+	FB_B1 = g_buffer(MIX_DEST_B1 - FB_SRC_B, ptr);
+	
+	s_buffer(MIX_DEST_A0, ACC0 - (FB_A0 * FB_ALPHA), ptr);
+	s_buffer(MIX_DEST_A1, ACC1 - (FB_A1 * FB_ALPHA), ptr);
+	
+	s_buffer(MIX_DEST_B0, (FB_ALPHA * ACC0) - (FB_A0 * (FB_ALPHA - 1.f)) - (FB_B0 * FB_X), ptr);
+	s_buffer(MIX_DEST_B1, (FB_ALPHA * ACC1) - (FB_A1 * (FB_ALPHA - 1.f)) - (FB_B1 * FB_X), ptr);
+	
+	iRVBLeft  = (g_buffer(MIX_DEST_A0, ptr)+g_buffer(MIX_DEST_B0, ptr))/3.f;
+	iRVBRight = (g_buffer(MIX_DEST_A1, ptr)+g_buffer(MIX_DEST_B1, ptr))/3.f;
+	
+	CurrAddr++;
+	if(CurrAddr>delay-1) CurrAddr=0;
+	
+	return (double)iRVBLeft;
+}
+
+__inline double MixREVERBRight(void)
+{
+	return (double)iRVBRight;
+}
+
+void MV_FPReverb(int volume)
+{
+	int i, count = MV_BufferSize / MV_SampleSize * MV_Channels;
+
+//	sprintf(err, "count: %d, old_delay: %d", count, delay);
+	//EnterCriticalSection(&reverbCS);
+	SDL_mutexP(reverbMutex);
+
+	check_buffer();
+
+	// DAVE
+	if(delay == 0)
+	{
+		//get out now!!!
+		printf("Error! MV_FPReverb() delay==0\n");
+		return;
+	}
+
+//	OutputDebugStringA(err);
+	
+	if (MV_Channels == 1)
+	{
+		for (i = 0; i < count; i++)
+		{
+			double temp = MV_FooBuffer[i];
+			MV_FooBuffer[i] += ((MixREVERBLeft(temp, temp, reverbBuffer) + MixREVERBRight()) * .5) * (double)volume / (double)MV_MaxVolume;
+		}
+	}
+	else
+	{
+		count >>= 1;
+		for (i = 0; i < count; i++)
+		{
+			double left = MV_FooBuffer[i*2];
+			double right = MV_FooBuffer[i*2+1];
+			double scale = (double)volume / (double)MV_MaxVolume;
+			left += MixREVERBLeft(left, right, reverbBuffer) * scale;
+			right += MixREVERBRight() * scale;
+			MV_FooBuffer[i*2] = left;
+			MV_FooBuffer[i*2+1] = right;
+		}
+	}
+
+	//LeaveCriticalSection(&reverbCS);
+	SDL_mutexV(reverbMutex);
+}
+
+void MV_FPReverbFree(void)
+{
+	SDL_mutexP(reverbMutex);
+	//EnterCriticalSection(&reverbCS);
+	delay = 0;
+	if (reverbBuffer)
+	{
+		free(reverbBuffer);
+		reverbBuffer = 0;
+	}
+	//LeaveCriticalSection(&reverbCS);
+	SDL_mutexV(reverbMutex);
+}
+
+void MV_16BitDownmix(char *dest, int count)
+{
+	int i;
+
+	short *pdest = (short *)dest;
+
+	for (i = 0; i < count; i++)
+	{
+		int out = (int)((MV_FooBuffer[i] * (double)0x8000));
+		if (out < -32768) pdest[i] = -32768;
+		else if (out > 32767) pdest[i] = 32767;
+		else pdest[i] = out;
+	}
+}
+
+void MV_8BitDownmix(char *dest, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+	{
+		int out = ((int)((MV_FooBuffer[i] * (double)0x80)));
+		if (out < -128) dest[i] = 0;
+		else if (out > 127) dest[i] = 255;
+		else dest[i] = out + 0x80;
+	}
+}
+
+void MV_16BitReverbFast( const char *src, char *dest, int count, int shift )
+{
+	int i;
+
+	short *pdest = (short *)dest;
+	const short *psrc = (const short *)src;
+	
+	for (i = 0; i < count; i++) {
+		pdest[i] = psrc[i] >> shift;
+	}
+}
+
+void MV_8BitReverbFast( const signed char *src, signed char *dest, int count, int shift )
+{
+	int i;
+
+	unsigned char sh = 0x80 - (0x80 >> shift);
+	
+	for (i = 0; i < count; i++) {
+		unsigned char a = ((unsigned char) src[i]) >> shift;
+		unsigned char c = (((unsigned char) src[i]) ^ 0x80) >> 7;
+		
+		dest[i] = (signed char) (a + sh + c);
+	}
+}
--- /dev/null
+++ b/Game/src/audiolib/myprint.c
@@ -1,0 +1,310 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "myprint.h"
+
+static unsigned short disp_offset = 160 * 24;
+
+void DrawText
+   (
+   int x,
+   int y,
+   int ch,
+   int foreground,
+   int background
+   )
+
+   {
+   char *vid;
+
+   vid  = ( char * )( 0xb0000 );
+   vid += y * 160;
+   vid += x * 2;
+
+   if ( ch != NONE )
+      {
+      *vid = ch;
+      }
+   vid++;
+   *vid = ( ( background & 0x0f ) << 4 ) | ( foreground & 0x0f );
+   }
+
+void TextBox
+   (
+   int  x1,
+   int  y1,
+   int  x2,
+   int  y2,
+   int ch,
+   int  foreground,
+   int  background
+   )
+
+   {
+   int x;
+   int y;
+
+   for( x = x1; x <= x2; x++ )
+      {
+      for( y = y1; y <= y2; y++ )
+         {
+         DrawText( x, y, ch, foreground, background );
+         }
+      }
+   }
+
+void TextFrame
+   (
+   int x1,
+   int y1,
+   int x2,
+   int y2,
+   int type,
+   int foreground,
+   int background
+   )
+
+   {
+   int x;
+   int y;
+
+   if ( type == 0 )
+      {
+      for( x = x1 + 1; x < x2; x++ )
+         {
+         DrawText( x, y1, type, foreground, background );
+         DrawText( x, y2, type, foreground, background );
+         }
+      for( y = y1 + 1; y < y2; y++ )
+         {
+         DrawText( x1, y, type, foreground, background );
+         DrawText( x2, y, type, foreground, background );
+         }
+      }
+   if ( type == SINGLE_FRAME )
+      {
+      DrawText( x1, y1, '�', foreground, background );
+      DrawText( x2, y1, '�', foreground, background );
+      DrawText( x1, y2, '�', foreground, background );
+      DrawText( x2, y2, '�', foreground, background );
+      for( x = x1 + 1; x < x2; x++ )
+         {
+         DrawText( x, y1, '�', foreground, background );
+         DrawText( x, y2, '�', foreground, background );
+         }
+      for( y = y1 + 1; y < y2; y++ )
+         {
+         DrawText( x1, y, '�', foreground, background );
+         DrawText( x2, y, '�', foreground, background );
+         }
+      }
+   if ( type == DOUBLE_FRAME )
+      {
+      DrawText( x1, y1, '�', foreground, background );
+      DrawText( x2, y1, '�', foreground, background );
+      DrawText( x1, y2, '�', foreground, background );
+      DrawText( x2, y2, '�', foreground, background );
+      for( x = x1 + 1; x < x2; x++ )
+         {
+         DrawText( x, y1, '�', foreground, background );
+         DrawText( x, y2, '�', foreground, background );
+         }
+      for( y = y1 + 1; y < y2; y++ )
+         {
+         DrawText( x1, y, '�', foreground, background );
+         DrawText( x2, y, '�', foreground, background );
+         }
+      }
+   }
+
+void mysetxy
+   (
+   int x,
+   int y
+   )
+
+   {
+   disp_offset = ( x * 2 ) + ( y * 160 );
+   }
+
+void myputch
+   (
+   char ch
+   )
+
+   {
+   int j;
+   char *disp_start = (char *)( 0xb0000 );
+
+   if ( disp_offset >= 160 * 24 )
+      {
+      for ( j = 160; j < 160 * 24; j += 2 )
+         {
+         *( disp_start + j - 160 ) = *( disp_start + j );
+         }
+
+      disp_offset = 160 * 23;
+
+      for ( j = disp_offset; j < ( 160 * 24 ); j += 2 )
+         {
+         *( disp_start + j ) = ' ';
+         }
+      }
+
+   if ( ch >= 32 )
+      {
+      *( disp_start + disp_offset ) = ch;
+      disp_offset = disp_offset + 2;
+      }
+
+   if ( ch == '\r' )
+      {
+      disp_offset = disp_offset / 160;
+      disp_offset = disp_offset * 160;
+      }
+
+   if ( ch == '\n' )
+      {
+      disp_offset = disp_offset + 160;
+      if ( disp_offset < 160 * 24 )
+         {
+         for ( j = disp_offset; j < ( ( ( disp_offset / 160 ) + 1 ) *
+            160 ); j += 2 )
+            {
+            *( disp_start + j ) = ' ';
+            }
+         }
+      }
+   }
+
+int printstring
+   (
+   char *string
+   )
+
+   {
+   int count;
+   char *ptr;
+
+   ptr = string;
+   count = 0;
+
+   while ( *ptr )
+      {
+      myputch( *ptr );
+      count++;
+      ptr++;
+      }
+
+   return( count );
+   }
+
+
+int printnum
+   (
+   int number
+   )
+
+   {
+   char string[ 100 ];
+   int  count;
+
+   itoa( number, string, 10 );
+   count = printstring( string );
+
+   return( count );
+   }
+
+int printunsigned
+   (
+   unsigned long number,
+   int radix
+   )
+
+   {
+   char string[ 100 ];
+   int  count;
+
+   ultoa( number, string, radix );
+   count = printstring( string );
+
+   return( count );
+   }
+
+int myprintf
+   (
+   char *fmt,
+   ...
+   )
+
+   {
+   va_list argptr;
+   int     count;
+   char    *ptr;
+
+   return( 0 );
+
+   // DEBUG
+   mysetxy( 0, 0 );
+
+   va_start( argptr, fmt );
+   ptr = fmt;
+   count = 0;
+
+   while( *ptr != 0 )
+      {
+      if ( *ptr == '%' )
+         {
+         ptr++;
+         switch( *ptr )
+            {
+            case 0 :
+               return( EOF );
+               break;
+            case 'd' :
+               count += printnum( va_arg( argptr, int ) );
+               break;
+            case 's' :
+               count += printstring( va_arg( argptr, char * ) );
+               break;
+            case 'u' :
+               count += printunsigned( va_arg( argptr, int ), 10 );
+               break;
+            case 'x' :
+            case 'X' :
+               count += printunsigned( va_arg( argptr, int ), 16 );
+               break;
+            }
+         ptr++;
+         }
+      else
+         {
+         myputch( *ptr );
+         count++;
+         ptr++;
+         }
+      }
+
+   va_end( argptr );
+
+   return( count );
+   }
--- /dev/null
+++ b/Game/src/audiolib/myprint.h
@@ -1,0 +1,43 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+#ifndef __MYPRINT_H
+#define __MYPRINT_H
+
+enum COLORS
+   {
+   BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY,
+   LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE
+   };
+
+#define NONE         -1
+#define SINGLE_FRAME -1
+#define DOUBLE_FRAME -2
+
+void DrawText( int x, int y, int ch, int foreground, int background );
+void TextBox( int x1, int y1, int x2, int y2, int ch, int foreground, int background );
+void TextFrame( int x1, int y1, int x2, int y2, int type, int foreground, int background );
+void mysetxy( int x, int y );
+void myputch( char ch );
+int  printstring( char *string );
+int  printnum( int number );
+int  printunsigned( unsigned long number, int radix );
+int  myprintf( char *fmt, ... );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/newgf1.h
@@ -1,0 +1,431 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/***************************************************************************
+*	NAME:  GF1.H
+**	COPYRIGHT:
+**	"Copyright (c) 1991,1992, by FORTE
+**
+**       "This software is furnished under a license and may be used,
+**       copied, or disclosed only in accordance with the terms of such
+**       license and with the inclusion of the above copyright notice.
+**       This software or any other copies thereof may not be provided or
+**       otherwise made available to any other person. No title to and
+**       ownership of the software is hereby transfered."
+****************************************************************************
+*  CREATION DATE: 07/01/92
+*--------------------------------------------------------------------------*
+*     VERSION	DATE	   NAME		DESCRIPTION
+*>	1.0	07/01/92		Original
+***************************************************************************/
+
+#ifndef	_GF1_H		/* allow header to be processed only once */
+#define _GF1_H
+
+/* error codes */
+#define OK			0
+#define NO_MORE_VOICES		-1
+#define	BASE_NOT_FOUND		1
+#define BAD_IRQ			2
+#define BAD_DMA			3
+#define OS_LOADED		4
+#define NOT_LOADED		5
+#define NO_MEMORY		6
+#define DMA_BUSY		7
+#define NO_MORE_HANDLERS	8
+#define DMA_HUNG		9
+#define CARD_NOT_FOUND		10
+#define CARD_BEING_USED		11
+#define NO_MORE_INTERRUPTS	12
+#define BAD_TIMER		13
+#define BAD_PATCH		14
+#define OLD_PATCH		15
+#define DOS_ERROR		16
+#define FILE_NOT_FOUND		17
+
+/* bits */
+#define	BIT0	0x01
+#define	BIT1	0x02
+#define	BIT2	0x04
+#define	BIT3	0x08
+#define	BIT4	0x10
+#define	BIT5	0x20
+#define	BIT6	0x40
+#define	BIT7	0x80
+
+/* bounds for volume enveloping functions */
+#define MIN_OFFSET      5U
+#define MAX_OFFSET      251U
+
+/* bounds for voice allocation */
+#define MIN_VOICES	14
+#define MAX_VOICES	32
+
+/* DMA control bits */
+#define DMA_ENABLE		BIT0
+#define DMA_READ		BIT1
+#define DMA_WIDTH_16		BIT2 /* width of DMA channel */
+#define DMA_RATE_DIV_1		BIT3
+#define DMA_RATE_DIV_2		BIT4
+#define DMA_IRQ_ENABLE		BIT5
+#define DMA_IRQ_PRESENT		BIT6
+#define DMA_DATA_16		BIT6 /* width of data */
+#define DMA_INVERT_MSB		BIT7
+
+/* SAMPLE control bits */
+#define DMA_STEREO		2
+
+/* DMA flags */
+#define GF1_RECORD	0 /* use dma control or sample control */
+#define GF1_DMA		1
+
+/* MIDI control register */
+#define MIDI_RESET	(BIT0|BIT1)
+#define MIDI_TD_INT	BIT5
+#define MIDI_RD_INT	BIT7
+
+/* MIDI_STATUS_REGISTER */
+#define MIDI_RD		BIT0
+#define MIDI_TD		BIT1
+#define MIDI_ERR_FRAMING BIT4
+#define MIDI_ERR_OVERRUN BIT5
+
+/* digital playback flags */
+#define TYPE_8BIT	BIT0	/* 1 use 8 bit data */
+				/* 0 use 16 bit data */
+#define TYPE_PRELOAD	BIT1	/* preload data */
+#define TYPE_INVERT_MSB BIT2	/* invert most significant bit during dma */
+#define TYPE_STEREO	BIT3	/* 1 for stereo data */
+
+/* sound effects and digital music types */
+#define SND_LOOP_MASK		(BIT0|BIT1)
+#define SND_LOOP_NONE		0
+#define SND_LOOP		1
+#define SND_LOOP_BIDIR		2
+#define SND_8BIT		(BIT2)
+#define SND_BACKWARD		(BIT3)
+
+#define SOUND_PLAYING		2
+#define SOUND_ACTIVE		1
+
+/* patch macros */
+#define HEADER_SIZE	           12
+#define ID_SIZE		           10
+#define DESC_SIZE 	           60
+#define RESERVED_SIZE	           40
+#define PATCH_HEADER_RESERVED_SIZE 36
+#define LAYER_RESERVED_SIZE	   40
+#define PATCH_DATA_RESERVED_SIZE   36
+#define GF1_HEADER_TEXT            "GF1PATCH110"
+#define INST_NAME_SIZE		   16
+#define ENVELOPES		   6
+#define MAX_LAYERS		   4
+
+/* patch modes */
+#define PATCH_16		BIT0
+#define PATCH_UNSIGNED		BIT1
+#define PATCH_LOOPEN		BIT2
+#define PATCH_BIDIR		BIT3
+#define PATCH_BACKWARD  	BIT4
+#define PATCH_SUSTAIN   	BIT5
+#define PATCH_NO_SRELEASE	BIT6
+#define PATCH_FAST_REL		BIT7
+
+/* flags for patch loading */
+#define PATCH_LOAD_8_BIT BIT0
+
+/* digital playback callback reasons & return values */
+#define	DIG_DONE	   0
+#define DIG_MORE_DATA	   1
+#define DIG_BUFFER_DONE    2
+#define DIG_PAUSE          3
+
+/* log table used for vibrato and pitch bend.  log table made public for
+** developers use */
+#define LOG_TAB_SIZE 12
+extern long gf1_log_table[LOG_TAB_SIZE];
+
+#if defined(__BORLANDC__)
+#undef RFAR
+#define RFAR far
+#elif defined(_MSC_VER) && (_MSC_VER <= 600)
+#define RFAR far
+#elif defined(_MSC_VER) && (_MSC_VER > 600)
+#define RFAR __far
+#else
+#undef RFAR
+#define RFAR
+#endif
+
+/* structure definitions */
+struct	load_os	
+{
+	unsigned short 	voices;
+	unsigned short	forced_base_port;
+	unsigned char	forced_gf1_irq;
+	unsigned char	forced_midi_irq;
+	unsigned char	forced_channel_in;
+	unsigned char	forced_channel_out;
+};
+
+struct	patchheader
+{
+	char		header[ HEADER_SIZE ];	
+	char		gravis_id[ ID_SIZE ];	/* Id = "ID#000002" */
+	char		description[ DESC_SIZE ];
+	unsigned char	instruments;
+	char		voices;
+	char		channels;
+	unsigned short	wave_forms;
+	unsigned short	master_volume;
+	unsigned long	data_size;
+	char		reserved[ PATCH_HEADER_RESERVED_SIZE ];
+};
+
+struct	instrumentdata
+{
+	unsigned short	instrument;
+	char		instrument_name[ INST_NAME_SIZE ];
+	long		instrument_size;
+	char		layers;
+	char		reserved[ RESERVED_SIZE ];	
+};
+
+struct	layerdata
+{
+	char		layer_duplicate;
+	char		layer;
+	long		layer_size;
+	char		samples;
+	char		reserved[ LAYER_RESERVED_SIZE ];	
+};
+
+struct	patchdata
+{
+	char		wave_name[7];
+	unsigned char	fractions;
+	long		wave_size;
+	long		start_loop;
+	long		end_loop;
+	unsigned short	sample_rate;
+	long		low_frequency;
+	long		high_frequency;
+	long		root_frequency;
+	short		tune;
+	unsigned char	balance;
+	unsigned char	envelope_rate[ ENVELOPES ];
+	unsigned char	envelope_offset[ ENVELOPES ];	
+	unsigned char	tremolo_sweep;
+	unsigned char	tremolo_rate;
+	unsigned char	tremolo_depth;
+	unsigned char	vibrato_sweep;
+	unsigned char	vibrato_rate;
+	unsigned char	vibrato_depth;
+	char		modes;
+	short		scale_frequency;
+	unsigned short	scale_factor;		/* from 0 to 2048 or 0 to 2 */
+	char		reserved[ PATCH_DATA_RESERVED_SIZE ];
+};
+
+struct wave_struct
+{
+	unsigned long		start_loop;
+	unsigned long		end_loop;
+	long		low_frequency;
+	long		high_frequency;
+	long		root_frequency;
+	unsigned long	mem;
+	unsigned short	scale_frequency;
+	unsigned short	sample_rate;
+	unsigned short	scale_factor;
+	unsigned short	start_acc_low;
+	unsigned short	start_acc_high;
+	unsigned short	start_low;
+	unsigned short	start_high;
+	unsigned short	end_low;
+	unsigned short	end_high;
+	unsigned short	end_acc_low;
+	unsigned short	end_acc_high;
+	unsigned short	sample_ratio;
+	unsigned long	wave_size;
+	unsigned char	fractions;
+	unsigned char	balance;
+	unsigned char	envelope_rate[ ENVELOPES ];
+	unsigned char	envelope_offset[ ENVELOPES ];	
+	unsigned char	tremolo_sweep;
+	unsigned char	tremolo_rate;
+	unsigned char	tremolo_depth;
+	unsigned char	vibrato_sweep;
+	unsigned char	vibrato_rate;
+	unsigned char	vibrato_depth;
+	unsigned char	modes;
+};
+
+struct patchinfo {
+	struct patchheader header;
+	struct instrumentdata idata;
+};
+
+struct patch {
+	short nlayers;
+	struct wave_struct RFAR *layer_waves[MAX_LAYERS];
+	short layer_nwaves[MAX_LAYERS];
+	unsigned short detune;
+};
+
+struct gf1_dma_buff {
+	unsigned char RFAR *vptr;
+	unsigned long paddr;
+};
+
+struct gf1_sound {
+	unsigned long mem_pos;
+	unsigned long start_loop;
+	unsigned long end_loop;
+	unsigned char type;
+};
+
+/* GLOBAL VARIABLES (flags) */
+extern char gf1_linear_volumes;
+extern char gf1_dig_use_extra_voice;
+
+/* FUNCTION PROTOTYPES */
+/* Initializeation routines */
+int	gf1_init_ports(int);
+int	gf1_load_os(struct load_os RFAR *os);
+int	gf1_unload_os(void);
+void	gf1_set_appname(char RFAR *);
+void	reset_ultra(int);
+int	gf1_asm_init(void);
+unsigned char gf1_peek(unsigned long address);
+void gf1_poke(unsigned long address, unsigned char data);
+void gf1_poke_block(unsigned char RFAR *data, unsigned long address, unsigned long len, unsigned char dma_control);
+char gf1_good_dram(unsigned long address);
+int GetUltraCfg(struct load_os RFAR *os);
+unsigned long gf1_malloc(unsigned long);
+void gf1_free(unsigned long);
+unsigned long gf1_mem_avail(void);
+unsigned long gf1_mem_largest_avail(void);
+void gf1_delay(void);
+int gf1_allocate_voice(int priority, void (RFAR *steal_notify)(int));
+void gf1_free_voice(unsigned int i);
+void gf1_adjust_priority(int voice, int priority);
+int gf1_dram_xfer(struct gf1_dma_buff RFAR *dptr, unsigned long size, unsigned long dram_address, unsigned char dma_control, unsigned short flags);
+void gf1_stop_dma(void);
+long convert_to_16bit(long address);
+int gf1_wait_dma(void);
+int gf1_dma_ready(void);
+unsigned long gf1_amount_xferred(void);
+int gf1_detect_card(unsigned short port);
+char *gf1_error_str(int);
+int gf1_play_digital(unsigned short priority, unsigned char RFAR *buffer,
+	unsigned long size, unsigned long gf1_addr, unsigned short volume,
+	unsigned short pan, unsigned short frequency, unsigned char type,
+	struct gf1_dma_buff RFAR *dptr,
+	int (RFAR *callback)(int, int, unsigned char RFAR * RFAR *, unsigned long RFAR *));
+void gf1_restart_digital(int voice);
+void gf1_start_digital(int voice);
+void gf1_pause_digital(int voice);
+void RFAR gf1_stop_digital(int voice);
+void gf1_dig_set_dma_rate(unsigned short rate);
+unsigned long gf1_digital_position(int voice);
+int gf1_myatoi(void);
+int gf1_update_waveform(struct wave_struct RFAR *wave_info);
+int gf1_get_patch_info(char RFAR *patch_file, struct patchinfo RFAR *patch);
+int gf1_load_patch(char RFAR *patch_file, struct patchinfo RFAR *patchinfo,
+	struct patch RFAR *patch,
+	struct gf1_dma_buff RFAR *dptr, unsigned short size,
+	unsigned char RFAR *wavemem, int flags);
+void gf1_unload_patch(struct patch RFAR *patch);
+void gf1_detune_patch(struct patch RFAR *patch, unsigned short detune);
+unsigned short gf1_calc_fc(unsigned int sample_ratio,long root,long frequency);
+void gf1_midi_stop_voice(int voice);
+void gf1_midi_wait_voice(int voice);
+unsigned short gf1_midi_status_note(int voice);
+unsigned short gf1_midi_status_voice(int voice);
+void RFAR gf1_midi_stop_note(int note_voice);
+void gf1_midi_note_on(struct patch RFAR *patch, int priority, int note, int velocity, int channel);
+void gf1_midi_note_off(int note, int channel);
+void gf1_midi_silence_patch_notes(struct patch RFAR *patch);
+void gf1_midi_patch_removed(struct patch RFAR *patch);
+int gf1_enable_timer1(int (RFAR *callback)(void), int resolution);
+int gf1_enable_timer2(int (RFAR *callback)(void), int resolution);
+void gf1_disable_timer1(void);
+void gf1_disable_timer2(void);
+void gf1_channel_pitch_bend(int channel, unsigned int bend);
+void gf1_midi_synth_volume(unsigned short synth, int master_volume);
+void gf1_midi_change_program(struct patch RFAR *patch, int channel);
+void gf1_midi_set_vibrato(int channel, int value);
+void gf1_midi_change_volume(int channel, unsigned int volume);
+void gf1_midi_set_balance(int balance, int channel);
+void gf1_midi_channel_sustain(int channel, int sustain);
+void gf1_midi_all_notes_off(int channel);
+void gf1_midi_pitch_bend(int channel, int lsb, int msb);
+void gf1_midi_parameter(int channel, int control, int value);
+int gf1_midi_get_channel_notes(int channel, int notes[]);
+int gf1_midi_get_channel_volume(int channel);
+int gf1_midi_get_master_volume(void);
+int gf1_midi_get_volume(int voice);
+unsigned short gf1_read(int handle, void RFAR *io_buffer, unsigned short size);
+unsigned short gf1_close_file(int handle);
+unsigned int gf1_seek(int handle, unsigned long offset, int method);
+int gf1_open(char RFAR *name);
+#ifdef __FLAT__
+int gf1_atoi(char RFAR **str, int base);
+#else
+int gf1_atoi(void);
+#endif
+void gf1_leave(void);
+short gf1_enter(void);
+void gf1_enter1(void);
+int gf1_play_next_buffer(int voice, unsigned char RFAR *buff, unsigned long size);
+void gf1_dig_set_vol(unsigned short voice, unsigned short vol);
+void gf1_dig_set_pan(unsigned short voice, unsigned short pan);
+int gf1_set_external_semaphore(void RFAR *addr);
+int gf1_clear_external_semaphore(void RFAR *addr);
+void gf1_midi_reset(int c);
+int gf1_add_midi_recv_handler(int (RFAR *handler)());
+int gf1_add_dma_handler(int (*handler)());
+int gf1_add_voice_handler(int (*handler)(int));
+int gf1_add_volume_handler(int (*handler)(int));
+int gf1_add_timer_handler(int timer, int (RFAR *handler)(void));
+void gf1_set_record_rate(unsigned long rate);
+void gf1_create_patch(struct patch RFAR *patch);
+int gf1_add_layer(struct patch RFAR *patch, int layer, char RFAR *wavemem);
+void gf1_get_waveform_info(struct patch RFAR *patch, int layer, int waven,
+	struct wave_struct RFAR *wave);
+void gf1_set_waveform_info(struct patch RFAR *patch, int layer, int waven,
+	struct wave_struct RFAR *wave);
+void gf1_enable_line_in(void);
+void gf1_disable_line_in(void);
+void gf1_enable_mic_in(void);
+void gf1_disable_mic_in(void);
+void gf1_enable_output(void);
+void gf1_disable_output(void);
+void gf1_sound_volume(unsigned short voice, int volume,
+	unsigned long period /* us*10 */);
+void gf1_sound_pan(unsigned short voice, unsigned short pan);
+void gf1_sound_frequency(unsigned short voice, unsigned long freq);
+void RFAR gf1_sound_stop(int voice);
+void gf1_sound_mode(int voice, struct gf1_sound RFAR *sound,
+	unsigned char type);
+int gf1_sound_start(unsigned short priority, struct gf1_sound RFAR *sound,
+	short volume, unsigned long period, short pan, unsigned long freq);
+int gf1_sound_playing(int voice);
+#endif
--- /dev/null
+++ b/Game/src/audiolib/nodpmi.c
@@ -1,0 +1,179 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: DPMI.C
+
+   author: James R. Dose
+   date:   April 8, 1994
+
+   Functions for performing DPMI calls.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "dpmi.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+/*---------------------------------------------------------------------
+   Function: DPMI_GetRealModeVector
+
+   Returns the vector of a real mode interrupt.
+---------------------------------------------------------------------*/
+
+unsigned long DPMI_GetRealModeVector
+   (
+   int num
+   )
+
+   {
+   return 0;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_SetRealModeVector
+
+   Sets the vector of a real mode interrupt.
+---------------------------------------------------------------------*/
+
+void DPMI_SetRealModeVector
+   (
+   int num,
+   unsigned long vector
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_CallRealModeFunction
+
+   Performs a call to a real mode function.
+---------------------------------------------------------------------*/
+
+int DPMI_CallRealModeFunction
+   (
+   dpmi_regs *callregs
+   )
+
+   {
+   return( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_LockMemory
+
+   Locks a region of memory to keep the virtual memory manager from
+   paging the region out.
+---------------------------------------------------------------------*/
+
+int DPMI_LockMemory
+   (
+   void *address,
+   unsigned length
+   )
+
+   {
+   return ( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_LockMemoryRegion
+
+   Locks a region of memory to keep the virtual memory manager from
+   paging the region out.
+---------------------------------------------------------------------*/
+
+int DPMI_LockMemoryRegion
+   (
+   void *start,
+   void *end
+   )
+
+   {
+   int status;
+
+   status = DPMI_LockMemory( start, ( char * )end - ( char * )start );
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_UnlockMemory
+
+   Unlocks a region of memory that was previously locked.
+---------------------------------------------------------------------*/
+
+int DPMI_UnlockMemory
+   (
+   void *address,
+   unsigned length
+   )
+
+   {
+   return ( DPMI_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: DPMI_UnlockMemoryRegion
+
+   Unlocks a region of memory that was previously locked.
+---------------------------------------------------------------------*/
+
+int DPMI_UnlockMemoryRegion
+   (
+   void *start,
+   void *end
+   )
+
+   {
+   int status;
+
+   status = DPMI_UnlockMemory( start, ( char * )end - ( char * )start );
+
+   return( status );
+   }
+
+int DPMI_GetDOSMemory( void **ptr, int *descriptor, unsigned length )
+{
+	/* Lovely... */
+	
+	*ptr = (void *)malloc(length);
+	
+	*descriptor = (int) *ptr;
+	
+	return (descriptor == 0) ? DPMI_Error : DPMI_Ok;
+}
+
+int DPMI_FreeDOSMemory( int descriptor )
+{
+	free((void *)descriptor);
+	
+	return (descriptor == 0) ? DPMI_Error : DPMI_Ok;
+}
--- /dev/null
+++ b/Game/src/audiolib/nomusic.c
@@ -1,0 +1,115 @@
+#include "music.h"
+
+char *MUSIC_ErrorString(int ErrorNumber)
+{
+	return "";
+}
+
+int MUSIC_Init(int SoundCard, int Address)
+{
+	return 0;
+}
+
+int MUSIC_Shutdown(void)
+{
+	return 0;
+}
+
+void MUSIC_SetMaxFMMidiChannel(int channel)
+{
+}
+
+void MUSIC_SetVolume(int volume)
+{
+}
+
+void MUSIC_SetMidiChannelVolume(int channel, int volume)
+{
+}
+
+void MUSIC_ResetMidiChannelVolumes(void)
+{
+}
+
+int MUSIC_GetVolume(void)
+{
+	return 0;
+}
+
+void MUSIC_SetLoopFlag(int loopflag)
+{
+}
+
+int MUSIC_SongPlaying(void)
+{
+	return 0;
+}
+
+void MUSIC_Continue(void)
+{
+}
+
+void MUSIC_Pause(void)
+{
+}
+
+int MUSIC_StopSong(void)
+{
+	return 0;
+}
+
+int MUSIC_PlaySong(unsigned char *song, int loopflag)
+{
+	return 0;
+}
+
+void MUSIC_SetContext(int context)
+{
+}
+
+int MUSIC_GetContext(void)
+{
+	return 0;
+}
+
+void MUSIC_SetSongTick(unsigned long PositionInTicks)
+{
+}
+
+void MUSIC_SetSongTime(unsigned long milliseconds)
+{
+}
+
+void MUSIC_SetSongPosition(int measure, int beat, int tick)
+{
+}
+
+void MUSIC_GetSongPosition(songposition *pos)
+{
+}
+
+void MUSIC_GetSongLength(songposition *pos)
+{
+}
+
+int MUSIC_FadeVolume(int tovolume, int milliseconds)
+{
+	return 0;
+}
+
+int MUSIC_FadeActive(void)
+{
+	return 0;
+}
+
+void MUSIC_StopFade(void)
+{
+}
+
+void MUSIC_RerouteMidiChannel(int channel, int cdecl function( int event, int c1, int c2 ))
+{
+}
+
+void MUSIC_RegisterTimbreBank(unsigned char *timbres)
+{
+}
--- /dev/null
+++ b/Game/src/audiolib/pas16.c
@@ -1,0 +1,1924 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: PAS16.C
+
+   author: James R. Dose
+   date:   March 27, 1994
+
+   Low level routines to support Pro AudioSpectrum and compatible
+   sound cards.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dpmi.h"
+#include "dma.h"
+#include "interrup.h"
+#include "irq.h"
+#include "pas16.h"
+#include "_pas16.h"
+
+#define USESTACK
+
+static const int PAS_Interrupts[ PAS_MaxIrq + 1 ]  =
+   {
+   INVALID, INVALID,     0xa,     0xb,
+   INVALID,     0xd, INVALID,     0xf,
+   INVALID, INVALID,    0x72,    0x73,
+      0x74, INVALID, INVALID,    0x77
+   };
+
+static void    ( interrupt far *PAS_OldInt )( void );
+
+static int PAS_IntController1Mask;
+static int PAS_IntController2Mask;
+
+static int PAS_Installed = FALSE;
+static int PAS_TranslateCode = DEFAULT_BASE;
+
+static int PAS_OriginalPCMLeftVolume  = 75;
+static int PAS_OriginalPCMRightVolume = 75;
+
+static int PAS_OriginalFMLeftVolume  = 75;
+static int PAS_OriginalFMRightVolume = 75;
+
+unsigned int PAS_DMAChannel;
+static int PAS_Irq;
+
+static MVState *PAS_State = NULL;
+static MVFunc  *PAS_Func  = NULL;
+
+static MVState PAS_OriginalState;
+static int     PAS_SampleSizeConfig;
+
+static char   *PAS_DMABuffer;
+static char   *PAS_DMABufferEnd;
+static char   *PAS_CurrentDMABuffer;
+static int     PAS_TotalDMABufferSize;
+
+static int      PAS_TransferLength   = 0;
+static int      PAS_MixMode          = PAS_DefaultMixMode;
+static unsigned PAS_SampleRate       = PAS_DefaultSampleRate;
+static int      PAS_TimeInterval     = 0;
+
+volatile int   PAS_SoundPlaying;
+
+void ( *PAS_CallBack )( void );
+
+// adequate stack size
+#define kStackSize 2048
+
+static unsigned short StackSelector = NULL;
+static unsigned long  StackPointer;
+
+static unsigned short oldStackSelector;
+static unsigned long  oldStackPointer;
+
+// This is defined because we can't create local variables in a
+// function that switches stacks.
+static int irqstatus;
+
+// These declarations are necessary to use the inline assembly pragmas.
+
+extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
+extern void SetStack(unsigned short selector,unsigned long stackptr);
+
+// This function will get the current stack selector and pointer and save
+// them off.
+#pragma aux GetStack =  \
+   "mov  [edi],esp"     \
+   "mov  ax,ss"         \
+   "mov  [esi],ax"      \
+   parm [esi] [edi]     \
+   modify [eax esi edi];
+
+// This function will set the stack selector and pointer to the specified
+// values.
+#pragma aux SetStack =  \
+   "mov  ss,ax"         \
+   "mov  esp,edx"       \
+   parm [ax] [edx]      \
+   modify [eax edx];
+
+int PAS_ErrorCode = PAS_Ok;
+
+#define PAS_SetErrorCode( status ) \
+   PAS_ErrorCode   = ( status );
+
+/*---------------------------------------------------------------------
+   Function: PAS_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *PAS_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case PAS_Warning :
+      case PAS_Error :
+         ErrorString = PAS_ErrorString( PAS_ErrorCode );
+         break;
+
+      case PAS_Ok :
+         ErrorString = "Pro AudioSpectrum ok.";
+         break;
+
+      case PAS_DriverNotFound :
+         ErrorString = "MVSOUND.SYS not loaded.";
+         break;
+
+      case PAS_DmaError :
+         ErrorString = DMA_ErrorString( DMA_Error );
+         break;
+
+      case PAS_InvalidIrq :
+         ErrorString = "Invalid Pro AudioSpectrum Irq.";
+         break;
+
+      case PAS_UnableToSetIrq :
+         ErrorString = "Unable to set Pro AudioSpectrum IRQ.  Try selecting an IRQ of 7 or below.";
+         break;
+
+      case PAS_Dos4gwIrqError :
+         ErrorString = "Unsupported Pro AudioSpectrum Irq.";
+         break;
+
+      case PAS_NoSoundPlaying :
+         ErrorString = "No sound playing on Pro AudioSpectrum.";
+         break;
+
+      case PAS_CardNotFound :
+         ErrorString = "Could not find Pro AudioSpectrum.";
+         break;
+
+      case PAS_DPMI_Error :
+         ErrorString = "DPMI Error in PAS16.";
+         break;
+
+      case PAS_OutOfMemory :
+         ErrorString = "Out of conventional memory in PAS16.";
+         break;
+
+      default :
+         ErrorString = "Unknown Pro AudioSpectrum error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define PAS_LockStart PAS_CheckForDriver
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_CheckForDriver
+
+   Checks to see if MVSOUND.SYS is installed.
+---------------------------------------------------------------------*/
+
+int PAS_CheckForDriver
+   (
+   void
+   )
+
+   {
+   union REGS regs;
+   unsigned   result;
+
+   regs.w.ax = MV_CheckForDriver;
+   regs.w.bx = 0x3f3f;
+
+   #ifdef __386__
+      int386( MV_SoundInt, &regs, &regs );
+   #else
+      int86( MV_SoundInt, &regs, &regs );
+   #endif
+
+   if ( regs.w.ax != MV_CheckForDriver )
+      {
+      PAS_SetErrorCode( PAS_DriverNotFound );
+      return( PAS_Error );
+      }
+
+   result = regs.w.bx ^ regs.w.cx ^ regs.w.dx;
+   if ( result != MV_Signature )
+      {
+      PAS_SetErrorCode( PAS_DriverNotFound );
+      return( PAS_Error );
+      }
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetStateTable
+
+   Returns a pointer to the state table containing hardware state
+   information.  The state table is necessary because the Pro Audio-
+   Spectrum contains only write-only registers.
+---------------------------------------------------------------------*/
+
+MVState *PAS_GetStateTable
+   (
+   void
+   )
+
+   {
+   union REGS   regs;
+   MVState *ptr;
+
+   regs.w.ax = MV_GetPointerToStateTable;
+
+   #ifdef __386__
+      int386( MV_SoundInt, &regs, &regs );
+   #else
+      int86( MV_SoundInt, &regs, &regs );
+   #endif
+
+   if ( regs.w.ax != MV_Signature )
+      {
+      PAS_SetErrorCode( PAS_DriverNotFound );
+      return( NULL );
+      }
+
+   #if defined(__WATCOMC__) && defined(__FLAT__)
+      ptr = ( MVState * )( ( ( ( unsigned )regs.w.dx ) << 4 ) +
+         ( ( unsigned )regs.w.bx ) );
+   #else
+      ptr = MK_FP( regs.w.dx, regs.w.bx );
+   #endif
+
+   return( ptr );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetFunctionTable
+
+   Returns a pointer to the function table containing addresses of
+   driver functions.
+---------------------------------------------------------------------*/
+
+MVFunc *PAS_GetFunctionTable
+   (
+   void
+   )
+
+   {
+   union REGS   regs;
+   MVFunc *ptr;
+
+   regs.w.ax = MV_GetPointerToFunctionTable;
+
+   #ifdef __386__
+      int386( MV_SoundInt, &regs, &regs );
+   #else
+      int86( MV_SoundInt, &regs, &regs );
+   #endif
+
+   if ( regs.w.ax != MV_Signature )
+      {
+      PAS_SetErrorCode( PAS_DriverNotFound );
+      return( NULL );
+      }
+
+   #if defined(__WATCOMC__) && defined(__FLAT__)
+      ptr = ( MVFunc * )( ( ( ( unsigned )regs.w.dx ) << 4 ) +
+         ( ( unsigned )regs.w.bx ) );
+   #else
+      ptr = MK_FP( regs.w.dx, regs.w.bx );
+   #endif
+
+   return( ptr );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetCardSettings
+
+   Returns the DMA and the IRQ channels of the sound card.
+---------------------------------------------------------------------*/
+
+int PAS_GetCardSettings
+   (
+   void
+   )
+
+   {
+   union REGS   regs;
+   int          status;
+
+   regs.w.ax = MV_GetDmaIrqInt;
+
+   #ifdef __386__
+      int386( MV_SoundInt, &regs, &regs );
+   #else
+      int86( MV_SoundInt, &regs, &regs );
+   #endif
+
+   if ( regs.w.ax != MV_Signature )
+      {
+      PAS_SetErrorCode( PAS_DriverNotFound );
+      return( PAS_Error );
+      }
+
+   PAS_DMAChannel = regs.w.bx;
+   PAS_Irq        = regs.w.cx;
+
+   if ( PAS_Irq > PAS_MaxIrq )
+      {
+      PAS_SetErrorCode( PAS_Dos4gwIrqError );
+      return( PAS_Error );
+      }
+
+   if ( !VALID_IRQ( PAS_Irq ) )
+      {
+      PAS_SetErrorCode( PAS_InvalidIrq );
+      return( PAS_Error );
+      }
+
+   if ( PAS_Interrupts[ PAS_Irq ] == INVALID )
+      {
+      PAS_SetErrorCode( PAS_InvalidIrq );
+      return( PAS_Error );
+      }
+
+   status = DMA_VerifyChannel( PAS_DMAChannel );
+   if ( status == DMA_Error )
+      {
+      PAS_SetErrorCode( PAS_DmaError );
+      return( PAS_Error );
+      }
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_EnableInterrupt
+
+   Enables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+void PAS_EnableInterrupt
+   (
+   void
+   )
+
+   {
+   int mask;
+   int data;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   if ( PAS_Irq < 8 )
+      {
+      mask = inp( 0x21 ) & ~( 1 << PAS_Irq );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask = inp( 0xA1 ) & ~( 1 << ( PAS_Irq - 8 ) );
+      outp( 0xA1, mask  );
+
+      mask = inp( 0x21 ) & ~( 1 << 2 );
+      outp( 0x21, mask  );
+      }
+
+   // Flush any pending interrupts
+   PAS_Write( InterruptStatus, PAS_Read( InterruptStatus ) & 0x40 );
+
+   // Enable the interrupt on the PAS
+   data = PAS_State->intrctlr;
+   data |= SampleBufferInterruptFlag;
+   PAS_Write( InterruptControl, data );
+   PAS_State->intrctlr = data;
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_DisableInterrupt
+
+   Disables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+void PAS_DisableInterrupt
+   (
+   void
+   )
+
+   {
+   int mask;
+   int data;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   // Disable the interrupt on the PAS
+   data = PAS_State->intrctlr;
+   data &= ~( SampleRateInterruptFlag | SampleBufferInterruptFlag );
+   PAS_Write( InterruptControl, data );
+   PAS_State->intrctlr = data;
+
+   // Restore interrupt mask
+   if ( PAS_Irq < 8 )
+      {
+      mask  = inp( 0x21 ) & ~( 1 << PAS_Irq );
+      mask |= PAS_IntController1Mask & ( 1 << PAS_Irq );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask  = inp( 0x21 ) & ~( 1 << 2 );
+      mask |= PAS_IntController1Mask & ( 1 << 2 );
+      outp( 0x21, mask  );
+
+      mask  = inp( 0xA1 ) & ~( 1 << ( PAS_Irq - 8 ) );
+      mask |= PAS_IntController2Mask & ( 1 << ( PAS_Irq - 8 ) );
+      outp( 0xA1, mask  );
+      }
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_ServiceInterrupt
+
+   Handles interrupt generated by sound card at the end of a voice
+   transfer.  Calls the user supplied callback function.
+---------------------------------------------------------------------*/
+
+void interrupt far PAS_ServiceInterrupt
+   (
+   void
+   )
+
+   {
+   #ifdef USESTACK
+   // save stack
+   GetStack( &oldStackSelector, &oldStackPointer );
+
+   // set our stack
+   SetStack( StackSelector, StackPointer );
+   #endif
+
+   irqstatus = PAS_Read( InterruptStatus );
+   if ( ( irqstatus & SampleBufferInterruptFlag ) == 0 )
+      {
+      #ifdef USESTACK
+      // restore stack
+      SetStack( oldStackSelector, oldStackPointer );
+      #endif
+
+      _chain_intr( PAS_OldInt );
+      }
+
+   // Clear the interrupt
+   irqstatus &= ~SampleBufferInterruptFlag;
+   PAS_Write( InterruptStatus, irqstatus );
+
+   // send EOI to Interrupt Controller
+   if ( PAS_Irq > 7 )
+      {
+      outp( 0xA0, 0x20 );
+      }
+   outp( 0x20, 0x20 );
+
+
+   // Keep track of current buffer
+   PAS_CurrentDMABuffer += PAS_TransferLength;
+   if ( PAS_CurrentDMABuffer >= PAS_DMABufferEnd )
+      {
+      PAS_CurrentDMABuffer = PAS_DMABuffer;
+      }
+
+   // Call the caller's callback function
+   if ( PAS_CallBack != NULL )
+      {
+      PAS_CallBack();
+      }
+
+   #ifdef USESTACK
+   // restore stack
+   SetStack( oldStackSelector, oldStackPointer );
+   #endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_Write
+
+   Writes a byte of data to the sound card.
+---------------------------------------------------------------------*/
+
+void PAS_Write
+   (
+   int Register,
+   int Data
+   )
+
+   {
+   int port;
+
+   port = Register ^ PAS_TranslateCode;
+   outp( port, Data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_Read
+
+   Reads a byte of data from the sound card.
+---------------------------------------------------------------------*/
+
+int PAS_Read
+   (
+   int Register
+   )
+
+   {
+   int port;
+   int data;
+
+   port = Register ^ PAS_TranslateCode;
+   data = inp( port );
+   return( data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetSampleRateTimer
+
+   Programs the Sample Rate Timer.
+---------------------------------------------------------------------*/
+
+void PAS_SetSampleRateTimer
+   (
+   void
+   )
+
+   {
+   int LoByte;
+   int HiByte;
+   int data;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   // Disable the Sample Rate Timer
+   data = PAS_State->audiofilt;
+   data &= ~SampleRateTimerGateFlag;
+   PAS_Write( AudioFilterControl, data );
+   PAS_State->audiofilt = data;
+
+   // Select the Sample Rate Timer
+   data = SelectSampleRateTimer;
+   PAS_Write( LocalTimerControl, data );
+   PAS_State->tmrctlr = data;
+
+   LoByte = lobyte( PAS_TimeInterval );
+   HiByte = hibyte( PAS_TimeInterval );
+
+   // Program the Sample Rate Timer
+   PAS_Write( SampleRateTimer, LoByte );
+   PAS_Write( SampleRateTimer, HiByte );
+   PAS_State->samplerate = PAS_TimeInterval;
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetSampleBufferCount
+
+   Programs the Sample Buffer Count.
+---------------------------------------------------------------------*/
+
+void PAS_SetSampleBufferCount
+   (
+   void
+   )
+
+   {
+   int LoByte;
+   int HiByte;
+   int count;
+   int data;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   // Disable the Sample Buffer Count
+   data = PAS_State->audiofilt;
+   data &= ~SampleBufferCountGateFlag;
+   PAS_Write( AudioFilterControl, data );
+   PAS_State->audiofilt = data;
+
+   // Select the Sample Buffer Count
+   data = SelectSampleBufferCount;
+   PAS_Write( LocalTimerControl, data );
+   PAS_State->tmrctlr = data;
+
+   count = PAS_TransferLength;
+
+   // Check if we're using a 16-bit DMA channel
+   if ( PAS_DMAChannel > 3 )
+      {
+      count >>= 1;
+      }
+
+   LoByte = lobyte( count );
+   HiByte = hibyte( count );
+
+   // Program the Sample Buffer Count
+   PAS_Write( SampleBufferCount, LoByte );
+   PAS_Write( SampleBufferCount, HiByte );
+   PAS_State->samplecnt = count;
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetPlaybackRate
+
+   Sets the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+void PAS_SetPlaybackRate
+   (
+   unsigned rate
+   )
+
+   {
+   if ( rate < PAS_MinSamplingRate )
+      {
+      rate = PAS_MinSamplingRate;
+      }
+
+   if ( rate > PAS_MaxSamplingRate )
+      {
+      rate = PAS_MaxSamplingRate;
+      }
+
+   PAS_TimeInterval = ( unsigned )CalcTimeInterval( rate );
+   if ( PAS_MixMode & STEREO )
+      {
+      PAS_TimeInterval /= 2;
+      }
+
+   // Keep track of what the actual rate is
+   PAS_SampleRate = CalcSamplingRate( PAS_TimeInterval );
+   if ( PAS_MixMode & STEREO )
+      {
+      PAS_SampleRate /= 2;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetPlaybackRate
+
+   Returns the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+unsigned PAS_GetPlaybackRate
+   (
+   void
+   )
+
+   {
+   return( PAS_SampleRate );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetMixMode
+
+   Sets the sound card to play samples in mono or stereo.
+---------------------------------------------------------------------*/
+
+int PAS_SetMixMode
+   (
+   int mode
+   )
+
+   {
+   mode &= PAS_MaxMixMode;
+
+   // Check board revision.  Revision # 0 can't play 16-bit data.
+   if ( ( PAS_State->intrctlr & 0xe0 ) == 0 )
+      {
+      // Force the mode to 8-bit data.
+      mode &= ~SIXTEEN_BIT;
+      }
+
+   PAS_MixMode = mode;
+
+   PAS_SetPlaybackRate( PAS_SampleRate );
+
+   return( mode );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_StopPlayback
+
+   Ends the DMA transfer of digitized sound to the sound card.
+---------------------------------------------------------------------*/
+
+void PAS_StopPlayback
+   (
+   void
+   )
+
+   {
+   int data;
+
+   // Don't allow anymore interrupts
+   PAS_DisableInterrupt();
+
+   // Stop the transfer of digital data
+   data = PAS_State->crosschannel;
+   data &= PAS_PCMStopMask;
+   PAS_Write( CrossChannelControl, data );
+   PAS_State->crosschannel = data;
+
+   // Turn off 16-bit unsigned data
+   data = PAS_Read( SampleSizeConfiguration );
+   data &= PAS_SampleSizeMask;
+   PAS_Write( SampleSizeConfiguration, data );
+
+   // Disable the DMA channel
+   DMA_EndTransfer( PAS_DMAChannel );
+
+   PAS_SoundPlaying = FALSE;
+
+   PAS_DMABuffer = NULL;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetupDMABuffer
+
+   Programs the DMAC for sound transfer.
+---------------------------------------------------------------------*/
+
+int PAS_SetupDMABuffer
+   (
+   char *BufferPtr,
+   int   BufferSize,
+   int   mode
+   )
+
+   {
+   int DmaStatus;
+   int data;
+
+   // Enable PAS Dma
+   data  = PAS_State->crosschannel;
+   data |= PAS_DMAEnable;
+   PAS_Write( CrossChannelControl, data );
+   PAS_State->crosschannel = data;
+
+   DmaStatus = DMA_SetupTransfer( PAS_DMAChannel, BufferPtr, BufferSize, mode );
+   if ( DmaStatus == DMA_Error )
+      {
+      PAS_SetErrorCode( PAS_DmaError );
+      return( PAS_Error );
+      }
+
+   PAS_DMABuffer          = BufferPtr;
+   PAS_CurrentDMABuffer   = BufferPtr;
+   PAS_TotalDMABufferSize = BufferSize;
+   PAS_DMABufferEnd       = BufferPtr + BufferSize;
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetCurrentPos
+
+   Returns the offset within the current sound being played.
+---------------------------------------------------------------------*/
+
+int PAS_GetCurrentPos
+   (
+   void
+   )
+
+   {
+   char *CurrentAddr;
+   int   offset;
+
+   if ( !PAS_SoundPlaying )
+      {
+      PAS_SetErrorCode( PAS_NoSoundPlaying );
+      return( PAS_Error );
+      }
+
+   CurrentAddr = DMA_GetCurrentPos( PAS_DMAChannel );
+   if ( CurrentAddr == NULL )
+      {
+      PAS_SetErrorCode( PAS_DmaError );
+      return( PAS_Error );
+      }
+
+   offset = ( int )( ( ( unsigned long )CurrentAddr ) -
+      ( ( unsigned long )PAS_CurrentDMABuffer ) );
+
+   if ( PAS_MixMode & SIXTEEN_BIT )
+      {
+      offset >>= 1;
+      }
+
+   if ( PAS_MixMode & STEREO )
+      {
+      offset >>= 1;
+      }
+
+   return( offset );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetFilterSetting
+
+   Returns the bit settings for the appropriate filter level.
+---------------------------------------------------------------------*/
+
+int PAS_GetFilterSetting
+   (
+   int rate
+   )
+
+   {
+   /* CD Quality 17897hz */
+   if ( ( unsigned long )rate > ( unsigned long )17897L * 2 )
+      {
+      /* 00001b 20hz to 17.8khz */
+      return( 0x01 );
+      }
+
+   /* Cassette Quality 15090hz */
+   if ( ( unsigned long )rate > ( unsigned long )15909L * 2 )
+      {
+      /* 00010b 20hz to 15.9khz */
+      return( 0x02 );
+      }
+
+   /* FM Radio Quality 11931hz */
+   if ( ( unsigned long )rate > ( unsigned long )11931L * 2 )
+      {
+      /* 01001b 20hz to 11.9khz */
+      return( 0x09 );
+      }
+
+   /* AM Radio Quality  8948hz */
+   if ( ( unsigned long )rate > ( unsigned long )8948L * 2 )
+      {
+      /* 10001b 20hz to 8.9khz */
+      return( 0x11 );
+      }
+
+   /* Telphone Quality  5965hz */
+   if ( ( unsigned long )rate > ( unsigned long )5965L * 2 )
+      {
+      /* 00100b 20hz to 5.9khz */
+      return( 0x19 );
+      }
+
+   /* Male voice quality 2982hz */
+   /* 111001b 20hz to 2.9khz */
+   return( 0x04 );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_BeginTransfer
+
+   Starts playback of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+void PAS_BeginTransfer
+   (
+   int mode
+   )
+
+   {
+   int data;
+
+   PAS_SetSampleRateTimer();
+
+   PAS_SetSampleBufferCount();
+
+   PAS_EnableInterrupt();
+
+   // Get sample size configuration
+   data = PAS_Read( SampleSizeConfiguration );
+
+   // Check board revision.  Revision # 0 can't play 16-bit data.
+   if ( PAS_State->intrctlr & 0xe0 )
+      {
+      data &= PAS_SampleSizeMask;
+
+      // set sample size bit
+      if ( PAS_MixMode & SIXTEEN_BIT )
+         {
+         data |= PAS_16BitSampleFlag;
+         }
+      }
+
+   // set oversampling rate
+   data &= PAS_OverSamplingMask;
+   data |= PAS_4xOverSampling;
+
+   // Set sample size configuration
+   PAS_Write( SampleSizeConfiguration, data );
+
+   // Get Cross channel setting
+   data  = PAS_State->crosschannel;
+   data &= PAS_ChannelConnectMask;
+   if ( mode == RECORD )
+      {
+      data |= PAS_PCMStartADC;
+      }
+   else
+      {
+      data |= PAS_PCMStartDAC;
+      }
+
+   // set stereo mode bit
+   if ( !( PAS_MixMode & STEREO ) )
+      {
+      data |= PAS_StereoFlag;
+      }
+
+   PAS_Write( CrossChannelControl, data );
+   PAS_State->crosschannel = data;
+
+   // Get the filter appropriate filter setting
+   data = PAS_GetFilterSetting( PAS_SampleRate );
+
+   // Enable the Sample Rate Timer and Sample Buffer Count
+   data |= SampleRateTimerGateFlag | SampleBufferCountGateFlag;
+
+   if ( mode != RECORD )
+      {
+      // Enable audio (not Audio Mute)
+      data |= PAS_AudioMuteFlag;
+      }
+
+   PAS_Write( AudioFilterControl, data );
+   PAS_State->audiofilt = data;
+
+   PAS_SoundPlaying = TRUE;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_BeginBufferedPlayback
+
+   Begins multibuffered playback of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+int PAS_BeginBufferedPlayback
+   (
+   char *BufferStart,
+   int   BufferSize,
+   int   NumDivisions,
+   unsigned SampleRate,
+   int   MixMode,
+   void ( *CallBackFunc )( void )
+   )
+
+   {
+   int DmaStatus;
+
+   PAS_StopPlayback();
+
+   PAS_SetMixMode( MixMode );
+   PAS_SetPlaybackRate( SampleRate );
+
+   PAS_TransferLength = BufferSize / NumDivisions;
+   PAS_SetCallBack( CallBackFunc );
+
+   DmaStatus = PAS_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitRead );
+   if ( DmaStatus == PAS_Error )
+      {
+      return( PAS_Error );
+      }
+
+   PAS_BeginTransfer( PLAYBACK );
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_BeginBufferedRecord
+
+   Begins multibuffered recording of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+int PAS_BeginBufferedRecord
+   (
+   char *BufferStart,
+   int   BufferSize,
+   int   NumDivisions,
+   unsigned SampleRate,
+   int   MixMode,
+   void ( *CallBackFunc )( void )
+   )
+
+   {
+   int DmaStatus;
+
+   PAS_StopPlayback();
+
+   PAS_SetMixMode( MixMode );
+   PAS_SetPlaybackRate( SampleRate );
+
+   PAS_TransferLength = BufferSize / NumDivisions;
+   PAS_SetCallBack( CallBackFunc );
+
+   DmaStatus = PAS_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitWrite );
+   if ( DmaStatus == PAS_Error )
+      {
+      return( PAS_Error );
+      }
+
+   PAS_BeginTransfer( RECORD );
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_CallInt
+
+   Calls interrupt 2fh.
+---------------------------------------------------------------------*/
+
+int PAS_CallInt( int ebx, int ecx, int edx );
+#pragma aux PAS_CallInt = \
+   "int 2fh",         \
+   parm [ ebx ] [ ecx ] [ edx ] modify exact [ eax ebx ecx edx esi edi ] value [ ebx ];
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_CallMVFunction
+
+   Performs a call to a real mode function.
+---------------------------------------------------------------------*/
+
+int PAS_CallMVFunction
+   (
+   unsigned long function,
+   int ebx,
+   int ecx,
+   int edx
+   )
+
+   {
+   dpmi_regs callregs;
+   int       status;
+
+   callregs.EBX = ebx;
+   callregs.ECX = ecx;
+   callregs.EDX = edx;
+
+   callregs.SS  = 0;
+   callregs.SP  = 0;
+
+   callregs.DS  = 0;
+   callregs.ES  = 0;
+   callregs.FS  = 0;
+   callregs.GS  = 0;
+
+   callregs.IP = function;
+   callregs.CS = function >> 16;
+
+   status = DPMI_CallRealModeFunction( &callregs );
+   if ( status != DPMI_Ok )
+      {
+      return( PAS_Error );
+      }
+
+   return( callregs.EBX & 0xff );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetPCMVolume
+
+   Sets the volume of digitized sound playback.
+---------------------------------------------------------------------*/
+
+int PAS_SetPCMVolume
+   (
+   int volume
+   )
+
+   {
+   int status;
+
+   volume = max( 0, volume );
+   volume = min( volume, 255 );
+
+   volume *= 100;
+   volume /= 255;
+
+   status = PAS_CallMVFunction( PAS_Func->SetMixer, volume,
+      OUTPUTMIXER, L_PCM );
+   if ( status == PAS_Error )
+      {
+      return( status );
+      }
+
+   status = PAS_CallMVFunction( PAS_Func->SetMixer, volume,
+      OUTPUTMIXER, R_PCM );
+   if ( status == PAS_Error )
+      {
+      return( status );
+      }
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetPCMVolume
+
+   Returns the current volume of digitized sound playback.
+---------------------------------------------------------------------*/
+
+int PAS_GetPCMVolume
+   (
+   void
+   )
+
+   {
+   int leftvolume;
+   int rightvolume;
+   int totalvolume;
+
+   if ( PAS_Func == NULL )
+      {
+      return( PAS_Error );
+      }
+
+   leftvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, L_PCM );
+   rightvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, R_PCM );
+
+   if ( ( leftvolume == PAS_Error ) || ( rightvolume == PAS_Error ) )
+      {
+      return( PAS_Error );
+      }
+
+   leftvolume  &= 0xff;
+   rightvolume &= 0xff;
+
+   totalvolume = ( rightvolume + leftvolume ) / 2;
+   totalvolume *= 255;
+   totalvolume /= 100;
+   return( totalvolume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetFMVolume
+
+   Sets the volume of FM sound playback.
+---------------------------------------------------------------------*/
+
+void PAS_SetFMVolume
+   (
+   int volume
+   )
+
+   {
+   volume = max( 0, volume );
+   volume = min( volume, 255 );
+
+   volume *= 100;
+   volume /= 255;
+   if ( PAS_Func )
+      {
+      PAS_CallMVFunction( PAS_Func->SetMixer, volume, OUTPUTMIXER, L_FM );
+      PAS_CallMVFunction( PAS_Func->SetMixer, volume, OUTPUTMIXER, R_FM );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetFMVolume
+
+   Returns the current volume of FM sound playback.
+---------------------------------------------------------------------*/
+
+int PAS_GetFMVolume
+   (
+   void
+   )
+
+   {
+   int leftvolume;
+   int rightvolume;
+   int totalvolume;
+
+   if ( PAS_Func == NULL )
+      {
+      return( 255 );
+      }
+
+   leftvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, L_FM ) & 0xff;
+   rightvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, R_FM ) & 0xff;
+
+   totalvolume  = ( rightvolume + leftvolume ) / 2;
+   totalvolume *= 255;
+   totalvolume /= 100;
+   totalvolume  = min( 255, totalvolume );
+
+   return( totalvolume );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_GetCardInfo
+
+   Returns the maximum number of bits that can represent a sample
+   (8 or 16) and the number of channels (1 for mono, 2 for stereo).
+---------------------------------------------------------------------*/
+
+int PAS_GetCardInfo
+   (
+   int *MaxSampleBits,
+   int *MaxChannels
+   )
+
+   {
+   int status;
+
+   if ( PAS_State == NULL )
+      {
+      status = PAS_CheckForDriver();
+      if ( status != PAS_Ok )
+         {
+         return( status );
+         }
+
+      PAS_State = PAS_GetStateTable();
+      if ( PAS_State == NULL )
+         {
+         return( PAS_Error );
+         }
+      }
+
+   *MaxChannels = 2;
+
+   // Check board revision.  Revision # 0 can't play 16-bit data.
+   if ( ( PAS_State->intrctlr & 0xe0 ) == 0 )
+      {
+      *MaxSampleBits = 8;
+      }
+   else
+      {
+      *MaxSampleBits = 16;
+      }
+
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SetCallBack
+
+   Specifies the user function to call at the end of a sound transfer.
+---------------------------------------------------------------------*/
+
+void PAS_SetCallBack
+   (
+   void ( *func )( void )
+   )
+
+   {
+   PAS_CallBack = func;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_FindCard
+
+   Auto-detects the port the Pro AudioSpectrum is set for.
+---------------------------------------------------------------------*/
+
+int PAS_FindCard
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status = PAS_TestAddress( DEFAULT_BASE );
+   if ( status == 0 )
+      {
+      PAS_TranslateCode = DEFAULT_BASE;
+      return( PAS_Ok );
+      }
+
+   status = PAS_TestAddress( ALT_BASE_1 );
+   if ( status == 0 )
+      {
+      PAS_TranslateCode = ALT_BASE_1;
+      return( PAS_Ok );
+      }
+
+   status = PAS_TestAddress( ALT_BASE_2 );
+   if ( status == 0 )
+      {
+      PAS_TranslateCode = ALT_BASE_2;
+      return( PAS_Ok );
+      }
+
+   status = PAS_TestAddress( ALT_BASE_3 );
+   if ( status == 0 )
+      {
+      PAS_TranslateCode = ALT_BASE_3;
+      return( PAS_Ok );
+      }
+
+   PAS_SetErrorCode( PAS_CardNotFound );
+   return( PAS_Error );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SaveMusicVolume
+
+   Saves the user's FM mixer settings.
+---------------------------------------------------------------------*/
+
+int PAS_SaveMusicVolume
+   (
+   void
+   )
+
+   {
+   int status;
+   int data;
+
+   if ( !PAS_Installed )
+      {
+      status = PAS_CheckForDriver();
+      if ( status != PAS_Ok )
+         {
+         return( status );
+         }
+
+      PAS_State = PAS_GetStateTable();
+      if ( PAS_State == NULL )
+         {
+         return( PAS_Error );
+         }
+
+      PAS_Func = PAS_GetFunctionTable();
+      if ( PAS_Func == NULL )
+         {
+         return( PAS_Error );
+         }
+
+      status = PAS_GetCardSettings();
+      if ( status != PAS_Ok )
+         {
+         return( status );
+         }
+
+      status = PAS_FindCard();
+      if ( status != PAS_Ok )
+         {
+         return( status );
+         }
+
+      // Enable PAS Sound
+      data  = PAS_State->audiofilt;
+      data |= PAS_AudioMuteFlag;
+
+      PAS_Write( AudioFilterControl, data );
+      PAS_State->audiofilt = data;
+      }
+
+   status = PAS_CallMVFunction( PAS_Func->GetMixer, 0, OUTPUTMIXER, L_FM );
+   if ( status != PAS_Error )
+      {
+      PAS_OriginalFMLeftVolume  = PAS_CallMVFunction( PAS_Func->GetMixer,
+         0, OUTPUTMIXER, L_FM ) & 0xff;
+
+      PAS_OriginalFMRightVolume = PAS_CallMVFunction( PAS_Func->GetMixer,
+         0, OUTPUTMIXER, R_FM ) & 0xff;
+
+      return( PAS_Ok );
+      }
+
+   return( PAS_Warning );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_RestoreMusicVolume
+
+   Restores the user's FM mixer settings.
+---------------------------------------------------------------------*/
+
+void PAS_RestoreMusicVolume
+   (
+   void
+   )
+
+   {
+   if ( PAS_Func )
+      {
+      PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalFMLeftVolume,
+         OUTPUTMIXER, L_FM );
+      PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalFMRightVolume,
+         OUTPUTMIXER, R_FM );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_SaveState
+
+   Saves the original state of the PAS prior to use.
+---------------------------------------------------------------------*/
+
+void PAS_SaveState
+   (
+   void
+   )
+
+   {
+   PAS_OriginalState.intrctlr     = PAS_State->intrctlr;
+   PAS_OriginalState.audiofilt    = PAS_State->audiofilt;
+   PAS_OriginalState.tmrctlr      = PAS_State->tmrctlr;
+   PAS_OriginalState.samplerate   = PAS_State->samplerate;
+   PAS_OriginalState.samplecnt    = PAS_State->samplecnt;
+   PAS_OriginalState.crosschannel = PAS_State->crosschannel;
+   PAS_SampleSizeConfig = PAS_Read( SampleSizeConfiguration );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_RestoreState
+
+   Restores the original state of the PAS after use.
+---------------------------------------------------------------------*/
+
+void PAS_RestoreState
+   (
+   void
+   )
+
+   {
+   int LoByte;
+   int HiByte;
+
+   // Select the Sample Rate Timer
+   PAS_Write( LocalTimerControl, SelectSampleRateTimer );
+   PAS_State->tmrctlr = SelectSampleRateTimer;
+
+   PAS_Write( SampleRateTimer, PAS_OriginalState.samplerate );
+   PAS_State->samplerate = PAS_OriginalState.samplerate;
+
+   // Select the Sample Buffer Count
+   PAS_Write( LocalTimerControl, SelectSampleBufferCount );
+   PAS_State->tmrctlr = SelectSampleBufferCount;
+
+   LoByte = lobyte( PAS_OriginalState.samplecnt );
+   HiByte = hibyte( PAS_OriginalState.samplecnt );
+   PAS_Write( SampleRateTimer, LoByte );
+   PAS_Write( SampleRateTimer, HiByte );
+   PAS_State->samplecnt = PAS_OriginalState.samplecnt;
+
+   PAS_Write( CrossChannelControl, PAS_OriginalState.crosschannel );
+   PAS_State->crosschannel = PAS_OriginalState.crosschannel;
+
+   PAS_Write( SampleSizeConfiguration, PAS_SampleSizeConfig );
+
+   PAS_Write( InterruptControl, PAS_OriginalState.intrctlr );
+   PAS_State->intrctlr = PAS_OriginalState.intrctlr;
+
+   PAS_Write( AudioFilterControl, PAS_OriginalState.audiofilt );
+   PAS_State->audiofilt = PAS_OriginalState.audiofilt;
+
+   PAS_Write( LocalTimerControl, PAS_OriginalState.tmrctlr );
+   PAS_State->tmrctlr = PAS_OriginalState.tmrctlr;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void PAS_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: allocateTimerStack
+
+   Allocate a block of memory from conventional (low) memory and return
+   the selector (which can go directly into a segment register) of the
+   memory block or 0 if an error occured.
+---------------------------------------------------------------------*/
+
+static unsigned short allocateTimerStack
+   (
+   unsigned short size
+   )
+
+   {
+   union REGS regs;
+
+   // clear all registers
+   memset( &regs, 0, sizeof( regs ) );
+
+   // DPMI allocate conventional memory
+   regs.w.ax = 0x100;
+
+   // size in paragraphs
+   regs.w.bx = ( size + 15 ) / 16;
+
+   int386( 0x31, &regs, &regs );
+   if (!regs.w.cflag)
+      {
+      // DPMI call returns selector in dx
+      // (ax contains real mode segment
+      // which is ignored here)
+
+      return( regs.w.dx );
+      }
+
+   // Couldn't allocate memory.
+   return( NULL );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: deallocateTimerStack
+
+   Deallocate a block of conventional (low) memory given a selector to
+   it.  Assumes the block was allocated with DPMI function 0x100.
+---------------------------------------------------------------------*/
+
+static void deallocateTimerStack
+   (
+   unsigned short selector
+   )
+
+   {
+   union REGS regs;
+
+   if ( selector != NULL )
+      {
+      // clear all registers
+      memset( &regs, 0, sizeof( regs ) );
+
+      regs.w.ax = 0x101;
+      regs.w.dx = selector;
+      int386( 0x31, &regs, &regs );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_Init
+
+   Initializes the sound card and prepares the module to play
+   digitized sounds.
+---------------------------------------------------------------------*/
+
+int PAS_Init
+   (
+   void
+   )
+
+   {
+   int Interrupt;
+   int status;
+   int data;
+
+   if ( PAS_Installed )
+      {
+      return( PAS_Ok );
+      }
+
+   PAS_IntController1Mask = inp( 0x21 );
+   PAS_IntController2Mask = inp( 0xA1 );
+
+   status = PAS_CheckForDriver();
+   if ( status != PAS_Ok )
+      {
+      return( status );
+      }
+
+   PAS_State = PAS_GetStateTable();
+   if ( PAS_State == NULL )
+      {
+      return( PAS_Error );
+      }
+
+   PAS_Func = PAS_GetFunctionTable();
+   if ( PAS_Func == NULL )
+      {
+      return( PAS_Error );
+      }
+
+   status = PAS_GetCardSettings();
+   if ( status != PAS_Ok )
+      {
+      return( status );
+      }
+
+   status = PAS_FindCard();
+   if ( status != PAS_Ok )
+      {
+      return( status );
+      }
+
+   PAS_SaveState();
+
+   PAS_OriginalPCMLeftVolume  = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, L_PCM ) & 0xff;
+   PAS_OriginalPCMRightVolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
+      OUTPUTMIXER, R_PCM ) & 0xff;
+
+   PAS_SoundPlaying = FALSE;
+
+   PAS_SetCallBack( NULL );
+
+   PAS_DMABuffer = NULL;
+
+   status = PAS_LockMemory();
+   if ( status != PAS_Ok )
+      {
+      PAS_UnlockMemory();
+      return( status );
+      }
+
+   StackSelector = allocateTimerStack( kStackSize );
+   if ( StackSelector == NULL )
+      {
+      PAS_UnlockMemory();
+      PAS_SetErrorCode( PAS_OutOfMemory );
+      return( PAS_Error );
+      }
+
+   // Leave a little room at top of stack just for the hell of it...
+   StackPointer = kStackSize - sizeof( long );
+
+   // Install our interrupt handler
+   Interrupt = PAS_Interrupts[ PAS_Irq ];
+   PAS_OldInt = _dos_getvect( Interrupt );
+   if ( PAS_Irq < 8 )
+      {
+      _dos_setvect( Interrupt, PAS_ServiceInterrupt );
+      }
+   else
+      {
+      status = IRQ_SetVector( Interrupt, PAS_ServiceInterrupt );
+      if ( status != IRQ_Ok )
+         {
+         PAS_UnlockMemory();
+         deallocateTimerStack( StackSelector );
+         StackSelector = NULL;
+         PAS_SetErrorCode( PAS_UnableToSetIrq );
+         return( PAS_Error );
+         }
+      }
+
+   // Enable PAS Sound
+   data  = PAS_State->audiofilt;
+   data |= PAS_AudioMuteFlag;
+
+   PAS_Write( AudioFilterControl, data );
+   PAS_State->audiofilt = data;
+
+   PAS_SetPlaybackRate( PAS_DefaultSampleRate );
+   PAS_SetMixMode( PAS_DefaultMixMode );
+
+   PAS_Installed = TRUE;
+
+   PAS_SetErrorCode( PAS_Ok );
+   return( PAS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_Shutdown
+
+   Ends transfer of sound data to the sound card and restores the
+   system resources used by the card.
+---------------------------------------------------------------------*/
+
+void PAS_Shutdown
+   (
+   void
+   )
+
+   {
+   int Interrupt;
+
+   if ( PAS_Installed )
+      {
+      // Halt the DMA transfer
+      PAS_StopPlayback();
+
+      // Restore the original interrupt
+      Interrupt = PAS_Interrupts[ PAS_Irq ];
+      if ( PAS_Irq >= 8 )
+         {
+         IRQ_RestoreVector( Interrupt );
+         }
+      _dos_setvect( Interrupt, PAS_OldInt );
+
+      PAS_SoundPlaying = FALSE;
+
+      PAS_DMABuffer = NULL;
+
+      PAS_SetCallBack( NULL );
+
+      PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalPCMLeftVolume,
+         OUTPUTMIXER, L_PCM );
+      PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalPCMRightVolume,
+         OUTPUTMIXER, R_PCM );
+
+// DEBUG
+//      PAS_RestoreState();
+
+      PAS_UnlockMemory();
+
+      deallocateTimerStack( StackSelector );
+      StackSelector = NULL;
+
+      PAS_Installed = FALSE;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void PAS_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( PAS_LockStart, PAS_LockEnd );
+   DPMI_Unlock( PAS_Interrupts );
+   DPMI_Unlock( PAS_OldInt );
+   DPMI_Unlock( PAS_IntController1Mask );
+   DPMI_Unlock( PAS_IntController2Mask  );
+   DPMI_Unlock( PAS_Installed );
+   DPMI_Unlock( PAS_TranslateCode );
+   DPMI_Unlock( PAS_OriginalPCMLeftVolume );
+   DPMI_Unlock( PAS_OriginalPCMRightVolume );
+   DPMI_Unlock( PAS_OriginalFMLeftVolume );
+   DPMI_Unlock( PAS_OriginalFMRightVolume );
+   DPMI_Unlock( PAS_DMAChannel );
+   DPMI_Unlock( PAS_Irq );
+   DPMI_Unlock( PAS_State );
+   DPMI_Unlock( PAS_Func );
+   DPMI_Unlock( PAS_OriginalState );
+   DPMI_Unlock( PAS_SampleSizeConfig );
+   DPMI_Unlock( PAS_DMABuffer );
+   DPMI_Unlock( PAS_DMABufferEnd );
+   DPMI_Unlock( PAS_CurrentDMABuffer );
+   DPMI_Unlock( PAS_TotalDMABufferSize );
+   DPMI_Unlock( PAS_TransferLength );
+   DPMI_Unlock( PAS_MixMode );
+   DPMI_Unlock( PAS_SampleRate );
+   DPMI_Unlock( PAS_TimeInterval );
+   DPMI_Unlock( PAS_SoundPlaying );
+   DPMI_Unlock( PAS_CallBack );
+   DPMI_Unlock( PAS_ErrorCode );
+   DPMI_Unlock( irqstatus );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PAS_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int PAS_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( PAS_LockStart, PAS_LockEnd );
+   status |= DPMI_Lock( PAS_Interrupts );
+   status |= DPMI_Lock( PAS_OldInt );
+   status |= DPMI_Lock( PAS_IntController1Mask );
+   status |= DPMI_Lock( PAS_IntController2Mask  );
+   status |= DPMI_Lock( PAS_Installed );
+   status |= DPMI_Lock( PAS_TranslateCode );
+   status |= DPMI_Lock( PAS_OriginalPCMLeftVolume );
+   status |= DPMI_Lock( PAS_OriginalPCMRightVolume );
+   status |= DPMI_Lock( PAS_OriginalFMLeftVolume );
+   status |= DPMI_Lock( PAS_OriginalFMRightVolume );
+   status |= DPMI_Lock( PAS_DMAChannel );
+   status |= DPMI_Lock( PAS_Irq );
+   status |= DPMI_Lock( PAS_State );
+   status |= DPMI_Lock( PAS_Func );
+   status |= DPMI_Lock( PAS_OriginalState );
+   status |= DPMI_Lock( PAS_SampleSizeConfig );
+   status |= DPMI_Lock( PAS_DMABuffer );
+   status |= DPMI_Lock( PAS_DMABufferEnd );
+   status |= DPMI_Lock( PAS_CurrentDMABuffer );
+   status |= DPMI_Lock( PAS_TotalDMABufferSize );
+   status |= DPMI_Lock( PAS_TransferLength );
+   status |= DPMI_Lock( PAS_MixMode );
+   status |= DPMI_Lock( PAS_SampleRate );
+   status |= DPMI_Lock( PAS_TimeInterval );
+   status |= DPMI_Lock( PAS_SoundPlaying );
+   status |= DPMI_Lock( PAS_CallBack );
+   status |= DPMI_Lock( PAS_ErrorCode );
+   status |= DPMI_Lock( irqstatus );
+
+   if ( status != DPMI_Ok )
+      {
+      PAS_UnlockMemory();
+      PAS_SetErrorCode( PAS_DPMI_Error );
+      return( PAS_Error );
+      }
+
+   return( PAS_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/pas16.h
@@ -1,0 +1,81 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: PAS16.H
+
+   author: James R. Dose
+   date:   March 27, 1994
+
+   Public header for for PAS16.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __PAS16_H
+#define __PAS16_H
+
+enum PAS_ERRORS
+   {
+   PAS_Warning = -2,
+   PAS_Error   = -1,
+   PAS_Ok      = 0,
+   PAS_DriverNotFound,
+   PAS_DmaError,
+   PAS_InvalidIrq,
+   PAS_UnableToSetIrq,
+   PAS_Dos4gwIrqError,
+   PAS_NoSoundPlaying,
+   PAS_CardNotFound,
+   PAS_DPMI_Error,
+   PAS_OutOfMemory
+   };
+
+#define PAS_MaxMixMode        STEREO_16BIT
+#define PAS_DefaultSampleRate 11000
+#define PAS_DefaultMixMode    MONO_8BIT
+#define PAS_MaxIrq            15
+
+#define PAS_MinSamplingRate   4000
+#define PAS_MaxSamplingRate   44000
+
+extern unsigned int PAS_DMAChannel;
+
+char *PAS_ErrorString( int ErrorNumber );
+void  PAS_SetPlaybackRate( unsigned rate );
+unsigned PAS_GetPlaybackRate( void );
+int   PAS_SetMixMode( int mode );
+void  PAS_StopPlayback( void );
+int   PAS_GetCurrentPos( void );
+int   PAS_BeginBufferedPlayback( char *BufferStart, int BufferSize, int NumDivisions, unsigned SampleRate, int MixMode, void ( *CallBackFunc )( void ) );
+int   PAS_BeginBufferedRecord( char *BufferStart, int BufferSize, int NumDivisions, unsigned SampleRate, int MixMode, void ( *CallBackFunc )( void ) );
+int   PAS_SetPCMVolume( int volume );
+int   PAS_GetPCMVolume( void );
+void  PAS_SetFMVolume( int volume );
+int   PAS_GetFMVolume( void );
+int   PAS_GetCardInfo( int *MaxSampleBits, int *MaxChannels );
+void  PAS_SetCallBack( void ( *func )( void ) );
+int   PAS_SaveMusicVolume( void );
+void  PAS_RestoreMusicVolume( void );
+int   PAS_Init( void );
+void  PAS_Shutdown( void );
+void  PAS_UnlockMemory( void );
+int   PAS_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/pitch.c
@@ -1,0 +1,258 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: PITCH.C
+
+   author: James R. Dose
+   date:   June 14, 1993
+
+   Routines for pitch scaling.
+
+   (c) Copyright 1993 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <stdlib.h>
+//#include <math.h>
+#include "dpmi.h"
+#include "standard.h"
+#include "pitch.h"
+
+#define MAXDETUNE 25
+
+static unsigned long PitchTable[ 12 ][ MAXDETUNE ] =
+   {
+      { 0x10000, 0x10097, 0x1012f, 0x101c7, 0x10260, 0x102f9, 0x10392, 0x1042c,
+      0x104c6, 0x10561, 0x105fb, 0x10696, 0x10732, 0x107ce, 0x1086a, 0x10907,
+      0x109a4, 0x10a41, 0x10adf, 0x10b7d, 0x10c1b, 0x10cba, 0x10d59, 0x10df8,
+      0x10e98 },
+      { 0x10f38, 0x10fd9, 0x1107a, 0x1111b, 0x111bd, 0x1125f, 0x11302, 0x113a5,
+      0x11448, 0x114eb, 0x1158f, 0x11634, 0x116d8, 0x1177e, 0x11823, 0x118c9,
+      0x1196f, 0x11a16, 0x11abd, 0x11b64, 0x11c0c, 0x11cb4, 0x11d5d, 0x11e06,
+      0x11eaf },
+      { 0x11f59, 0x12003, 0x120ae, 0x12159, 0x12204, 0x122b0, 0x1235c, 0x12409,
+      0x124b6, 0x12563, 0x12611, 0x126bf, 0x1276d, 0x1281c, 0x128cc, 0x1297b,
+      0x12a2b, 0x12adc, 0x12b8d, 0x12c3e, 0x12cf0, 0x12da2, 0x12e55, 0x12f08,
+      0x12fbc },
+      { 0x1306f, 0x13124, 0x131d8, 0x1328d, 0x13343, 0x133f9, 0x134af, 0x13566,
+      0x1361d, 0x136d5, 0x1378d, 0x13846, 0x138fe, 0x139b8, 0x13a72, 0x13b2c,
+      0x13be6, 0x13ca1, 0x13d5d, 0x13e19, 0x13ed5, 0x13f92, 0x1404f, 0x1410d,
+      0x141cb },
+      { 0x1428a, 0x14349, 0x14408, 0x144c8, 0x14588, 0x14649, 0x1470a, 0x147cc,
+      0x1488e, 0x14951, 0x14a14, 0x14ad7, 0x14b9b, 0x14c5f, 0x14d24, 0x14dea,
+      0x14eaf, 0x14f75, 0x1503c, 0x15103, 0x151cb, 0x15293, 0x1535b, 0x15424,
+      0x154ee },
+      { 0x155b8, 0x15682, 0x1574d, 0x15818, 0x158e4, 0x159b0, 0x15a7d, 0x15b4a,
+      0x15c18, 0x15ce6, 0x15db4, 0x15e83, 0x15f53, 0x16023, 0x160f4, 0x161c5,
+      0x16296, 0x16368, 0x1643a, 0x1650d, 0x165e1, 0x166b5, 0x16789, 0x1685e,
+      0x16934 },
+      { 0x16a09, 0x16ae0, 0x16bb7, 0x16c8e, 0x16d66, 0x16e3e, 0x16f17, 0x16ff1,
+      0x170ca, 0x171a5, 0x17280, 0x1735b, 0x17437, 0x17513, 0x175f0, 0x176ce,
+      0x177ac, 0x1788a, 0x17969, 0x17a49, 0x17b29, 0x17c09, 0x17cea, 0x17dcc,
+      0x17eae },
+      { 0x17f91, 0x18074, 0x18157, 0x1823c, 0x18320, 0x18406, 0x184eb, 0x185d2,
+      0x186b8, 0x187a0, 0x18888, 0x18970, 0x18a59, 0x18b43, 0x18c2d, 0x18d17,
+      0x18e02, 0x18eee, 0x18fda, 0x190c7, 0x191b5, 0x192a2, 0x19391, 0x19480,
+      0x1956f },
+      { 0x1965f, 0x19750, 0x19841, 0x19933, 0x19a25, 0x19b18, 0x19c0c, 0x19d00,
+      0x19df4, 0x19ee9, 0x19fdf, 0x1a0d5, 0x1a1cc, 0x1a2c4, 0x1a3bc, 0x1a4b4,
+      0x1a5ad, 0x1a6a7, 0x1a7a1, 0x1a89c, 0x1a998, 0x1aa94, 0x1ab90, 0x1ac8d,
+      0x1ad8b },
+      { 0x1ae89, 0x1af88, 0x1b088, 0x1b188, 0x1b289, 0x1b38a, 0x1b48c, 0x1b58f,
+      0x1b692, 0x1b795, 0x1b89a, 0x1b99f, 0x1baa4, 0x1bbaa, 0x1bcb1, 0x1bdb8,
+      0x1bec0, 0x1bfc9, 0x1c0d2, 0x1c1dc, 0x1c2e6, 0x1c3f1, 0x1c4fd, 0x1c609,
+      0x1c716 },
+      { 0x1c823, 0x1c931, 0x1ca40, 0x1cb50, 0x1cc60, 0x1cd70, 0x1ce81, 0x1cf93,
+      0x1d0a6, 0x1d1b9, 0x1d2cd, 0x1d3e1, 0x1d4f6, 0x1d60c, 0x1d722, 0x1d839,
+      0x1d951, 0x1da69, 0x1db82, 0x1dc9c, 0x1ddb6, 0x1ded1, 0x1dfec, 0x1e109,
+      0x1e225 },
+      { 0x1e343, 0x1e461, 0x1e580, 0x1e6a0, 0x1e7c0, 0x1e8e0, 0x1ea02, 0x1eb24,
+      0x1ec47, 0x1ed6b, 0x1ee8f, 0x1efb4, 0x1f0d9, 0x1f1ff, 0x1f326, 0x1f44e,
+      0x1f576, 0x1f69f, 0x1f7c9, 0x1f8f3, 0x1fa1e, 0x1fb4a, 0x1fc76, 0x1fda3,
+      0x1fed1 }
+   };
+
+
+//static int PITCH_Installed = FALSE;
+
+
+/*---------------------------------------------------------------------
+   Function: PITCH_Init
+
+   Initializes pitch table.
+---------------------------------------------------------------------*/
+/*
+void PITCH_Init
+   (
+   void
+   )
+
+   {
+   int note;
+   int detune;
+
+   if ( !PITCH_Installed )
+      {
+      for( note = 0; note < 12; note++ )
+         {
+         for( detune = 0; detune < MAXDETUNE; detune++ )
+            {
+            PitchTable[ note ][ detune ] = 0x10000 *
+               pow( 2, ( note * MAXDETUNE + detune ) / ( 12.0 * MAXDETUNE ) );
+            }
+         }
+
+      PITCH_Installed = TRUE;
+      }
+   }
+*/
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define PITCH_LockStart PITCH_GetScale
+
+
+/*---------------------------------------------------------------------
+   Function: PITCH_GetScale
+
+   Returns a fixed-point value to scale number the specified amount.
+---------------------------------------------------------------------*/
+
+unsigned long PITCH_GetScale
+   (
+   int pitchoffset
+   )
+
+   {
+   unsigned long scale;
+   int octaveshift;
+   int noteshift;
+   int note;
+   int detune;
+
+//   if ( !PITCH_Installed )
+//      {
+//      PITCH_Init();
+//      }
+
+   if ( pitchoffset == 0 )
+      {
+      return( PitchTable[ 0 ][ 0 ] );
+      }
+
+   noteshift = pitchoffset % 1200;
+   if ( noteshift < 0 )
+      {
+      noteshift += 1200;
+      }
+
+   note   = noteshift / 100;
+   detune = ( noteshift % 100 ) / ( 100 / MAXDETUNE );
+   octaveshift = ( pitchoffset - noteshift ) / 1200;
+
+   if ( detune < 0 )
+      {
+      detune += ( 100 / MAXDETUNE );
+      note--;
+      if ( note < 0 )
+         {
+         note += 12;
+         octaveshift--;
+         }
+      }
+
+   scale = PitchTable[ note ][ detune ];
+
+   if ( octaveshift < 0 )
+      {
+      scale >>= -octaveshift;
+      }
+   else
+      {
+      scale <<= octaveshift;
+      }
+
+   return( scale );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PITCH_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void PITCH_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PITCH_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void PITCH_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( PITCH_LockStart, PITCH_LockEnd );
+   DPMI_Unlock( PitchTable );
+//   DPMI_Unlock( PITCH_Installed );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: PITCH_LockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+int PITCH_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( PITCH_LockStart, PITCH_LockEnd );
+   status |= DPMI_Lock( PitchTable );
+//   status |= DPMI_Lock( PITCH_Installed );
+
+   if ( status != DPMI_Ok )
+      {
+      PITCH_UnlockMemory();
+      return( PITCH_Error );
+      }
+
+   return( PITCH_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/pitch.h
@@ -1,0 +1,45 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: PITCH.H
+
+   author: James R. Dose
+   date:   June 14, 1994
+
+   Public header for PITCH.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __PITCH_H
+#define __PITCH_H
+
+enum PITCH_ERRORS
+   {
+   PITCH_Warning = -2,
+   PITCH_Error = -1,
+   PITCH_Ok = 0,
+   };
+
+//void          PITCH_Init( void );
+unsigned long PITCH_GetScale( int pitchoffset );
+void          PITCH_UnlockMemory( void );
+int           PITCH_LockMemory( void );
+#endif
--- /dev/null
+++ b/Game/src/audiolib/sndcards.h
@@ -1,0 +1,55 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: SNDCARDS.H
+
+   author: James R. Dose
+   date:   March 31, 1994
+
+   Contains enumerated type definitions for sound cards.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __SNDCARDS_H
+#define __SNDCARDS_H
+
+#define ASS_VERSION_STRING "1.12"
+
+typedef enum
+   {
+//   ASS_NoSound,
+   SoundBlaster,
+   ProAudioSpectrum,
+   SoundMan16,
+   Adlib,
+   GenMidi,
+   SoundCanvas,
+   Awe32,
+   WaveBlaster,
+   SoundScape,
+   UltraSound,
+   SoundSource,
+   TandySoundSource,
+   PC,
+   NumSoundCards
+   } soundcardnames;
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/sndscape.c
@@ -1,0 +1,1661 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: SNDSCAPE.C
+
+   author: James R. Dose
+   date:   October 25, 1994
+
+   Low level routines to support the Ensoniq Soundscape.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "interrup.h"
+#include "dpmi.h"
+#include "dma.h"
+#include "irq.h"
+#include "sndscape.h"
+#include "_sndscap.h"
+
+const int SOUNDSCAPE_Interrupts[ SOUNDSCAPE_MaxIrq + 1 ]  =
+   {
+   INVALID, INVALID,     0xa, INVALID,
+   INVALID,     0xd, INVALID,     0xf,
+   INVALID, INVALID,    0x72, INVALID,
+   INVALID, INVALID, INVALID, INVALID
+   };
+
+const int SOUNDSCAPE_SampleSize[ SOUNDSCAPE_MaxMixMode + 1 ] =
+   {
+   MONO_8BIT_SAMPLE_SIZE,  STEREO_8BIT_SAMPLE_SIZE,
+   MONO_16BIT_SAMPLE_SIZE, STEREO_16BIT_SAMPLE_SIZE
+   };
+
+static void ( __interrupt __far *SOUNDSCAPE_OldInt )( void );
+
+static int SOUNDSCAPE_Installed = FALSE;
+static int SOUNDSCAPE_FoundCard = FALSE;
+
+static char   *SOUNDSCAPE_DMABuffer;
+static char   *SOUNDSCAPE_DMABufferEnd;
+static char   *SOUNDSCAPE_CurrentDMABuffer;
+static int     SOUNDSCAPE_TotalDMABufferSize;
+
+static int      SOUNDSCAPE_TransferLength   = 0;
+static int      SOUNDSCAPE_MixMode          = SOUNDSCAPE_DefaultMixMode;
+static int      SOUNDSCAPE_SamplePacketSize = MONO_16BIT_SAMPLE_SIZE;
+static unsigned SOUNDSCAPE_SampleRate       = SOUNDSCAPE_DefaultSampleRate;
+
+volatile int   SOUNDSCAPE_SoundPlaying;
+
+void ( *SOUNDSCAPE_CallBack )( void );
+
+static int  SOUNDSCAPE_IntController1Mask;
+static int  SOUNDSCAPE_IntController2Mask;
+
+// some globals for chip type, ports, DMA, IRQs ... and stuff
+static struct
+   {
+   int BasePort;  // base address of the Ensoniq gate-array chip
+   int WavePort;  // the AD-1848 base address
+   int DMAChan;   // the DMA channel used for PCM
+   int WaveIRQ;   // the PCM IRQ
+   int MIDIIRQ;   // the MPU-401 IRQ
+   int ChipID;    // the Ensoniq chip type
+   int SBEmul;    // SoundBlaster emulation flag
+   int CDROM;     // CD-ROM flag
+   int IRQIndx;   // the Wave IRQ index - for hardware regs
+   int OldIRQs;   // Old IRQs flag to support older HW
+   } SOUNDSCAPE_Config;
+
+// adequate stack size
+#define kStackSize 2048
+
+static unsigned short StackSelector = NULL;
+static unsigned long  StackPointer;
+
+static unsigned short oldStackSelector;
+static unsigned long  oldStackPointer;
+
+// These declarations are necessary to use the inline assembly pragmas.
+
+extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
+extern void SetStack(unsigned short selector,unsigned long stackptr);
+
+// This function will get the current stack selector and pointer and save
+// them off.
+#pragma aux GetStack =	\
+	"mov  [edi],esp"		\
+	"mov	ax,ss"	 		\
+	"mov  [esi],ax" 		\
+	parm [esi] [edi]		\
+	modify [eax esi edi];
+
+// This function will set the stack selector and pointer to the specified
+// values.
+#pragma aux SetStack =	\
+	"mov  ss,ax"			\
+	"mov  esp,edx"			\
+	parm [ax] [edx]		\
+	modify [eax edx];
+
+int SOUNDSCAPE_DMAChannel = -1;
+
+int SOUNDSCAPE_ErrorCode = SOUNDSCAPE_Ok;
+
+#define SOUNDSCAPE_SetErrorCode( status ) \
+   SOUNDSCAPE_ErrorCode   = ( status );
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *SOUNDSCAPE_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case SOUNDSCAPE_Warning :
+      case SOUNDSCAPE_Error :
+         ErrorString = SOUNDSCAPE_ErrorString( SOUNDSCAPE_ErrorCode );
+         break;
+
+      case SOUNDSCAPE_Ok :
+         ErrorString = "SoundScape ok.";
+         break;
+
+      case SOUNDSCAPE_EnvNotFound :
+         ErrorString = "SNDSCAPE environment variable not set.  This is used to locate \n"
+                       "SNDSCAPE.INI which is used to describe your sound card setup.";
+         break;
+
+      case SOUNDSCAPE_InitFileNotFound :
+         ErrorString = "Missing SNDSCAPE.INI file for SoundScape.  This file should be \n"
+                       "located in the directory indicated by the SNDSCAPE environment \n"
+                       "variable or in 'C:\SNDSCAPE' if SNDSCAPE is not set.";
+         break;
+
+      case SOUNDSCAPE_MissingProductInfo :
+         ErrorString = "Missing 'Product' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingPortInfo :
+         ErrorString = "Missing 'Port' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingDMAInfo :
+         ErrorString = "Missing 'DMA' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingIRQInfo :
+         ErrorString = "Missing 'IRQ' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingSBIRQInfo :
+         ErrorString = "Missing 'SBIRQ' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingSBENABLEInfo :
+         ErrorString = "Missing 'SBEnable' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_MissingWavePortInfo :
+         ErrorString = "Missing 'WavePort' field in SNDSCAPE.INI file for SoundScape.";
+         break;
+
+      case SOUNDSCAPE_HardwareError :
+         ErrorString = "Could not detect SoundScape.  Make sure your SNDSCAPE.INI file \n"
+                       "contains correct information about your hardware setup.";
+         break;
+
+      case SOUNDSCAPE_NoSoundPlaying :
+         ErrorString = "No sound playing on SoundScape.";
+         break;
+
+      case SOUNDSCAPE_InvalidSBIrq :
+         ErrorString = "Invalid SoundScape Irq in SBIRQ field of SNDSCAPE.INI.";
+         break;
+
+      case SOUNDSCAPE_UnableToSetIrq :
+         ErrorString = "Unable to set SoundScape IRQ.  Try selecting an IRQ of 7 or below.";
+         break;
+
+      case SOUNDSCAPE_DmaError :
+         ErrorString = DMA_ErrorString( DMA_Error );
+         break;
+
+      case SOUNDSCAPE_DPMI_Error :
+         ErrorString = "DPMI Error in SoundScape.";
+         break;
+
+      case SOUNDSCAPE_OutOfMemory :
+         ErrorString = "Out of conventional memory in SoundScape.";
+         break;
+
+      default :
+         ErrorString = "Unknown SoundScape error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define SOUNDSCAPE_LockStart SOUNDSCAPE_EnableInterrupt
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_EnableInterrupt
+
+   Enables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+static void SOUNDSCAPE_EnableInterrupt
+   (
+   void
+   )
+
+   {
+   int mask;
+
+   // Unmask system interrupt
+   if ( SOUNDSCAPE_Config.WaveIRQ < 8 )
+      {
+      mask = inp( 0x21 ) & ~( 1 << SOUNDSCAPE_Config.WaveIRQ );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask = inp( 0xA1 ) & ~( 1 << ( SOUNDSCAPE_Config.WaveIRQ - 8 ) );
+      outp( 0xA1, mask  );
+
+      mask = inp( 0x21 ) & ~( 1 << 2 );
+      outp( 0x21, mask  );
+      }
+
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_DisableInterrupt
+
+   Disables the triggering of the sound card interrupt.
+---------------------------------------------------------------------*/
+
+static void SOUNDSCAPE_DisableInterrupt
+   (
+   void
+   )
+
+   {
+   int mask;
+
+   // Restore interrupt mask
+   if ( SOUNDSCAPE_Config.WaveIRQ < 8 )
+      {
+      mask  = inp( 0x21 ) & ~( 1 << SOUNDSCAPE_Config.WaveIRQ );
+      mask |= SOUNDSCAPE_IntController1Mask & ( 1 << SOUNDSCAPE_Config.WaveIRQ );
+      outp( 0x21, mask  );
+      }
+   else
+      {
+      mask  = inp( 0x21 ) & ~( 1 << 2 );
+      mask |= SOUNDSCAPE_IntController1Mask & ( 1 << 2 );
+      outp( 0x21, mask  );
+
+      mask  = inp( 0xA1 ) & ~( 1 << ( SOUNDSCAPE_Config.WaveIRQ - 8 ) );
+      mask |= SOUNDSCAPE_IntController2Mask & ( 1 << ( SOUNDSCAPE_Config.WaveIRQ - 8 ) );
+      outp( 0xA1, mask  );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_ServiceInterrupt
+
+   Handles interrupt generated by sound card at the end of a voice
+   transfer.  Calls the user supplied callback function.
+---------------------------------------------------------------------*/
+
+static void __interrupt __far SOUNDSCAPE_ServiceInterrupt
+   (
+   void
+   )
+
+   {
+   // save stack
+   GetStack( &oldStackSelector, &oldStackPointer );
+
+   // set our stack
+   SetStack( StackSelector, StackPointer );
+
+	if ( !( inp( SOUNDSCAPE_Config.WavePort + AD_STATUS ) & 0x01 ) )
+      {
+      // restore stack
+      SetStack( oldStackSelector, oldStackPointer );
+
+      // Wasn't our interrupt.  Call the old one.
+      _chain_intr( SOUNDSCAPE_OldInt );
+      }
+
+   // clear the AD-1848 interrupt
+	outp( SOUNDSCAPE_Config.WavePort + AD_STATUS, 0x00 );
+
+   // Keep track of current buffer
+   SOUNDSCAPE_CurrentDMABuffer += SOUNDSCAPE_TransferLength;
+   if ( SOUNDSCAPE_CurrentDMABuffer >= SOUNDSCAPE_DMABufferEnd )
+      {
+      SOUNDSCAPE_CurrentDMABuffer = SOUNDSCAPE_DMABuffer;
+      }
+
+   // Call the caller's callback function
+   if ( SOUNDSCAPE_CallBack != NULL )
+      {
+      SOUNDSCAPE_CallBack();
+      }
+
+   // restore stack
+   SetStack( oldStackSelector, oldStackPointer );
+
+   // send EOI to Interrupt Controller
+   if ( SOUNDSCAPE_Config.WaveIRQ > 7 )
+      {
+      outp( 0xA0, 0x20 );
+      }
+   outp( 0x20, 0x20 );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ga_read
+
+   Reads Ensoniq indirect registers.
+---------------------------------------------------------------------*/
+
+static int ga_read
+   (
+   int rnum
+   )
+
+   {
+   int data;
+
+   outp( SOUNDSCAPE_Config.BasePort + GA_REGADDR, rnum );
+   data = inp( SOUNDSCAPE_Config.BasePort + GA_REGDATA );
+   return( data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ga_write
+
+   Writes to Ensoniq indirect registers.
+---------------------------------------------------------------------*/
+
+static void ga_write
+   (
+   int rnum,
+   int value
+   )
+
+   {
+	outp( SOUNDSCAPE_Config.BasePort + GA_REGADDR, rnum );
+	outp( SOUNDSCAPE_Config.BasePort + GA_REGDATA, value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ad_read
+
+   Reads the AD-1848 indirect registers.  This function should not be
+   used while the AD-1848 mode change is enabled
+---------------------------------------------------------------------*/
+
+static int ad_read
+   (
+   int rnum
+   )
+
+   {
+   int data;
+
+	outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, rnum );
+	data = inp( SOUNDSCAPE_Config.WavePort + AD_REGDATA );
+   return( data );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: ad_write
+
+   Writes to the AD-1848 indirect registers.  This function should
+   not be used while the AD-1848 mode change is enabled.
+---------------------------------------------------------------------*/
+
+static void ad_write
+   (
+   int rnum,
+   int value
+   )
+
+   {
+   outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, rnum );
+   outp( SOUNDSCAPE_Config.WavePort + AD_REGDATA, value );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: tdelay
+
+   Delay function - 250ms - for AD-1848 re-synch and autocalibration.
+---------------------------------------------------------------------*/
+
+static void tdelay
+   (
+   void
+   )
+
+   {
+	long time;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+   _enable();
+	time = clock() + CLOCKS_PER_SEC/4;
+	while(clock() < time)
+		;
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: pcm_format
+
+   Sets the PCM data format.
+---------------------------------------------------------------------*/
+
+static void pcm_format
+   (
+   void
+   )
+
+   {
+	int format;
+
+	// build the register value based on format
+	format = 0;
+
+   switch( SOUNDSCAPE_SampleRate )
+      {
+      case 11025:
+         format = 0x03;
+         break;
+
+      case 22050:
+         format = 0x07;
+         break;
+
+      case 44100:
+         format = 0x0b;
+         break;
+
+      default:
+         // Set it to 11025 hz
+         format = 0x03;
+         break;
+      }
+
+	// set other format bits and format globals
+   if ( SOUNDSCAPE_MixMode & SIXTEEN_BIT )
+      {
+      format |= 0x40;
+      }
+
+   if ( SOUNDSCAPE_MixMode & STEREO )
+      {
+      format |= 0x10;
+      }
+
+	// enable mode change, point to format reg
+	outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, 0x40 | AD_FORMAT );
+
+	// write the format
+	outp( SOUNDSCAPE_Config.WavePort + AD_REGDATA, format );
+
+	// delay for internal re-synch
+	tdelay();
+
+	// exit mode change state
+	outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, 0x00 );
+
+	// delay for autocalibration
+	tdelay();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_SetPlaybackRate
+
+   Sets the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+void SOUNDSCAPE_SetPlaybackRate
+   (
+   unsigned rate
+   )
+
+   {
+   if ( rate < 20000 )
+      {
+      rate = 11025;
+      }
+   else if ( rate < 30000 )
+      {
+      rate = 22050;
+      }
+   else
+      {
+      rate = 44100;
+      }
+
+   SOUNDSCAPE_SampleRate = rate;
+
+   // Set the rate
+   pcm_format();
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_GetPlaybackRate
+
+   Returns the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+unsigned SOUNDSCAPE_GetPlaybackRate
+   (
+   void
+   )
+
+   {
+   return( SOUNDSCAPE_SampleRate );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_SetMixMode
+
+   Sets the sound card to play samples in mono or stereo.
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_SetMixMode
+   (
+   int mode
+   )
+
+   {
+   SOUNDSCAPE_MixMode = mode & SOUNDSCAPE_MaxMixMode;
+   SOUNDSCAPE_SamplePacketSize = SOUNDSCAPE_SampleSize[ SOUNDSCAPE_MixMode ];
+
+   // Set the mixmode
+   pcm_format();
+
+   return( mode );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_StopPlayback
+
+   Ends the DMA transfer of digitized sound to the sound card.
+---------------------------------------------------------------------*/
+
+void SOUNDSCAPE_StopPlayback
+   (
+   void
+   )
+
+   {
+   // Don't allow anymore interrupts
+   SOUNDSCAPE_DisableInterrupt();
+
+	/* stop the AD-1848 */
+	ad_write( AD_CONFIG, 0x00 );
+
+	/* let it finish it's cycles */
+	tdelay();
+
+   // Disable the DMA channel
+   DMA_EndTransfer( SOUNDSCAPE_Config.DMAChan );
+
+   SOUNDSCAPE_SoundPlaying = FALSE;
+
+   SOUNDSCAPE_DMABuffer = NULL;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_SetupDMABuffer
+
+   Programs the DMAC for sound transfer.
+---------------------------------------------------------------------*/
+
+static int SOUNDSCAPE_SetupDMABuffer
+   (
+   char *BufferPtr,
+   int   BufferSize,
+   int   mode
+   )
+
+   {
+   int DmaStatus;
+
+   DmaStatus = DMA_SetupTransfer( SOUNDSCAPE_Config.DMAChan, BufferPtr, BufferSize, mode );
+   if ( DmaStatus == DMA_Error )
+      {
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_DmaError );
+      return( SOUNDSCAPE_Error );
+      }
+
+   SOUNDSCAPE_DMAChannel         = SOUNDSCAPE_Config.DMAChan;
+   SOUNDSCAPE_DMABuffer          = BufferPtr;
+   SOUNDSCAPE_CurrentDMABuffer   = BufferPtr;
+   SOUNDSCAPE_TotalDMABufferSize = BufferSize;
+   SOUNDSCAPE_DMABufferEnd       = BufferPtr + BufferSize;
+
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_GetCurrentPos
+
+   Returns the offset within the current sound being played.
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_GetCurrentPos
+   (
+   void
+   )
+
+   {
+   char *CurrentAddr;
+   int   offset;
+
+   if ( !SOUNDSCAPE_SoundPlaying )
+      {
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_NoSoundPlaying );
+      return( SOUNDSCAPE_Error );
+      }
+
+   CurrentAddr = DMA_GetCurrentPos( SOUNDSCAPE_Config.DMAChan );
+
+   offset = ( int )( ( ( unsigned long )CurrentAddr ) -
+      ( ( unsigned long )SOUNDSCAPE_CurrentDMABuffer ) );
+
+   if ( SOUNDSCAPE_MixMode & SIXTEEN_BIT )
+      {
+      offset >>= 1;
+      }
+
+   if ( SOUNDSCAPE_MixMode & STEREO )
+      {
+      offset >>= 1;
+      }
+
+   return( offset );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_BeginPlayback
+
+   Starts playback of digitized sound.
+---------------------------------------------------------------------*/
+
+static int SOUNDSCAPE_BeginPlayback
+   (
+   int length
+   )
+
+   {
+   int SampleLength;
+   int LoByte;
+   int HiByte;
+
+   if ( SOUNDSCAPE_MixMode & SIXTEEN_BIT )
+      {
+      SampleLength = length / 2;
+      }
+   else
+      {
+      SampleLength = length;
+      }
+
+   if ( SOUNDSCAPE_MixMode & STEREO )
+      {
+      SampleLength >>= 1;
+      }
+
+   SampleLength--;
+
+	// setup the AD-1848 interrupt count
+	// set the interrupt count value based on the format.
+	// count will decrement every sample period and generate
+	// an interrupt when in rolls over. we want this always
+	// to be at every 1/2 buffer, regardless of the data format,
+	// so the count must be adjusted accordingly.
+   HiByte = hibyte( SampleLength );
+   LoByte = lobyte( SampleLength );
+	ad_write( AD_LCOUNT, LoByte );
+	ad_write( AD_UCOUNT, HiByte );
+
+	/* unmask the host DMA controller */
+   SOUNDSCAPE_EnableInterrupt();
+
+	/* start the AD-1848 */
+	ad_write(AD_CONFIG, 0x01);
+
+   SOUNDSCAPE_SoundPlaying = TRUE;
+
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_BeginBufferedPlayback
+
+   Begins multibuffered playback of digitized sound on the sound card.
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_BeginBufferedPlayback
+   (
+   char    *BufferStart,
+   int      BufferSize,
+   int      NumDivisions,
+   unsigned SampleRate,
+   int      MixMode,
+   void  ( *CallBackFunc )( void )
+   )
+
+   {
+   int DmaStatus;
+   int TransferLength;
+
+   if ( SOUNDSCAPE_SoundPlaying )
+      {
+      SOUNDSCAPE_StopPlayback();
+      }
+
+   SOUNDSCAPE_SetMixMode( MixMode );
+
+   DmaStatus = SOUNDSCAPE_SetupDMABuffer( BufferStart, BufferSize,
+      DMA_AutoInitRead );
+   if ( DmaStatus == SOUNDSCAPE_Error )
+      {
+      return( SOUNDSCAPE_Error );
+      }
+
+   SOUNDSCAPE_SetPlaybackRate( SampleRate );
+
+   SOUNDSCAPE_SetCallBack( CallBackFunc );
+
+   SOUNDSCAPE_EnableInterrupt();
+
+   TransferLength = BufferSize / NumDivisions;
+   SOUNDSCAPE_TransferLength = TransferLength;
+
+   SOUNDSCAPE_BeginPlayback( TransferLength );
+
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_GetCardInfo
+
+   Returns the maximum number of bits that can represent a sample
+   (8 or 16) and the number of channels (1 for mono, 2 for stereo).
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_GetCardInfo
+   (
+   int *MaxSampleBits,
+   int *MaxChannels
+   )
+
+   {
+   int status;
+
+   status = SOUNDSCAPE_FindCard();
+   if ( status == SOUNDSCAPE_Ok )
+      {
+      *MaxChannels = 2;
+      *MaxSampleBits = 16;
+      return( SOUNDSCAPE_Ok );
+      }
+
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_SetCallBack
+
+   Specifies the user function to call at the end of a sound transfer.
+---------------------------------------------------------------------*/
+
+void SOUNDSCAPE_SetCallBack
+   (
+   void ( *func )( void )
+   )
+
+   {
+   SOUNDSCAPE_CallBack = func;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void SOUNDSCAPE_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+static void SOUNDSCAPE_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( SOUNDSCAPE_LockStart, SOUNDSCAPE_LockEnd );
+   DPMI_Unlock( SOUNDSCAPE_Config );
+   DPMI_Unlock( SOUNDSCAPE_OldInt );
+   DPMI_Unlock( SOUNDSCAPE_Installed );
+   DPMI_Unlock( SOUNDSCAPE_DMABuffer );
+   DPMI_Unlock( SOUNDSCAPE_DMABufferEnd );
+   DPMI_Unlock( SOUNDSCAPE_CurrentDMABuffer );
+   DPMI_Unlock( SOUNDSCAPE_TotalDMABufferSize );
+   DPMI_Unlock( SOUNDSCAPE_TransferLength );
+   DPMI_Unlock( SOUNDSCAPE_MixMode );
+   DPMI_Unlock( SOUNDSCAPE_SamplePacketSize );
+   DPMI_Unlock( SOUNDSCAPE_SampleRate );
+   DPMI_Unlock( SOUNDSCAPE_SoundPlaying );
+   DPMI_Unlock( SOUNDSCAPE_CallBack );
+   DPMI_Unlock( SOUNDSCAPE_IntController1Mask );
+   DPMI_Unlock( SOUNDSCAPE_IntController2Mask );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+static int SOUNDSCAPE_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( SOUNDSCAPE_LockStart, SOUNDSCAPE_LockEnd );
+   status |= DPMI_Lock( SOUNDSCAPE_Config );
+   status |= DPMI_Lock( SOUNDSCAPE_OldInt );
+   status |= DPMI_Lock( SOUNDSCAPE_Installed );
+   status |= DPMI_Lock( SOUNDSCAPE_DMABuffer );
+   status |= DPMI_Lock( SOUNDSCAPE_DMABufferEnd );
+   status |= DPMI_Lock( SOUNDSCAPE_CurrentDMABuffer );
+   status |= DPMI_Lock( SOUNDSCAPE_TotalDMABufferSize );
+   status |= DPMI_Lock( SOUNDSCAPE_TransferLength );
+   status |= DPMI_Lock( SOUNDSCAPE_MixMode );
+   status |= DPMI_Lock( SOUNDSCAPE_SamplePacketSize );
+   status |= DPMI_Lock( SOUNDSCAPE_SampleRate );
+   status |= DPMI_Lock( SOUNDSCAPE_SoundPlaying );
+   status |= DPMI_Lock( SOUNDSCAPE_CallBack );
+   status |= DPMI_Lock( SOUNDSCAPE_IntController1Mask );
+   status |= DPMI_Lock( SOUNDSCAPE_IntController2Mask );
+
+   if ( status != DPMI_Ok )
+      {
+      SOUNDSCAPE_UnlockMemory();
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_DPMI_Error );
+      return( SOUNDSCAPE_Error );
+      }
+
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: allocateTimerStack
+
+   Allocate a block of memory from conventional (low) memory and return
+   the selector (which can go directly into a segment register) of the
+   memory block or 0 if an error occured.
+---------------------------------------------------------------------*/
+
+static unsigned short allocateTimerStack
+   (
+   unsigned short size
+   )
+
+   {
+   union REGS regs;
+
+   // clear all registers
+   memset( &regs, 0, sizeof( regs ) );
+
+   // DPMI allocate conventional memory
+   regs.w.ax = 0x100;
+
+   // size in paragraphs
+   regs.w.bx = ( size + 15 ) / 16;
+
+   int386( 0x31, &regs, &regs );
+   if (!regs.w.cflag)
+      {
+      // DPMI call returns selector in dx
+      // (ax contains real mode segment
+      // which is ignored here)
+
+      return( regs.w.dx );
+      }
+
+   // Couldn't allocate memory.
+   return( NULL );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: deallocateTimerStack
+
+   Deallocate a block of conventional (low) memory given a selector to
+   it.  Assumes the block was allocated with DPMI function 0x100.
+---------------------------------------------------------------------*/
+
+static void deallocateTimerStack
+   (
+   unsigned short selector
+   )
+
+   {
+   union REGS regs;
+
+   if ( selector != NULL )
+      {
+      // clear all registers
+      memset( &regs, 0, sizeof( regs ) );
+
+      regs.w.ax = 0x101;
+      regs.w.dx = selector;
+      int386( 0x31, &regs, &regs );
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: parse
+
+   Parses for the right hand string of an .INI file equate.
+---------------------------------------------------------------------*/
+
+static int parse
+   (
+   char *val,
+   char *str,
+   FILE *p1
+   )
+
+   {
+	int  i;
+	int  j;
+   char tmpstr[ 81 ];
+
+	rewind( p1 );
+
+	while( !feof( p1 ) )
+      {
+      // get a new string
+      fgets( tmpstr, 81, p1 );
+      if( ( tmpstr[ 0 ] == '[' ) || ( tmpstr[ 0 ] == ';' ) ||
+         ( tmpstr[ 0 ] == '\n' ) )
+         {
+         continue;
+         }
+
+		// parse up to the '='
+      i = 0;
+      while( ( tmpstr[ i ] != '=' ) && ( tmpstr[ i ] != '\n' ) )
+         {
+         i++;
+         }
+
+		if( tmpstr[ i ] != '=' )
+         {
+         continue;
+         }
+
+		tmpstr[ i ] = '\0';
+
+		// see if it's the one we want
+		if ( strcmp( tmpstr, str ) )
+         {
+         continue;
+         }
+
+		// copy the right hand value to the destination string
+      i++;
+		for( j = 0; j < 32; j++ )
+         {
+         if ( ( tmpstr[ i ] == ' ' ) || ( tmpstr[ i ] == '\t' ) ||
+            ( tmpstr[ i ] == ',' ) || ( tmpstr[ i ] == '\n' ) )
+            {
+            break;
+            }
+
+			val[ j ] = tmpstr[ i ];
+         i++;
+         }
+		val[j] = '\0';
+
+      return( TRUE );
+      }
+
+   return( FALSE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_FindCard
+
+   Determines if a SoundScape is present and where it is located.
+---------------------------------------------------------------------*/
+
+static int SOUNDSCAPE_FindCard
+   (
+   void
+   )
+
+   {
+   int   found;
+   int   status;
+   int   tmp;
+	char *cp;
+   char  str[ 33 ];
+	FILE *fp;
+
+   if ( SOUNDSCAPE_FoundCard )
+      {
+      return( SOUNDSCAPE_Ok );
+      }
+
+	cp = getenv( "SNDSCAPE" );
+   if ( cp == NULL )
+      {
+      strcpy( str, "C:\\SNDSCAPE" );
+      }
+   else
+      {
+      strcpy( str, cp );
+      }
+
+   strcat(str, "\\SNDSCAPE.INI");
+
+	fp = fopen( str, "r" );
+   if ( fp == NULL )
+      {
+      if ( cp == NULL )
+         {
+         SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_EnvNotFound );
+         return( SOUNDSCAPE_Error );
+         }
+
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_InitFileNotFound );
+      return( SOUNDSCAPE_Error );
+      }
+
+	found = parse( str, "Product", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingProductInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	if( strstr( str, "SoundFX" ) == NULL )
+      {
+      SOUNDSCAPE_Config.OldIRQs = FALSE;
+      }
+	else
+      {
+		SOUNDSCAPE_Config.OldIRQs = TRUE;
+      }
+
+	found = parse( str, "Port", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingPortInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	SOUNDSCAPE_Config.BasePort = strtol( str, ( char ** )0, 16);
+
+	found = parse( str, "DMA", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingDMAInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	SOUNDSCAPE_Config.DMAChan = ( int )strtol( str, ( char ** )0, 10 );
+   status = DMA_VerifyChannel( SOUNDSCAPE_Config.DMAChan );
+   if ( status == DMA_Error )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_DmaError );
+      return( SOUNDSCAPE_Error );
+      }
+
+	found = parse( str, "IRQ", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingIRQInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	SOUNDSCAPE_Config.MIDIIRQ = ( int )strtol( str, ( char ** )0, 10 );
+   if ( SOUNDSCAPE_Config.MIDIIRQ == 2 )
+      {
+      SOUNDSCAPE_Config.MIDIIRQ = 9;
+      }
+
+	found = parse( str, "SBIRQ", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingSBIRQInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	SOUNDSCAPE_Config.WaveIRQ = ( int )strtol( str, ( char ** )0, 10 );
+	if ( SOUNDSCAPE_Config.WaveIRQ == 2 )
+      {
+		SOUNDSCAPE_Config.WaveIRQ = 9;
+      }
+
+   if ( !VALID_IRQ( SOUNDSCAPE_Config.WaveIRQ ) )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_InvalidSBIrq );
+      return( SOUNDSCAPE_Error );
+      }
+
+   if ( SOUNDSCAPE_Interrupts[ SOUNDSCAPE_Config.WaveIRQ ] == INVALID )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_InvalidSBIrq );
+      return( SOUNDSCAPE_Error );
+      }
+
+	found = parse( str, "SBEnable", fp );
+   if ( !found )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingSBENABLEInfo );
+      return( SOUNDSCAPE_Error );
+      }
+
+	if( !strcmp( str, "false" ) )
+      {
+      SOUNDSCAPE_Config.SBEmul = FALSE;
+      }
+	else
+      {
+      SOUNDSCAPE_Config.SBEmul = TRUE;
+      }
+
+	// do a hardware test
+	outp( SOUNDSCAPE_Config.BasePort + GA_REGADDR, 0x00f5 );
+	tmp = inp( SOUNDSCAPE_Config.BasePort + GA_REGADDR );
+	if ( ( tmp & 0x000f ) != 0x0005 )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_HardwareError );
+      return( SOUNDSCAPE_Error );
+      }
+
+	if( ( tmp & 0x00f0 ) == 0x00f0 )
+      {
+      fclose( fp );
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_HardwareError );
+      return( SOUNDSCAPE_Error );
+      }
+
+	// formulate the chip ID
+	tmp >>= 4;
+	if( tmp == 0 )
+      {
+      SOUNDSCAPE_Config.ChipID = ODIE;
+      }
+	else if ( !( tmp & 0x0008 ) )
+      {
+		SOUNDSCAPE_Config.ChipID = OPUS;
+      }
+	else
+      {
+		SOUNDSCAPE_Config.ChipID = MMIC;
+      }
+
+	// parse for the AD-1848 address if necessary
+	if( SOUNDSCAPE_Config.ChipID == ODIE )
+      {
+      found = parse( str, "WavePort", fp );
+      if ( !found )
+         {
+         fclose( fp );
+         SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_MissingWavePortInfo );
+         return( SOUNDSCAPE_Error );
+         }
+
+      SOUNDSCAPE_Config.WavePort = strtol( str, ( char ** )0, 16 );
+      }
+	else
+      {
+      // otherwise, the base address is fixed
+      SOUNDSCAPE_Config.WavePort = SOUNDSCAPE_Config.BasePort + AD_OFFSET;
+      }
+
+	// we're done with the file
+	fclose( fp );
+
+	// if it's an ODIE board, note CD-ROM decode enable
+	if ( SOUNDSCAPE_Config.ChipID == ODIE )
+      {
+		SOUNDSCAPE_Config.CDROM = ga_read( GA_CDCFG ) & 0x80;
+      }
+
+	// build the Wave IRQ index value
+	if( !SOUNDSCAPE_Config.OldIRQs )
+      {
+      switch( SOUNDSCAPE_Config.WaveIRQ )
+         {
+         case 9 :
+            SOUNDSCAPE_Config.IRQIndx = 0;
+            break;
+
+         case 5 :
+            SOUNDSCAPE_Config.IRQIndx = 1;
+            break;
+
+         case 7 :
+            SOUNDSCAPE_Config.IRQIndx = 2;
+            break;
+
+         default :
+            SOUNDSCAPE_Config.IRQIndx = 3;
+            break;
+         }
+      }
+   else
+      {
+      switch( SOUNDSCAPE_Config.WaveIRQ )
+         {
+         case 9 :
+            SOUNDSCAPE_Config.IRQIndx = 0;
+            break;
+
+         case 5 :
+            SOUNDSCAPE_Config.IRQIndx = 2;
+            break;
+
+         case 7 :
+            SOUNDSCAPE_Config.IRQIndx = 1;
+            break;
+
+         default :
+            SOUNDSCAPE_Config.IRQIndx = 3;
+            break;
+         }
+      }
+
+   SOUNDSCAPE_FoundCard = TRUE;
+   SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_Ok );
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_Setup
+
+   Setup the Soundscape card for native mode PCM.
+---------------------------------------------------------------------*/
+
+static int SOUNDSCAPE_Setup
+   (
+   void
+   )
+
+   {
+   int tmp;
+   int Interrupt;
+   int status;
+
+	// if necessary, clear any pending SB ints
+	if ( SOUNDSCAPE_Config.SBEmul )
+      {
+      inp( SB_IACK );
+      }
+
+   SOUNDSCAPE_DisableInterrupt();
+
+	// make sure the AD-1848 is not running
+	if ( ad_read( AD_CONFIG ) & 0x01 )
+      {
+      SOUNDSCAPE_StopPlayback();
+      }
+
+	// if necessary, do some signal re-routing
+	if( SOUNDSCAPE_Config.ChipID != MMIC )
+      {
+      // get the gate-array off of the DMA channel
+		ga_write( GA_DMACHB, 0x20 );
+
+      if ( !SOUNDSCAPE_Config.OldIRQs )
+         {
+         switch( SOUNDSCAPE_Config.MIDIIRQ )
+            {
+            case 5 :
+               tmp = 1;
+               break;
+
+            case 7 :
+               tmp = 2;
+               break;
+
+            case 9 :
+               tmp = 0;
+               break;
+
+            default :
+               tmp = 3;
+               break;
+            }
+         }
+      else
+         {
+         switch( SOUNDSCAPE_Config.MIDIIRQ )
+            {
+            case 5 :
+               tmp = 2;
+               break;
+
+            case 7 :
+               tmp = 1;
+               break;
+
+            case 9 :
+               tmp = 0;
+               break;
+
+            default :
+               tmp = 3;
+               break;
+            }
+         }
+
+      // set HostIRQ to MIDIIRQ for now
+      ga_write( GA_INTCFG, 0xf0 | ( tmp << 2 ) | tmp );
+
+      // now, route the AD-1848 stuff ...
+		if ( SOUNDSCAPE_Config.ChipID == OPUS )
+         {
+         // set the AD-1848 chip decode
+         ga_write( GA_HMCTL, ( ga_read( GA_HMCTL ) & 0xcf ) | 0x10 );
+         }
+      // setup the DMA polarity
+		ga_write( GA_DMACFG, 0x50 );
+
+		// init the CD-ROM (AD-1848) config register
+		ga_write( GA_CDCFG, 0x89 | ( SOUNDSCAPE_Config.DMAChan << 4 ) | ( SOUNDSCAPE_Config.IRQIndx << 1 ) );
+
+      // enable mode change, point to config reg
+		outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, 0x40 | AD_CONFIG );
+
+      // set interf cnfg reg for DMA mode, single chan, autocal on
+		outp( SOUNDSCAPE_Config.WavePort + AD_REGDATA, 0x0c );
+
+      // exit mode change state
+		outp( SOUNDSCAPE_Config.WavePort + AD_REGADDR, 0x00 );
+
+      // delay for autocalibration
+      tdelay();
+      }
+
+   // Install our interrupt handler
+   Interrupt = SOUNDSCAPE_Interrupts[ SOUNDSCAPE_Config.WaveIRQ ];
+   SOUNDSCAPE_OldInt = _dos_getvect( Interrupt );
+   if ( SOUNDSCAPE_Config.WaveIRQ < 8 )
+      {
+      _dos_setvect( Interrupt, SOUNDSCAPE_ServiceInterrupt );
+      }
+   else
+      {
+      status = IRQ_SetVector( Interrupt, SOUNDSCAPE_ServiceInterrupt );
+      if ( status != IRQ_Ok )
+         {
+         SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_UnableToSetIrq );
+         return( SOUNDSCAPE_Error );
+         }
+      }
+
+	// max left and right volumes
+	ad_write( AD_LEFTOUT, 0 );
+	ad_write( AD_RIGHTOUT, 0 );
+
+	// clear any pending interrupt condition
+	outp( SOUNDSCAPE_Config.WavePort + AD_STATUS, 0x00 );
+
+	// enable the interrupt pin
+	ad_write( AD_PINCTRL, ad_read( AD_PINCTRL ) | 0x02 );
+
+   SOUNDSCAPE_EnableInterrupt();
+
+   SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_Ok );
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_GetMIDIPort
+
+   Gets the address of the SoundScape MIDI port.
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_GetMIDIPort
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status = SOUNDSCAPE_FindCard();
+   if ( status != SOUNDSCAPE_Ok )
+      {
+      return( status );
+      }
+
+   return( SOUNDSCAPE_Config.BasePort );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_Init
+
+   Initializes the sound card and prepares the module to play
+   digitized sounds.
+---------------------------------------------------------------------*/
+
+int SOUNDSCAPE_Init
+   (
+   void
+   )
+
+   {
+   int status;
+
+   if ( SOUNDSCAPE_Installed )
+      {
+      SOUNDSCAPE_Shutdown();
+      }
+
+   // Save the interrupt masks
+   SOUNDSCAPE_IntController1Mask = inp( 0x21 );
+   SOUNDSCAPE_IntController2Mask = inp( 0xA1 );
+
+   SOUNDSCAPE_SoundPlaying = FALSE;
+   SOUNDSCAPE_SetCallBack( NULL );
+   SOUNDSCAPE_DMABuffer = NULL;
+
+   status = SOUNDSCAPE_FindCard();
+   if ( status != SOUNDSCAPE_Ok )
+      {
+      return( status );
+      }
+
+   status = SOUNDSCAPE_LockMemory();
+   if ( status != SOUNDSCAPE_Ok )
+      {
+      SOUNDSCAPE_UnlockMemory();
+      return( status );
+      }
+
+   StackSelector = allocateTimerStack( kStackSize );
+   if ( StackSelector == NULL )
+      {
+      SOUNDSCAPE_UnlockMemory();
+      SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_OutOfMemory );
+      return( SOUNDSCAPE_Error );
+      }
+
+   // Leave a little room at top of stack just for the hell of it...
+   StackPointer = kStackSize - sizeof( long );
+
+   SOUNDSCAPE_Installed = TRUE;
+
+   status = SOUNDSCAPE_Setup();
+   if ( status != SOUNDSCAPE_Ok )
+      {
+      SOUNDSCAPE_Shutdown();
+      return( status );
+      }
+
+//	printf("Testing DMA and IRQ ...\n");
+//	if( test_dma_irq() )
+//      {
+//      printf("\t\007Hardware Not Responding\n\n");
+//      close_soundscape();
+//      return( SOUNDSCAPE_Error );
+//      }
+
+   SOUNDSCAPE_SetPlaybackRate( SOUNDSCAPE_DefaultSampleRate );
+   SOUNDSCAPE_SetMixMode( SOUNDSCAPE_DefaultMixMode );
+
+   SOUNDSCAPE_SetErrorCode( SOUNDSCAPE_Ok );
+   return( SOUNDSCAPE_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SOUNDSCAPE_Shutdown
+
+   Ends transfer of sound data to the sound card and restores the
+   system resources used by the card.
+---------------------------------------------------------------------*/
+
+void SOUNDSCAPE_Shutdown
+   (
+   void
+   )
+
+   {
+   int Interrupt;
+
+   // Halt the DMA transfer
+   SOUNDSCAPE_StopPlayback();
+
+	// disable the AD-1848 interrupt pin
+	ad_write( AD_PINCTRL, ad_read( AD_PINCTRL ) & 0xfd );
+
+	// if necessary, do some signal re-routing
+	if ( SOUNDSCAPE_Config.ChipID != MMIC )
+      {
+		// re-init the CD-ROM (AD-1848) config register as needed.
+		// this will disable the AD-1848 interface.
+		if ( SOUNDSCAPE_Config.ChipID == ODIE )
+         {
+         ga_write( GA_CDCFG, SOUNDSCAPE_Config.CDROM );
+         }
+		else
+         {
+         ga_write( GA_CDCFG, ga_read( GA_CDCFG ) & 0x7f);
+         }
+
+		// if necessary, reset the SoundBlaster IRQ
+		if ( SOUNDSCAPE_Config.SBEmul )
+         {
+         ga_write( GA_INTCFG, ( ga_read( GA_INTCFG ) & 0xf3 ) |
+            ( SOUNDSCAPE_Config.IRQIndx << 2 ) );
+         }
+
+      // re-assign the gate-array DMA channel
+		ga_write( GA_DMACHB, 0x80 | ( SOUNDSCAPE_Config.DMAChan << 4 ) );
+      }
+
+   // Restore the original interrupt
+   Interrupt = SOUNDSCAPE_Interrupts[ SOUNDSCAPE_Config.WaveIRQ ];
+   if ( SOUNDSCAPE_Config.WaveIRQ >= 8 )
+      {
+      IRQ_RestoreVector( Interrupt );
+      }
+   _dos_setvect( Interrupt, SOUNDSCAPE_OldInt );
+
+   SOUNDSCAPE_SoundPlaying = FALSE;
+
+   SOUNDSCAPE_DMABuffer = NULL;
+
+   SOUNDSCAPE_SetCallBack( NULL );
+
+   SOUNDSCAPE_UnlockMemory();
+
+   if ( StackSelector != NULL )
+      {
+      deallocateTimerStack( StackSelector );
+      StackSelector = NULL;
+      }
+
+   SOUNDSCAPE_Installed = FALSE;
+   }
--- /dev/null
+++ b/Game/src/audiolib/sndscape.h
@@ -1,0 +1,73 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: SNDSCAPE.H
+
+   author: James R. Dose
+   date:   October 26, 1994
+
+   Public header for SNDSCAPE.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __SNDSCAPE_H
+#define __SNDSCAPE_H
+
+extern int SOUNDSCAPE_DMAChannel;
+extern int SOUNDSCAPE_ErrorCode;
+
+enum SOUNDSCAPE_ERRORS
+   {
+   SOUNDSCAPE_Warning = -2,
+   SOUNDSCAPE_Error = -1,
+   SOUNDSCAPE_Ok = 0,
+   SOUNDSCAPE_EnvNotFound,
+   SOUNDSCAPE_InitFileNotFound,
+   SOUNDSCAPE_MissingProductInfo,
+   SOUNDSCAPE_MissingPortInfo,
+   SOUNDSCAPE_MissingDMAInfo,
+   SOUNDSCAPE_MissingIRQInfo,
+   SOUNDSCAPE_MissingSBIRQInfo,
+   SOUNDSCAPE_MissingSBENABLEInfo,
+   SOUNDSCAPE_MissingWavePortInfo,
+   SOUNDSCAPE_HardwareError,
+   SOUNDSCAPE_NoSoundPlaying,
+   SOUNDSCAPE_InvalidSBIrq,
+   SOUNDSCAPE_UnableToSetIrq,
+   SOUNDSCAPE_DmaError,
+   SOUNDSCAPE_DPMI_Error,
+   SOUNDSCAPE_OutOfMemory
+   };
+
+char    *SOUNDSCAPE_ErrorString( int ErrorNumber );
+void     SOUNDSCAPE_SetPlaybackRate( unsigned rate );
+unsigned SOUNDSCAPE_GetPlaybackRate( void );
+int      SOUNDSCAPE_SetMixMode( int mode );
+void     SOUNDSCAPE_StopPlayback( void );
+int      SOUNDSCAPE_GetCurrentPos( void );
+int      SOUNDSCAPE_BeginBufferedPlayback( char *BufferStart, int BufferSize, int NumDivisions, unsigned SampleRate, int MixMode, void ( *CallBackFunc )( void ) );
+int      SOUNDSCAPE_GetCardInfo( int *MaxSampleBits, int *MaxChannels );
+void     SOUNDSCAPE_SetCallBack( void ( *func )( void ) );
+int      SOUNDSCAPE_GetMIDIPort( void );
+int      SOUNDSCAPE_Init( void );
+void     SOUNDSCAPE_Shutdown( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/sndsrc.c
@@ -1,0 +1,658 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: SNDSRC.C
+
+   author: James R. Dose
+   date:   March 26, 1994
+
+   Low level routines to support the Disney Sound Source.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#define STEREO      1
+#define SIXTEEN_BIT 2
+
+#define MONO_8BIT    0
+#define STEREO_8BIT  ( STEREO )
+#define MONO_16BIT   ( SIXTEEN_BIT )
+#define STEREO_16BIT ( STEREO | SIXTEEN_BIT )
+
+#include <stdlib.h>
+#include <dos.h>
+#include <conio.h>
+#include "dpmi.h"
+#include "task_man.h"
+#include "sndcards.h"
+#include "user.h"
+#include "sndsrc.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+static int SS_Installed = FALSE;
+
+static int SS_Port = SS_DefaultPort;
+static int SS_OffCommand = 0xc;
+
+static char   *SS_BufferStart;
+static char   *SS_BufferEnd;
+static char   *SS_CurrentBuffer;
+static int     SS_BufferNum = 0;
+static int     SS_NumBuffers = 0;
+static int     SS_TotalBufferSize = 0;
+static int     SS_TransferLength  = 0;
+static int     SS_CurrentLength   = 0;
+
+static char   *SS_SoundPtr;
+volatile int   SS_SoundPlaying;
+
+static task   *SS_Timer;
+
+void ( *SS_CallBack )( void );
+
+int SS_ErrorCode = SS_Ok;
+
+#define SS_SetErrorCode( status ) \
+   SS_ErrorCode   = ( status );
+
+/*---------------------------------------------------------------------
+   Function: SS_ErrorString
+
+   Returns a pointer to the error message associated with an error
+   number.  A -1 returns a pointer the current error.
+---------------------------------------------------------------------*/
+
+char *SS_ErrorString
+   (
+   int ErrorNumber
+   )
+
+   {
+   char *ErrorString;
+
+   switch( ErrorNumber )
+      {
+      case SS_Error :
+         ErrorString = SS_ErrorString( SS_ErrorCode );
+         break;
+
+      case SS_Ok :
+         ErrorString = "Sound Source ok.";
+         break;
+
+      case SS_NotFound :
+         ErrorString = "Could not detect Sound Source.";
+         break;
+
+      case SS_NoSoundPlaying :
+         ErrorString = "No sound playing in SndSrc.";
+         break;
+
+      case SS_DPMI_Error :
+         ErrorString = "DPMI Error in SndSrc.";
+         break;
+
+      default :
+         ErrorString = "Unknown Sound Source error code.";
+         break;
+      }
+
+   return( ErrorString );
+   }
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define SS_LockStart SS_ServiceInterrupt
+
+
+/*---------------------------------------------------------------------
+   Function: SS_ServiceInterrupt
+
+   Handles interrupt generated by sound card at the end of a voice
+   transfer.  Calls the user supplied callback function.
+---------------------------------------------------------------------*/
+
+static void SS_ServiceInterrupt
+   (
+   task *Task
+   )
+
+   {
+   int port = SS_Port;
+   int count;
+
+   count = 0;
+   while( ( inp( port + 1 ) & 0x40 ) == 0 )
+      {
+      outp( port, *SS_SoundPtr++ );
+      outp( port + 2, SS_OffCommand );
+      outp( port + 2, 4 );
+
+      SS_CurrentLength--;
+      if ( SS_CurrentLength == 0 )
+         {
+         // Keep track of current buffer
+         SS_CurrentBuffer += SS_TransferLength;
+         SS_BufferNum++;
+         if ( SS_BufferNum >= SS_NumBuffers )
+            {
+            SS_BufferNum = 0;
+            SS_CurrentBuffer = SS_BufferStart;
+            }
+
+         SS_CurrentLength = SS_TransferLength;
+         SS_SoundPtr = SS_CurrentBuffer;
+
+         // Call the caller's callback function
+         if ( SS_CallBack != NULL )
+            {
+            SS_CallBack();
+            }
+         }
+
+      count++;
+      // Only do at most 14 samples per tick
+      if ( count > 13 )
+         {
+         break;
+         }
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_StopPlayback
+
+   Ends the transfer of digitized sound to the Sound Source.
+---------------------------------------------------------------------*/
+
+void SS_StopPlayback
+   (
+   void
+   )
+
+   {
+   if ( SS_SoundPlaying )
+      {
+      TS_Terminate( SS_Timer );
+
+      outp( SS_Port, 0x80 );
+      outp( SS_Port + 2, SS_OffCommand );
+      outp( SS_Port + 2, 4 );
+
+      SS_SoundPlaying = FALSE;
+
+      SS_BufferStart = NULL;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_GetCurrentPos
+
+   Returns the offset within the current sound being played.
+---------------------------------------------------------------------*/
+
+int SS_GetCurrentPos
+   (
+   void
+   )
+
+   {
+   int offset;
+
+   if ( !SS_SoundPlaying )
+      {
+      SS_SetErrorCode( SS_NoSoundPlaying );
+      return( SS_Warning );
+      }
+
+   offset = ( int )( ( ( unsigned long )SS_SoundPtr ) -
+      ( ( unsigned long )SS_CurrentBuffer ) );
+
+   return( offset );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void SS_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_BeginBufferedPlayback
+
+   Begins multibuffered playback of digitized sound on the Sound Source.
+---------------------------------------------------------------------*/
+
+int SS_BeginBufferedPlayback
+   (
+   char *BufferStart,
+   int   BufferSize,
+   int   NumDivisions,
+   void ( *CallBackFunc )( void )
+   )
+
+   {
+   if ( SS_SoundPlaying )
+      {
+      SS_StopPlayback();
+      }
+
+   SS_SetCallBack( CallBackFunc );
+
+   SS_BufferStart     = BufferStart;
+   SS_CurrentBuffer   = BufferStart;
+   SS_SoundPtr        = BufferStart;
+   SS_TotalBufferSize = BufferSize;
+   SS_BufferEnd       = BufferStart + BufferSize;
+   SS_TransferLength  = BufferSize / NumDivisions;
+   SS_CurrentLength   = SS_TransferLength;
+   SS_BufferNum       = 0;
+   SS_NumBuffers      = NumDivisions;
+
+   SS_SoundPlaying = TRUE;
+
+//   SS_Timer = TS_ScheduleTask( SS_ServiceInterrupt, 438, 1, NULL );
+   SS_Timer = TS_ScheduleTask( SS_ServiceInterrupt, 510, 1, NULL );
+   TS_Dispatch();
+
+   return( SS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_GetPlaybackRate
+
+   Returns the rate at which the digitized sound will be played in
+   hertz.
+---------------------------------------------------------------------*/
+
+int SS_GetPlaybackRate
+   (
+   void
+   )
+
+   {
+   return( SS_SampleRate );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_SetMixMode
+
+   Sets the sound card to play samples in mono or stereo.
+---------------------------------------------------------------------*/
+
+int SS_SetMixMode
+   (
+   int mode
+   )
+
+   {
+   mode = MONO_8BIT;
+   return( mode );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_SetPort
+
+   Selects which port to use to write to the Sound Source.
+---------------------------------------------------------------------*/
+
+int SS_SetPort
+   (
+   int port
+   )
+
+   {
+   if ( SS_Installed )
+      {
+      SS_Shutdown();
+      }
+
+   SS_Port = port;
+
+   return( SS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_SetCallBack
+
+   Specifies the user function to call at the end of a sound transfer.
+---------------------------------------------------------------------*/
+
+void SS_SetCallBack
+   (
+   void ( *func )( void )
+   )
+
+   {
+   SS_CallBack = func;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_TestTimer
+
+   Used as a delay in SS_TestSoundSource.
+---------------------------------------------------------------------*/
+
+void SS_TestTimer
+   (
+   task *Task
+   )
+
+   {
+   ( *( int * )( Task->data ) )++;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_TestSoundSource
+
+   Detect if the Sound Source is located at the specified port.
+---------------------------------------------------------------------*/
+
+int SS_TestSoundSource
+   (
+   int port
+   )
+
+   {
+   int   present;
+   task *timer;
+   volatile int ticks;
+   int   i;
+
+   present = FALSE;
+
+   timer = TS_ScheduleTask( SS_TestTimer, 140, 1, &ticks );
+   TS_Dispatch();
+
+   outp( port + 2, 4 );
+
+   ticks = 0;
+
+   while( ticks < 4 )
+      {
+      // Do nothing for a while
+      }
+
+   TS_Terminate( timer );
+
+   if ( ( inp( port + 1 ) & 0x40 ) == 0 )
+      {
+      for( i = 32; i > 0; i-- )
+         {
+         outp( port, 0x80 );
+         outp( port + 2, SS_OffCommand );
+         outp( port + 2, 4 );
+         }
+
+      if ( inp( port + 1 ) & 0x40 )
+         {
+         present = TRUE;
+         }
+      }
+
+   outp( port + 2, SS_OffCommand );
+
+   return( present );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_DetectSoundSource
+
+   Detects which port the Sound Source is located.
+---------------------------------------------------------------------*/
+
+int SS_DetectSoundSource
+   (
+   void
+   )
+
+   {
+   if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT1 ) )
+      {
+      SS_Port = SS_Port1;
+      return( TRUE );
+      }
+
+   if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT2 ) )
+      {
+      SS_Port = SS_Port2;
+      return( TRUE );
+      }
+
+   if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT3 ) )
+      {
+      SS_Port = SS_Port3;
+      return( TRUE );
+      }
+
+   if ( SS_TestSoundSource( SS_Port1 ) )
+      {
+      SS_Port = SS_Port1;
+      return( TRUE );
+      }
+
+   if ( SS_TestSoundSource( SS_Port2 ) )
+      {
+      SS_Port = SS_Port2;
+      return( TRUE );
+      }
+
+   if ( SS_TestSoundSource( SS_Port3 ) )
+      {
+      SS_Port = SS_Port3;
+      return( TRUE );
+      }
+
+   return( FALSE );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_Init
+
+   Initializes the Sound Source prepares the module to play digitized
+   sounds.
+---------------------------------------------------------------------*/
+
+int SS_Init
+   (
+   int soundcard
+   )
+
+   {
+   int status;
+
+   if ( SS_Installed )
+      {
+      SS_Shutdown();
+      }
+
+   if ( ( soundcard == TandySoundSource ) ||
+      ( USER_CheckParameter( SELECT_TANDY_SOUNDSOURCE ) ) )
+      {
+      // Tandy
+      SS_OffCommand = 0x0e;
+      }
+   else
+      {
+      // Disney
+      SS_OffCommand = 0x0c;
+      }
+
+   status = SS_DetectSoundSource();
+   if ( !status )
+      {
+      SS_SetErrorCode( SS_NotFound );
+      return( SS_Warning );
+      }
+
+   status = SS_LockMemory();
+   if ( status != SS_Ok )
+      {
+      SS_UnlockMemory();
+      return( status );
+      }
+
+   status = SS_Ok;
+
+   outp( SS_Port + 2, 4 );
+
+   SS_SoundPlaying = FALSE;
+
+   SS_SetCallBack( NULL );
+
+   SS_BufferStart = NULL;
+
+   SS_Installed = TRUE;
+
+   SS_SetErrorCode( status );
+   return( status );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_Shutdown
+
+   Ends transfer of sound data to the Sound Source.
+---------------------------------------------------------------------*/
+
+void SS_Shutdown
+   (
+   void
+   )
+
+   {
+   // Halt the transfer
+   SS_StopPlayback();
+
+   outp( SS_Port + 2, SS_OffCommand );
+
+   SS_SoundPlaying = FALSE;
+
+   SS_BufferStart = NULL;
+
+   SS_SetCallBack( NULL );
+
+   SS_UnlockMemory();
+
+   SS_Installed = FALSE;
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void SS_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( SS_LockStart, SS_LockEnd );
+   DPMI_Unlock( SS_Installed );
+   DPMI_Unlock( SS_Port );
+   DPMI_Unlock( SS_OffCommand );
+   DPMI_Unlock( SS_BufferStart );
+   DPMI_Unlock( SS_BufferEnd );
+   DPMI_Unlock( SS_CurrentBuffer );
+   DPMI_Unlock( SS_BufferNum );
+   DPMI_Unlock( SS_NumBuffers );
+   DPMI_Unlock( SS_TotalBufferSize );
+   DPMI_Unlock( SS_TransferLength );
+   DPMI_Unlock( SS_CurrentLength );
+   DPMI_Unlock( SS_SoundPtr );
+   DPMI_Unlock( SS_SoundPlaying );
+   DPMI_Unlock( SS_Timer );
+   DPMI_Unlock( SS_CallBack );
+   DPMI_Unlock( SS_ErrorCode );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: SS_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int SS_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( SS_LockStart, SS_LockEnd );
+   status |= DPMI_Lock( SS_Installed );
+   status |= DPMI_Lock( SS_Port );
+   status |= DPMI_Lock( SS_OffCommand );
+   status |= DPMI_Lock( SS_BufferStart );
+   status |= DPMI_Lock( SS_BufferEnd );
+   status |= DPMI_Lock( SS_CurrentBuffer );
+   status |= DPMI_Lock( SS_BufferNum );
+   status |= DPMI_Lock( SS_NumBuffers );
+   status |= DPMI_Lock( SS_TotalBufferSize );
+   status |= DPMI_Lock( SS_TransferLength );
+   status |= DPMI_Lock( SS_CurrentLength );
+   status |= DPMI_Lock( SS_SoundPtr );
+   status |= DPMI_Lock( SS_SoundPlaying );
+   status |= DPMI_Lock( SS_Timer );
+   status |= DPMI_Lock( SS_CallBack );
+   status |= DPMI_Lock( SS_ErrorCode );
+
+   if ( status != DPMI_Ok )
+      {
+      SS_UnlockMemory();
+      SS_SetErrorCode( SS_DPMI_Error );
+      return( SS_Error );
+      }
+
+   return( SS_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/sndsrc.h
@@ -1,0 +1,70 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: SNDSRC.H
+
+   author: James R. Dose
+   date:   March 26, 1994
+
+   Public header for for SNDSRC.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __SNDSRC_H
+#define __SNDSRC_H
+
+enum SS_ERRORS
+   {
+   SS_Warning = -2,
+   SS_Error   = -1,
+   SS_Ok      = 0,
+   SS_NotFound,
+   SS_NoSoundPlaying,
+   SS_DPMI_Error
+   };
+
+#define SELECT_SOUNDSOURCE_PORT1 "ss1"
+#define SELECT_SOUNDSOURCE_PORT2 "ss2"
+#define SELECT_SOUNDSOURCE_PORT3 "ss3"
+#define SELECT_TANDY_SOUNDSOURCE "sst"
+
+#define SS_Port1 0x3bc
+#define SS_Port2 0x378
+#define SS_Port3 0x278
+
+#define SS_DefaultPort 0x378
+#define SS_SampleRate  7000
+#define SS_DMAChannel  -1
+
+char *SS_ErrorString( int ErrorNumber );
+void  SS_StopPlayback( void );
+int   SS_GetCurrentPos( void );
+int   SS_BeginBufferedPlayback( char *BufferStart, int BufferSize, int NumDivisions, void ( *CallBackFunc )( void ) );
+int   SS_GetPlaybackRate( void );
+int   SS_SetMixMode( int mode );
+int   SS_SetPort( int port );
+void  SS_SetCallBack( void ( *func )( void ) );
+int   SS_Init( int soundcard );
+void  SS_Shutdown( void );
+void  SS_UnlockMemory( void );
+int   SS_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/standard.h
@@ -1,0 +1,72 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: STANDARD.H
+
+   author: James R. Dose
+   date:   May 25, 1994
+
+   Header containing standard definitions.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __STANDARD_H
+#define __STANDARD_H
+
+typedef int boolean;
+typedef int errorcode;
+
+#ifndef TRUE
+   #define TRUE ( 1 == 1 )
+   #define FALSE ( !TRUE )
+#endif
+
+enum STANDARD_ERRORS
+   {
+   Warning = -2,
+   FatalError = -1,
+   Success = 0
+   };
+
+#define BITSET( data, bit ) \
+   ( ( ( data ) & ( bit ) ) == ( bit ) )
+
+#define ARRAY_LENGTH( array ) \
+   ( sizeof( array ) / sizeof( ( array )[ 0 ] ) )
+
+#define WITHIN_BOUNDS( array, index ) \
+   ( ( 0 <= ( index ) ) && ( ( index ) < ARRAY_LENGTH( array ) ) )
+
+#define FOREVER    for( ; ; )
+
+#ifdef NDEBUG
+   #define DEBUGGING 0
+#else
+   #define DEBUGGING 1
+#endif
+
+#define DEBUG_CODE \
+   if ( DEBUGGING == 0 ) \
+      { \
+      } \
+   else
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/task_man.c
@@ -1,0 +1,976 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: TASK_MAN.C
+
+   author: James R. Dose
+   date:   July 25, 1994
+
+   Low level timer task scheduler.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+//#define USESTACK
+#define LOCKMEMORY
+#define NOINTS
+#define USE_USRHOOKS
+
+#include <stdlib.h>
+#include <dos.h>
+#include <conio.h>
+#include <string.h>
+#include "interrup.h"
+#include "linklist.h"
+#include "task_man.h"
+
+#ifdef USESTACK
+#include "dpmi.h"
+#endif
+#ifdef LOCKMEMORY
+#include "dpmi.h"
+#endif
+
+#ifdef USE_USRHOOKS
+#include "usrhooks.h"
+#define FreeMem( ptr )   USRHOOKS_FreeMem( ( ptr ) )
+#else
+#define FreeMem( ptr )   free( ( ptr ) )
+#endif
+
+typedef struct
+   {
+   task *start;
+   task *end;
+   } tasklist;
+
+
+/*---------------------------------------------------------------------
+   Global variables
+---------------------------------------------------------------------*/
+
+#ifdef USESTACK
+
+// adequate stack size
+#define kStackSize 2048
+
+static unsigned short StackSelector = NULL;
+static unsigned long  StackPointer;
+
+static unsigned short oldStackSelector;
+static unsigned long  oldStackPointer;
+
+#endif
+
+static task HeadTask;
+static task *TaskList = &HeadTask;
+
+static void ( __interrupt __far *OldInt8 )( void );
+
+static volatile long TaskServiceRate  = 0x10000L;
+static volatile long TaskServiceCount = 0;
+
+#ifndef NOINTS
+static volatile int  TS_TimesInInterrupt;
+#endif
+
+static char TS_Installed = FALSE;
+
+volatile int TS_InInterrupt = FALSE;
+
+/*---------------------------------------------------------------------
+   Function prototypes
+---------------------------------------------------------------------*/
+
+static void TS_FreeTaskList( void );
+static void TS_SetClockSpeed( long speed );
+static long TS_SetTimer( long TickBase );
+static void TS_SetTimerToMaxTaskRate( void );
+static void __interrupt __far TS_ServiceSchedule( void );
+static void __interrupt __far TS_ServiceScheduleIntEnabled( void );
+static void TS_AddTask( task *ptr );
+static int  TS_Startup( void );
+static void RestoreRealTimeClock( void );
+
+// These declarations are necessary to use the inline assembly pragmas.
+
+extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
+extern void SetStack(unsigned short selector,unsigned long stackptr);
+
+// This function will get the current stack selector and pointer and save
+// them off.
+#pragma aux GetStack =	\
+	"mov  [edi],esp"		\
+	"mov	ax,ss"	 		\
+	"mov  [esi],ax" 		\
+	parm [esi] [edi]		\
+	modify [eax esi edi];
+
+// This function will set the stack selector and pointer to the specified
+// values.
+#pragma aux SetStack =	\
+	"mov  ss,ax"			\
+	"mov  esp,edx"			\
+	parm [ax] [edx]		\
+	modify [eax edx];
+
+
+/**********************************************************************
+
+   Memory locked functions:
+
+**********************************************************************/
+
+
+#define TS_LockStart TS_FreeTaskList
+
+
+/*---------------------------------------------------------------------
+   Function: TS_FreeTaskList
+
+   Terminates all tasks and releases any memory used for control
+   structures.
+---------------------------------------------------------------------*/
+
+static void TS_FreeTaskList
+   (
+   void
+   )
+
+   {
+   task *node;
+   task *next;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   node = TaskList->next;
+   while( node != TaskList )
+      {
+      next = node->next;
+      FreeMem( node );
+      node = next;
+      }
+
+   TaskList->next = TaskList;
+   TaskList->prev = TaskList;
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_SetClockSpeed
+
+   Sets the rate of the 8253 timer.
+---------------------------------------------------------------------*/
+
+static void TS_SetClockSpeed
+   (
+   long speed
+   )
+
+   {
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   if ( ( speed > 0 ) && ( speed < 0x10000L ) )
+      {
+      TaskServiceRate = speed;
+      }
+   else
+      {
+      TaskServiceRate = 0x10000L;
+      }
+
+   outp( 0x43, 0x36 );
+   outp( 0x40, TaskServiceRate );
+   outp( 0x40, TaskServiceRate >> 8 );
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_SetTimer
+
+   Calculates the rate at which a task will occur and sets the clock
+   speed if necessary.
+---------------------------------------------------------------------*/
+
+static long TS_SetTimer
+   (
+   long TickBase
+   )
+
+   {
+   long speed;
+
+   speed = 1192030L / TickBase;
+   if ( speed < TaskServiceRate )
+      {
+      TS_SetClockSpeed( speed );
+      }
+
+   return( speed );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_SetTimerToMaxTaskRate
+
+   Finds the fastest running task and sets the clock to operate at
+   that speed.
+---------------------------------------------------------------------*/
+
+static void TS_SetTimerToMaxTaskRate
+   (
+   void
+   )
+
+   {
+   task     *ptr;
+   long      MaxServiceRate;
+   unsigned  flags;
+
+   flags = DisableInterrupts();
+
+   MaxServiceRate = 0x10000L;
+
+   ptr = TaskList->next;
+   while( ptr != TaskList )
+      {
+      if ( ptr->rate < MaxServiceRate )
+         {
+         MaxServiceRate = ptr->rate;
+         }
+
+      ptr = ptr->next;
+      }
+
+   if ( TaskServiceRate != MaxServiceRate )
+      {
+      TS_SetClockSpeed( MaxServiceRate );
+      }
+
+   RestoreInterrupts( flags );
+   }
+
+
+#ifdef NOINTS
+/*---------------------------------------------------------------------
+   Function: TS_ServiceSchedule
+
+   Interrupt service routine
+---------------------------------------------------------------------*/
+
+static void __interrupt __far TS_ServiceSchedule
+   (
+   void
+   )
+
+   {
+   task *ptr;
+   task *next;
+
+
+   TS_InInterrupt = TRUE;
+
+   #ifdef USESTACK
+   // save stack
+   GetStack( &oldStackSelector, &oldStackPointer );
+
+   // set our stack
+   SetStack( StackSelector, StackPointer );
+   #endif
+
+   ptr = TaskList->next;
+   while( ptr != TaskList )
+      {
+      next = ptr->next;
+
+      if ( ptr->active )
+         {
+         ptr->count += TaskServiceRate;
+//JIM
+//         if ( ptr->count >= ptr->rate )
+         while( ptr->count >= ptr->rate )
+            {
+            ptr->count -= ptr->rate;
+            ptr->TaskService( ptr );
+            }
+         }
+      ptr = next;
+      }
+
+   #ifdef USESTACK
+   // restore stack
+   SetStack( oldStackSelector, oldStackPointer );
+   #endif
+
+   TaskServiceCount += TaskServiceRate;
+   if ( TaskServiceCount > 0xffffL )
+      {
+      TaskServiceCount &= 0xffff;
+      _chain_intr( OldInt8 );
+      }
+
+   outp( 0x20,0x20 );
+
+   TS_InInterrupt = FALSE;
+   }
+
+#else
+
+/*---------------------------------------------------------------------
+   Function: TS_ServiceScheduleIntEnabled
+
+   Interrupt service routine with interrupts enabled.
+---------------------------------------------------------------------*/
+
+static void __interrupt __far TS_ServiceScheduleIntEnabled
+   (
+   void
+   )
+
+   {
+   task *ptr;
+   task *next;
+
+   TS_TimesInInterrupt++;
+   TaskServiceCount += TaskServiceRate;
+   if ( TaskServiceCount > 0xffffL )
+      {
+      TaskServiceCount &= 0xffff;
+      _chain_intr( OldInt8 );
+      }
+
+   outp( 0x20,0x20 );
+
+   if ( TS_InInterrupt )
+      {
+      return;
+      }
+
+   TS_InInterrupt = TRUE;
+   _enable();
+
+   #ifdef USESTACK
+   // save stack
+   GetStack( &oldStackSelector, &oldStackPointer );
+
+   // set our stack
+   SetStack( StackSelector, StackPointer );
+   #endif
+
+   while( TS_TimesInInterrupt )
+      {
+      ptr = TaskList->next ;
+      while( ptr != TaskList )
+         {
+         next = ptr->next;
+
+         if ( ptr->active )
+            {
+            ptr->count += TaskServiceRate;
+            if ( ptr->count >= ptr->rate )
+               {
+               ptr->count -= ptr->rate;
+               ptr->TaskService( ptr );
+               }
+            }
+         ptr = next;
+         }
+      TS_TimesInInterrupt--;
+      }
+
+   _disable();
+
+   #ifdef USESTACK
+   // restore stack
+   SetStack( oldStackSelector, oldStackPointer );
+   #endif
+
+   TS_InInterrupt = FALSE;
+   }
+#endif
+
+
+#ifdef USESTACK
+
+/*---------------------------------------------------------------------
+   Function: allocateTimerStack
+
+   Allocate a block of memory from conventional (low) memory and return
+   the selector (which can go directly into a segment register) of the
+   memory block or 0 if an error occured.
+---------------------------------------------------------------------*/
+
+static unsigned short allocateTimerStack
+   (
+   unsigned short size
+   )
+
+   {
+   union REGS regs;
+
+   // clear all registers
+   memset( &regs, 0, sizeof( regs ) );
+
+   // DPMI allocate conventional memory
+   regs.w.ax = 0x100;
+
+   // size in paragraphs
+   regs.w.bx = ( size + 15 ) / 16;
+
+   int386( 0x31, &regs, &regs );
+   if (!regs.w.cflag)
+      {
+      // DPMI call returns selector in dx
+      // (ax contains real mode segment
+      // which is ignored here)
+
+      return( regs.w.dx );
+      }
+
+   // Couldn't allocate memory.
+   return( NULL );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: deallocateTimerStack
+
+   Deallocate a block of conventional (low) memory given a selector to
+   it.  Assumes the block was allocated with DPMI function 0x100.
+---------------------------------------------------------------------*/
+
+static void deallocateTimerStack
+   (
+   unsigned short selector
+   )
+
+   {
+	union REGS regs;
+
+	if ( selector != NULL )
+      {
+      // clear all registers
+      memset( &regs, 0, sizeof( regs ) );
+
+      regs.w.ax = 0x101;
+      regs.w.dx = selector;
+      int386( 0x31, &regs, &regs );
+      }
+   }
+
+#endif
+
+/*---------------------------------------------------------------------
+   Function: TS_Startup
+
+   Sets up the task service routine.
+---------------------------------------------------------------------*/
+
+static int TS_Startup
+   (
+   void
+   )
+
+   {
+   if ( !TS_Installed )
+      {
+#ifdef LOCKMEMORY
+
+      int status;
+
+      status = TS_LockMemory();
+      if ( status != TASK_Ok )
+         {
+         TS_UnlockMemory();
+         return( status );
+         }
+
+#endif
+
+#ifdef USESTACK
+
+	   StackSelector = allocateTimerStack( kStackSize );
+      if ( StackSelector == NULL )
+         {
+
+#ifdef LOCKMEMORY
+
+         TS_UnlockMemory();
+
+#endif
+         return( TASK_Error );
+         }
+
+      // Leave a little room at top of stack just for the hell of it...
+      StackPointer = kStackSize - sizeof( long );
+
+#endif
+
+//static const task *TaskList = &HeadTask;
+      TaskList->next = TaskList;
+      TaskList->prev = TaskList;
+
+      TaskServiceRate  = 0x10000L;
+      TaskServiceCount = 0;
+
+#ifndef NOINTS
+      TS_TimesInInterrupt = 0;
+#endif
+
+      OldInt8 = _dos_getvect( 0x08 );
+      #ifdef NOINTS
+         _dos_setvect( 0x08, TS_ServiceSchedule );
+      #else
+         _dos_setvect( 0x08, TS_ServiceScheduleIntEnabled );
+      #endif
+
+      TS_Installed = TRUE;
+      }
+
+   return( TASK_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_Shutdown
+
+   Ends processing of all tasks.
+---------------------------------------------------------------------*/
+
+void TS_Shutdown
+   (
+   void
+   )
+
+   {
+   if ( TS_Installed )
+      {
+      TS_FreeTaskList();
+
+      TS_SetClockSpeed( 0 );
+
+      _dos_setvect( 0x08, OldInt8 );
+
+#ifdef USESTACK
+
+      deallocateTimerStack( StackSelector );
+      StackSelector = NULL;
+
+#endif
+
+      // Set Date and Time from CMOS
+//      RestoreRealTimeClock();
+
+#ifdef LOCKMEMORY
+
+      TS_UnlockMemory();
+
+#endif
+      TS_Installed = FALSE;
+      }
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_ScheduleTask
+
+   Schedules a new task for processing.
+---------------------------------------------------------------------*/
+
+task *TS_ScheduleTask
+   (
+   void  ( *Function )( task * ),
+   int   rate,
+   int   priority,
+   void *data
+   )
+
+   {
+   task *ptr;
+
+#ifdef USE_USRHOOKS
+   int   status;
+
+   ptr = NULL;
+
+   status = USRHOOKS_GetMem( &ptr, sizeof( task ) );
+   if ( status == USRHOOKS_Ok )
+#else
+   ptr = malloc( sizeof( task ) );
+   if ( ptr != NULL )
+#endif
+      {
+      if ( !TS_Installed )
+         {
+         status = TS_Startup();
+         if ( status != TASK_Ok )
+            {
+            FreeMem( ptr );
+            return( NULL );
+            }
+         }
+
+      ptr->TaskService = Function;
+      ptr->data = data;
+      ptr->rate = TS_SetTimer( rate );
+      ptr->count = 0;
+      ptr->priority = priority;
+      ptr->active = FALSE;
+
+      TS_AddTask( ptr );
+      }
+
+   return( ptr );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_AddTask
+
+   Adds a new task to our list of tasks.
+---------------------------------------------------------------------*/
+
+static void TS_AddTask
+   (
+   task *node
+   )
+
+   {
+   LL_SortedInsertion( TaskList, node, next, prev, task, priority );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_Terminate
+
+   Ends processing of a specific task.
+---------------------------------------------------------------------*/
+
+int TS_Terminate
+   (
+   task *NodeToRemove
+   )
+
+   {
+   task *ptr;
+   task *next;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   ptr = TaskList->next;
+   while( ptr != TaskList )
+      {
+      next = ptr->next;
+
+      if ( ptr == NodeToRemove )
+         {
+         LL_RemoveNode( NodeToRemove, next, prev );
+         NodeToRemove->next = NULL;
+         NodeToRemove->prev = NULL;
+         FreeMem( NodeToRemove );
+
+         TS_SetTimerToMaxTaskRate();
+
+         RestoreInterrupts( flags );
+
+         return( TASK_Ok );
+         }
+
+      ptr = next;
+      }
+
+   RestoreInterrupts( flags );
+
+   return( TASK_Warning );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_Dispatch
+
+   Begins processing of all inactive tasks.
+---------------------------------------------------------------------*/
+
+void TS_Dispatch
+   (
+   void
+   )
+
+   {
+   task *ptr;
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   ptr = TaskList->next;
+   while( ptr != TaskList )
+      {
+      ptr->active = TRUE;
+      ptr = ptr->next;
+      }
+
+   RestoreInterrupts( flags );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_SetTaskRate
+
+   Sets the rate at which the specified task is serviced.
+---------------------------------------------------------------------*/
+
+void TS_SetTaskRate
+   (
+   task *Task,
+   int rate
+   )
+
+   {
+   unsigned flags;
+
+   flags = DisableInterrupts();
+
+   Task->rate = TS_SetTimer( rate );
+   TS_SetTimerToMaxTaskRate();
+
+   RestoreInterrupts( flags );
+   }
+
+
+#ifdef LOCKMEMORY
+
+/*---------------------------------------------------------------------
+   Function: TS_LockEnd
+
+   Used for determining the length of the functions to lock in memory.
+---------------------------------------------------------------------*/
+
+static void TS_LockEnd
+   (
+   void
+   )
+
+   {
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_UnlockMemory
+
+   Unlocks all neccessary data.
+---------------------------------------------------------------------*/
+
+void TS_UnlockMemory
+   (
+   void
+   )
+
+   {
+   DPMI_UnlockMemoryRegion( TS_LockStart, TS_LockEnd );
+   DPMI_Unlock( TaskList );
+   DPMI_Unlock( OldInt8 );
+   DPMI_Unlock( TaskServiceRate );
+   DPMI_Unlock( TaskServiceCount );
+   DPMI_Unlock( TS_Installed );
+
+#ifndef NOINTS
+   DPMI_Unlock( TS_TimesInInterrupt );
+#endif
+
+#ifdef USESTACK
+   DPMI_Unlock( StackSelector );
+   DPMI_Unlock( StackPointer );
+   DPMI_Unlock( oldStackSelector );
+   DPMI_Unlock( oldStackPointer );
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: TS_LockMemory
+
+   Locks all neccessary data.
+---------------------------------------------------------------------*/
+
+int TS_LockMemory
+   (
+   void
+   )
+
+   {
+   int status;
+
+   status  = DPMI_LockMemoryRegion( TS_LockStart, TS_LockEnd );
+   status |= DPMI_Lock( TaskList );
+   status |= DPMI_Lock( OldInt8 );
+   status |= DPMI_Lock( TaskServiceRate );
+   status |= DPMI_Lock( TaskServiceCount );
+   status |= DPMI_Lock( TS_Installed );
+
+#ifndef NOINTS
+   status |= DPMI_Lock( TS_TimesInInterrupt );
+#endif
+
+#ifdef USESTACK
+   status |= DPMI_Lock( StackSelector );
+   status |= DPMI_Lock( StackPointer );
+   status |= DPMI_Lock( oldStackSelector );
+   status |= DPMI_Lock( oldStackPointer );
+#endif
+
+   if ( status != DPMI_Ok )
+      {
+      TS_UnlockMemory();
+      return( TASK_Error );
+      }
+
+   return( TASK_Ok );
+   }
+
+#endif
+
+/*
+// Converts a hex byte to an integer
+
+static int btoi
+   (
+   unsigned char bcd
+   )
+
+   {
+   unsigned b;
+   unsigned c;
+   unsigned d;
+
+   b = bcd / 16;
+   c = bcd - b * 16;
+   d = b * 10 + c;
+   return( d );
+   }
+
+
+static void RestoreRealTimeClock
+   (
+   void
+   )
+
+   {
+   int read;
+   int i;
+   int hr;
+   int min;
+   int sec;
+   int cent;
+   int yr;
+   int mo;
+   int day;
+   int year;
+   union REGS inregs;
+
+   // Read Real Time Clock Time.
+   read = FALSE;
+	inregs.h.ah = 0x02;
+   for( i = 1; i <= 3; i++ )
+      {
+      int386( 0x1A, &inregs, &inregs );
+      if ( inregs.x.cflag == 0 )
+         {
+         read = TRUE;
+         }
+      }
+
+   if ( read )
+      {
+      //and convert BCD to integer format
+      hr  = btoi( inregs.h.ch );
+      min = btoi( inregs.h.cl );
+      sec = btoi( inregs.h.dh );
+
+      // Read Real Time Clock Date.
+      inregs.h.ah = 0x04;
+      int386( 0x1A, &inregs, &inregs );
+      if ( inregs.x.cflag == 0 )
+         {
+         //and convert BCD to integer format
+         cent = btoi( inregs.h.ch );
+         yr   = btoi( inregs.h.cl );
+         mo   = btoi( inregs.h.dh );
+         day  = btoi( inregs.h.dl );
+         year = cent * 100 + yr;
+
+         // Set System Time.
+         inregs.h.ch = hr;
+         inregs.h.cl = min;
+         inregs.h.dh = sec;
+         inregs.h.dl = 0;
+         inregs.h.ah = 0x2D;
+         int386( 0x21, &inregs, &inregs );
+
+         // Set System Date.
+         inregs.w.cx = year;
+         inregs.h.dh = mo;
+         inregs.h.dl = day;
+         inregs.h.ah = 0x2B;
+         int386( 0x21, &inregs, &inregs );
+         }
+      }
+   }
+*/
+/*
+   struct dostime_t time;
+   struct dosdate_t date;
+
+   outp(0x70,0);
+   time.second=inp(0x71);
+   outp(0x70,2);
+   time.minute=inp(0x71);
+   outp(0x70,4);
+   time.hour=inp(0x71);
+
+   outp(0x70,7);
+   date.day=inp(0x71);
+   outp(0x70,8);
+   date.month=inp(0x71);
+   outp(0x70,9);
+   date.year=inp(0x71);
+
+   time.second=(time.second&0x0f)+((time.second>>4)*10);
+   time.minute=(time.minute&0x0f)+((time.minute>>4)*10);
+   time.hour=(time.hour&0x0f)+((time.hour>>4)*10);
+
+   date.day=(date.day&0x0f)+((date.day>>4)*10);
+   date.month=(date.month&0x0f)+((date.month>>4)*10);
+   date.year=(date.year&0x0f)+((date.year>>4)*10);
+
+   _dos_settime(&time);
+   _dos_setdate(&date);
+
+*/
--- /dev/null
+++ b/Game/src/audiolib/task_man.h
@@ -1,0 +1,68 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: TASK_MAN.C
+
+   author: James R. Dose
+   date:   July 25, 1994
+
+   Public header for TASK_MAN.C, a low level timer task scheduler.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __TASK_MAN_H
+#define __TASK_MAN_H
+
+enum TASK_ERRORS
+   {
+   TASK_Warning = -2,
+   TASK_Error = -1,
+   TASK_Ok = 0
+   };
+
+typedef struct task
+{
+    struct   task *next;
+    struct   task *prev;
+    void          ( *TaskService )( struct task * );
+    void          *data;
+    long          rate;
+    volatile long count;
+    int           priority;
+    int           active;
+} task;
+
+// TS_InInterrupt is TRUE during a taskman interrupt.
+// Use this if you have code that may be used both outside
+// and within interrupts.
+
+extern volatile int TS_InInterrupt;
+
+void    TS_Shutdown( void );
+task    *TS_ScheduleTask( void ( *Function )( task * ), int rate,
+                          int priority, void *data );
+int     TS_Terminate( task *ptr );
+void    TS_Dispatch( void );
+void    TS_SetTaskRate( task *Task, int rate );
+void    TS_UnlockMemory( void );
+int     TS_LockMemory( void );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/user.c
@@ -1,0 +1,133 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: USER.C
+
+   author: James R. Dose
+   date:   April 26, 1994
+
+   Routines to parse command line options.
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifdef PLAT_DOS
+#include <dos.h>
+#endif
+
+#include <string.h>
+#include "user.h"
+
+#define TRUE  ( 1 == 1 )
+#define FALSE ( !TRUE )
+
+#ifdef PLAT_DOS
+extern int   _argc;
+extern char **_argv;
+#endif
+
+/*---------------------------------------------------------------------
+   Function: USER_CheckParameter
+
+   Checks if the specified string is present in the command line.
+---------------------------------------------------------------------*/
+
+int USER_CheckParameter
+   (
+   const char *parameter
+   )
+
+   {
+#ifdef PLAT_DOS
+   int i;
+   int found;
+   char *ptr;
+
+   found = FALSE;
+   i = 1;
+   while( i < _argc )
+      {
+      ptr = _argv[ i ];
+
+      // Only check parameters preceded by - or /
+      if ( ( *ptr == '-' ) || ( *ptr == '/' ) )
+         {
+         ptr++;
+         if ( stricmp( parameter, ptr ) == 0 )
+            {
+            found = TRUE;
+            break;
+            }
+         }
+
+      i++;
+      }
+
+   return( found );
+#else
+   return FALSE;
+#endif
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: USER_GetText
+
+   Checks if the specified string is present in the command line
+   and returns a pointer to the text following it.
+---------------------------------------------------------------------*/
+
+char *USER_GetText
+   (
+   const char *parameter
+   )
+
+   {
+#ifdef PLAT_DOS
+   int i;
+   char *text;
+   char *ptr;
+
+   text = NULL;
+   i = 1;
+   while( i < _argc )
+      {
+      ptr = _argv[ i ];
+
+      // Only check parameters preceded by - or /
+      if ( ( *ptr == '-' ) || ( *ptr == '/' ) )
+         {
+         ptr++;
+         if ( stricmp( parameter, ptr ) == 0 )
+            {
+            i++;
+            text = _argv[ i ];
+            break;
+            }
+         }
+
+      i++;
+      }
+
+   return( text );
+#else
+   return NULL;
+#endif
+   }
--- /dev/null
+++ b/Game/src/audiolib/user.h
@@ -1,0 +1,38 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: USER.H
+
+   author: James R. Dose
+   phone:  (214)-271-1365 Ext #221
+   date:   April 26, 1994
+
+   Public header for USER.C
+
+   (c) Copyright 1994 James R. Dose.  All Rights Reserved.
+**********************************************************************/
+
+#ifndef __USER_H
+#define __USER_H
+
+int   USER_CheckParameter( const char *parameter );
+char *USER_GetText( const char *parameter );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/usrhooks.c
@@ -1,0 +1,84 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: USRHOOKS.C
+
+   author: James R. Dose
+   date:   July 26, 1994
+
+   This module contains cover functions for operations the library
+   needs that may be restricted by the calling program.  This code
+   is left public for you to modify.
+**********************************************************************/
+
+#include <stdlib.h>
+#include "usrhooks.h"
+
+
+/*---------------------------------------------------------------------
+   Function: USRHOOKS_GetMem
+
+   Allocates the requested amount of memory and returns a pointer to
+   its location, or NULL if an error occurs.  NOTE: pointer is assumed
+   to be dword aligned.
+---------------------------------------------------------------------*/
+
+int USRHOOKS_GetMem
+   (
+   void **ptr,
+   unsigned long size
+   )
+
+   {
+   void *memory;
+
+   memory = malloc( size );
+   if ( memory == NULL )
+      {
+      return( USRHOOKS_Error );
+      }
+
+   *ptr = memory;
+
+   return( USRHOOKS_Ok );
+   }
+
+
+/*---------------------------------------------------------------------
+   Function: USRHOOKS_FreeMem
+
+   Deallocates the memory associated with the specified pointer.
+---------------------------------------------------------------------*/
+
+int USRHOOKS_FreeMem
+   (
+   void *ptr
+   )
+
+   {
+   if ( ptr == NULL )
+      {
+      return( USRHOOKS_Error );
+      }
+
+   free( ptr );
+
+   return( USRHOOKS_Ok );
+   }
--- /dev/null
+++ b/Game/src/audiolib/usrhooks.h
@@ -1,0 +1,55 @@
+/*
+Copyright (C) 1994-1995 Apogee Software, Ltd.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+/**********************************************************************
+   module: USRHOOKS.H
+
+   author: James R. Dose
+   date:   July 26, 1994
+
+   Public header file for USRHOOKS.C.
+
+   This module contains cover functions for operations the library
+   needs that may be restricted by the calling program.  The function
+   prototypes in this header should not be modified.
+**********************************************************************/
+
+#ifndef __USRHOOKS_H
+#define __USRHOOKS_H
+
+/*---------------------------------------------------------------------
+   Error definitions
+---------------------------------------------------------------------*/
+
+enum USRHOOKS_Errors
+   {
+   USRHOOKS_Warning = -2,
+   USRHOOKS_Error   = -1,
+   USRHOOKS_Ok      = 0
+   };
+
+
+/*---------------------------------------------------------------------
+   Function Prototypes
+---------------------------------------------------------------------*/
+
+int USRHOOKS_GetMem( void **ptr, unsigned long size );
+int USRHOOKS_FreeMem( void *ptr );
+
+#endif
--- /dev/null
+++ b/Game/src/audiolib/util.h
@@ -1,0 +1,13 @@
+#ifndef AUDIOLIB__UTIL_H
+#define AUDIOLIB__UTIL_H
+
+#ifndef min
+#define min(a, b)  ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b)  ((a) > (b) ? (a) : (b))
+#endif
+
+#endif
+
--- /dev/null
+++ b/Game/src/config.c
@@ -1,0 +1,1005 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#if PLATFORM_DOS
+#include <conio.h>
+#include <dos.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include "duke3d.h"
+#include "scriplib.h"
+#include "cache1d.h"
+
+// we load this in to get default button and key assignments
+// as well as setting up function mappings
+
+#include "_functio.h"
+
+//
+// Sound variables
+//
+int32 FXDevice;
+int32 MusicDevice;
+int32 FXVolume;
+int32 MusicVolume;
+int32 SoundToggle;
+int32 MusicToggle;
+int32 VoiceToggle;
+int32 AmbienceToggle;
+int32 OpponentSoundToggle; // xduke to toggle opponent's sounds on/off in DM (duke 1.3d scheme)
+fx_blaster_config BlasterConfig;
+int32 NumVoices;
+int32 NumChannels;
+int32 NumBits;
+int32 MixRate;
+int32 MidiPort;
+int32 ReverseStereo;
+
+int32 ControllerType;
+int32 MouseAiming = 0;
+int32 BFullScreen = 1;
+
+//
+// Screen variables
+//
+
+int32 ScreenMode=2;
+int32 ScreenWidth = 640;
+int32 ScreenHeight = 480;
+
+//
+// Mouse variables
+//
+int32 mouseSensitivity_X;
+int32 mouseSensitivity_Y;
+
+static char setupfilename[512];//={SETUPFILENAME};
+static int32 scripthandle;
+static int32 setupread=0;
+/*
+===================
+=
+= CONFIG_GetSetupFilename
+=
+===================
+*/
+#define MAXSETUPFILES 20
+void CONFIG_GetSetupFilename( void )
+   {
+   struct find_t fblock;
+   char extension[10];
+   char * src;
+   char * filenames[MAXSETUPFILES];
+   int32 numfiles;
+   int32 i;
+
+#if 0 //STUB .CFG lookup
+   /*
+   strcpy(setupfilename,SETUPFILENAME);
+
+   // determine extension
+
+   src = setupfilename + strlen(setupfilename) - 1;
+
+   while (*src != '.')
+      {
+      src--;
+      }
+   strcpy (&extension[1],src);
+   extension[0] = '*';
+
+   numfiles=0;
+   if (_dos_findfirst(extension,0,&fblock)==0)
+      {
+      do
+         {
+         // skip timidity.cfg if it exists; it's needed for MIDI playback
+         //  with SDL_mixer, and isn't a Duke configuration file. --ryan.
+         if (strcmpi(fblock.name, "timidity.cfg") != 0)
+            {
+             filenames[numfiles]=SafeMalloc(128);
+             strcpy(filenames[numfiles],fblock.name);
+             numfiles++;
+             if (numfiles == MAXSETUPFILES)
+                break;
+            }
+         }
+      while(!_dos_findnext(&fblock));
+      }
+   i = CheckParm (SETUPNAMEPARM);
+   if (i!=0)
+      {
+      numfiles = 0;
+      strcpy(setupfilename,_argv[i+1]);
+      }
+   if (numfiles>1)
+      {
+      int32 time;
+      int32 oldtime;
+      int32 count;
+
+      printf("\nMultiple Configuration Files Encountered\n");
+      printf("========================================\n");
+      printf("Please choose a configuration file from the following list by pressing its\n");
+      printf("corresponding letter:\n");
+      for (i=0;i<numfiles;i++)
+         {
+         if (strcmpi(filenames[i],SETUPFILENAME))
+            {
+            printf("%c. %s\n",'a'+(char)i,filenames[i]);
+            }
+         else
+            {
+            printf("%c. %s <DEFAULT>\n",'a'+(char)i,filenames[i]);
+            }
+         }
+      printf("\n");
+      printf("(%s will be used if no selection is made within 10 seconds.)\n\n",SETUPFILENAME);
+      KB_FlushKeyboardQueue();
+      KB_ClearKeysDown();
+      count = 9;
+      oldtime = clock();
+      time=clock()+(10*CLOCKS_PER_SEC);
+      while (clock()<time)
+         {
+         if (clock()>oldtime)
+            {
+            printf("%ld seconds left. \r",count);
+            fflush(stdout);
+            oldtime = clock()+CLOCKS_PER_SEC;
+            count--;
+            }
+         if (KB_KeyWaiting())
+            {
+            int32 ch = KB_Getch();
+            ch -='a';
+            if (ch>=0 && ch<numfiles)
+               {
+               strcpy (setupfilename, filenames[ch]);
+               break;
+               }
+            }
+         }
+      printf("\n\n");
+      }
+   if (numfiles==1)
+      strcpy (setupfilename, filenames[0]);
+	  */
+#endif
+   setupfilename[0] = '\0';
+
+   // Are we trying to load a mod?
+   if(game_dir[0] != '\0')
+   {
+		FILE *fp = NULL;
+
+	   //Yes
+		sprintf(setupfilename, "%s\\%s", game_dir, SETUPFILENAME);
+		
+		// let's make sure it's actually there
+		fp = fopen(setupfilename, "r");
+		if(fp)
+		{
+			fclose(fp);
+		}else
+		{
+			// It doesn't exist, so revert to the main one.
+			printf("Config file: %s does not exist, using main config.\n", setupfilename);
+			sprintf(setupfilename, "%s", SETUPFILENAME);
+		}
+
+   }else
+   {
+	   //No
+		strcpy (setupfilename, SETUPFILENAME);
+   }
+
+   printf("Using Setup file: '%s'\n",setupfilename);
+   i=clock()+(3*CLOCKS_PER_SEC/4);
+   while (clock()<i)
+      {
+      ;
+      }
+#if 0 //STUB .CFG lookup
+   /*
+   for (i=0;i<numfiles;i++)
+      {
+      SafeFree(filenames[i]);
+      }
+   */
+#endif //STUB .CFG lookup
+   }
+
+/*
+===================
+=
+= CONFIG_FunctionNameToNum
+=
+===================
+*/
+
+int32 CONFIG_FunctionNameToNum( char * func )
+   {
+   int32 i;
+
+   for (i=0;i<NUMGAMEFUNCTIONS;i++)
+      {
+      if (!stricmp(func,gamefunctions[i]))
+         {
+         return i;
+         }
+      }
+   return -1;
+   }
+
+/*
+===================
+=
+= CONFIG_FunctionNumToName
+=
+===================
+*/
+
+char * CONFIG_FunctionNumToName( int32 func )
+{
+	if (-1 < func && func < NUMGAMEFUNCTIONS)
+	{
+		return gamefunctions[func];
+	}
+	else
+	{
+		return NULL;
+	}
+}
+
+/*
+===================
+=
+= CONFIG_AnalogNameToNum
+=
+===================
+*/
+
+
+int32 CONFIG_AnalogNameToNum( char * func )
+   {
+
+   if (!stricmp(func,"analog_turning"))
+      {
+      return analog_turning;
+      }
+   if (!stricmp(func,"analog_strafing"))
+      {
+      return analog_strafing;
+      }
+   if (!stricmp(func,"analog_moving"))
+      {
+      return analog_moving;
+      }
+   if (!stricmp(func,"analog_lookingupanddown"))
+      {
+      return analog_lookingupanddown;
+      }
+
+   return -1;
+   }
+
+/*
+===================
+=
+= CONFIG_SetDefaults
+=
+===================
+*/
+
+void CONFIG_SetDefaults( void )
+{
+   // sound
+   SoundToggle = 1;
+   MusicToggle = 1;
+   VoiceToggle = 1;
+   AmbienceToggle = 1;
+   OpponentSoundToggle = 1;
+   FXVolume = 220;
+   MusicVolume = 200;
+   FXDevice = SoundScape;
+   MusicDevice = -1;
+   ReverseStereo = 0;
+   
+   // mouse
+   mouseSensitivity_X = 16;
+   mouseSensitivity_Y = mouseSensitivity_X;
+
+   // game
+   ps[0].aim_mode = 0;
+   ud.screen_size = 8;
+   ud.extended_screen_size = 0;
+   ud.screen_tilting = 1;
+   ud.brightness = 16;
+   ud.auto_run = 1;
+   ud.showweapons = 0;
+   ud.tickrate = 0;
+   ud.scrollmode = 0;
+   ud.shadows = 1;
+   ud.detail = 1;
+   ud.lockout = 0;
+   ud.pwlockout[0] = '\0';
+   ud.crosshair = 1;
+   ud.m_marker = 1; // for multiplayer
+   ud.m_ffire = 1;
+   ud.showcinematics = 1;
+   ud.weaponautoswitch = 0;
+   ud.hideweapon = 0;
+   ud.auto_aim = 2; // full by default
+   ud.gitdat_mdk = 0;
+   ud.playing_demo_rev = 0;
+
+   // com
+   strcpy(ud.rtsname,"DUKE.RTS");
+   strcpy(ud.ridecule[0],"An inspiration for birth control.");
+   strcpy(ud.ridecule[1],"You're gonna die for that!");
+   strcpy(ud.ridecule[2],"It hurts to be you.");
+   strcpy(ud.ridecule[3],"Lucky Son of a Bitch.");
+   strcpy(ud.ridecule[4],"Hmmm....Payback time.");
+   strcpy(ud.ridecule[5],"You bottom dwelling scum sucker.");
+   strcpy(ud.ridecule[6],"Damn, you're ugly.");
+   strcpy(ud.ridecule[7],"Ha ha ha...Wasted!");
+   strcpy(ud.ridecule[8],"You suck!");
+   strcpy(ud.ridecule[9],"AARRRGHHHHH!!!");
+
+   // Controller
+	ControllerType = controltype_keyboardandmouse;
+}
+
+/*
+===================
+=
+= CONFIG_ReadKeys
+=
+===================
+*/
+
+void CONFIG_ReadKeys( void )
+   {
+   int32 i;
+   int32 numkeyentries;
+   int32 function;
+   char keyname1[80];
+   char keyname2[80];
+   kb_scancode key1,key2;
+
+	// set default keys in case duke3d.cfg was not found
+
+	// FIX_00011: duke3d.cfg not needed anymore to start the game. Will create a default one
+	//            if not found and use default keys.
+
+	for(i=0; i<NUMKEYENTRIES; i++)
+	{
+	function = CONFIG_FunctionNameToNum(keydefaults[i].entryKey);
+	key1 = (byte) KB_StringToScanCode( keydefaults[i].keyname1 );
+	key2 = (byte) KB_StringToScanCode( keydefaults[i].keyname2 );
+	CONTROL_MapKey( function, key1, key2 );
+	}
+
+   numkeyentries = SCRIPT_NumberEntries( scripthandle, "KeyDefinitions" );
+
+   for (i=0;i<numkeyentries;i++)  // i = number in which the functions appear in duke3d.cfg
+      {
+      function = CONFIG_FunctionNameToNum(SCRIPT_Entry( scripthandle, "KeyDefinitions", i ));
+      if (function != -1)  // ensure it is in the list gamefunctions[function]
+         {
+         memset(keyname1,0,sizeof(keyname1));
+         memset(keyname2,0,sizeof(keyname2));
+         SCRIPT_GetDoubleString
+            (
+            scripthandle,
+            "KeyDefinitions",
+            SCRIPT_Entry( scripthandle,"KeyDefinitions", i ),
+            keyname1,
+            keyname2
+            );
+         key1 = 0;
+         key2 = 0;
+         if (keyname1[0])
+            {
+            key1 = (byte) KB_StringToScanCode( keyname1 );
+            }
+         if (keyname2[0])
+            {
+            key2 = (byte) KB_StringToScanCode( keyname2 );
+            }
+         CONTROL_MapKey( function, key1, key2 );
+         }
+      }
+   }
+
+
+/*
+===================
+=
+= CONFIG_SetupMouse
+=
+===================
+*/
+
+void CONFIG_SetupMouse( int32 scripthandle )
+   {
+   int32 i;
+   char str[80];
+   char temp[80];
+   int32 function, scale;
+
+   for (i=0;i<MAXMOUSEBUTTONS;i++)
+      {
+      sprintf(str,"MouseButton%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      CONTROL_MapButton( function, i, false );
+      sprintf(str,"MouseButtonClicked%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      CONTROL_MapButton( function, i, true );
+      }
+   // map over the axes
+   for (i=0;i<MAXMOUSEAXES;i++)
+      {
+      sprintf(str,"MouseAnalogAxes%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString(scripthandle, "Controls", str,temp);
+      function = CONFIG_AnalogNameToNum(temp);
+      if (function != -1)
+         {
+#pragma message( "Fix the Analog mouse axis issue. Just make a new function for registering them." )      
+         //CONTROL_MapAnalogAxis(i,function);
+         }
+      sprintf(str,"MouseDigitalAxes%ld_0",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString(scripthandle, "Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      CONTROL_MapDigitalAxis( i, function, 0 );
+      sprintf(str,"MouseDigitalAxes%ld_1",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString(scripthandle, "Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      CONTROL_MapDigitalAxis( i, function, 1 );
+      sprintf(str,"MouseAnalogScale%ld",i);
+      SCRIPT_GetNumber(scripthandle, "Controls", str,&scale);
+#pragma message( "Fix the Analog mouse scale issue. Just make a new function for registering them." )      
+	  //CONTROL_SetAnalogAxisScale( i, scale );
+      }
+
+	SCRIPT_GetNumber( scripthandle, "Controls","MouseSensitivity_X_Rancid",&mouseSensitivity_X);
+	if(mouseSensitivity_X>63 || mouseSensitivity_X < 0)
+		mouseSensitivity_X  = 15;
+	// FIX_00014: Added Y cursor setup for mouse sensitivity in the menus 
+	// Copy Sensitivity_X into Sensitivity_Y in case it is not set.
+	mouseSensitivity_Y = mouseSensitivity_X;
+	SCRIPT_GetNumber( scripthandle, "Controls","MouseSensitivity_Y_Rancid",&mouseSensitivity_Y);
+	if(mouseSensitivity_Y>63 || mouseSensitivity_Y < 0)
+		mouseSensitivity_Y  = 15;
+
+   }
+
+/*
+===================
+=
+= CONFIG_SetupGamePad
+=
+===================
+*/
+
+void CONFIG_SetupGamePad( int32 scripthandle )
+   {
+   int32 i;
+   char str[80];
+   char temp[80];
+   int32 function;
+
+
+   for (i=0;i<MAXJOYBUTTONS;i++)
+      {
+      sprintf(str,"JoystickButton%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapButton( function, i, false );
+      sprintf(str,"JoystickButtonClicked%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapButton( function, i, true );
+      }
+   // map over the axes
+   for (i=0;i<MAXGAMEPADAXES;i++)
+      {
+      sprintf(str,"GamePadDigitalAxes%ld_0",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString(scripthandle, "Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapDigitalAxis( i, function, 0 );
+      sprintf(str,"GamePadDigitalAxes%ld_1",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString(scripthandle, "Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapDigitalAxis( i, function, 1 );
+      }
+   SCRIPT_GetNumber( scripthandle, "Controls","JoystickPort",&function);
+   CONTROL_JoystickPort = function;
+   }
+
+/*
+===================
+=
+= CONFIG_SetupJoystick
+=
+===================
+*/
+
+void CONFIG_SetupJoystick( int32 scripthandle )
+{
+   int32 i, j;
+   char str[80];
+   char temp[80];
+   int32 function, deadzone;
+   float scale;
+
+   for (i=0;i<MAXJOYBUTTONS;i++)
+      {
+      sprintf(str,"JoystickButton%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapJoyButton( function, i, false );
+      sprintf(str,"JoystickButtonClicked%ld",i);
+      memset(temp,0,sizeof(temp));
+      SCRIPT_GetString( scripthandle,"Controls", str,temp);
+      function = CONFIG_FunctionNameToNum(temp);
+      if (function != -1)
+         CONTROL_MapJoyButton( function, i, true );
+      }
+   // map over the axes
+   for (i=0;i<MAXJOYAXES;i++)
+      {
+        sprintf(str,"JoystickAnalogAxes%ld",i);
+        memset(temp,0,sizeof(temp));
+        SCRIPT_GetString(scripthandle, "Controls", str,temp);
+        function = CONFIG_AnalogNameToNum(temp);
+        //if (function != -1)
+            //{
+            CONTROL_MapAnalogAxis(i,function);
+            //}
+        sprintf(str,"JoystickDigitalAxes%ld_0",i);
+        memset(temp,0,sizeof(temp));
+        SCRIPT_GetString(scripthandle, "Controls", str,temp);
+        function = CONFIG_FunctionNameToNum(temp);
+        if (function != -1)
+            CONTROL_MapDigitalAxis( i, function, 0 );
+        sprintf(str,"JoystickDigitalAxes%ld_1",i);
+        memset(temp,0,sizeof(temp));
+        SCRIPT_GetString(scripthandle, "Controls", str,temp);
+        function = CONFIG_FunctionNameToNum(temp);
+        if (function != -1)
+            CONTROL_MapDigitalAxis( i, function, 1 );
+        sprintf(str,"JoystickAnalogScale%ld",i);
+        SCRIPT_GetFloat(scripthandle, "Controls", str,&scale);
+        CONTROL_SetAnalogAxisScale( i, scale );
+        deadzone = 0;
+        sprintf(str,"JoystickAnalogDeadzone%ld",i);
+        SCRIPT_GetNumber(scripthandle, "Controls", str, &deadzone);
+        CONTROL_SetAnalogAxisDeadzone( i, deadzone);
+      }
+
+   // map over the "top hats"
+   for (i=0; i < MAXJOYHATS; i++)
+   {
+	  for(j=0; j < 8; j++) // 8? because hats can have 8 different values
+	  { 
+		  sprintf(str,"JoystickHat%ld_%ld",i, j);
+		  memset(temp,0,sizeof(temp));
+		  SCRIPT_GetString( scripthandle,"Controls", str,temp);
+		  function = CONFIG_FunctionNameToNum(temp);
+		  if (function != -1)
+		  {
+			  CONTROL_MapJoyHat( function, i, j);	   
+		  }
+	  }
+   }
+
+   // read in JoystickPort
+   SCRIPT_GetNumber( scripthandle, "Controls","JoystickPort",&function);
+   CONTROL_JoystickPort = function;
+   // read in rudder state
+   SCRIPT_GetNumber( scripthandle, "Controls","EnableRudder",&CONTROL_RudderEnabled);
+}
+
+void readsavenames(void)
+{
+    long dummy;
+    short i;
+    char fn[] = "game_.sav";
+    FILE *fil;
+	char fullpathsavefilename[16];
+
+
+    for (i=0;i<10;i++)
+    {
+
+        fn[4] = i+'0';
+
+		// Are we loading a TC?
+		if(game_dir[0] != '\0')
+		{
+			// Yes
+			sprintf(fullpathsavefilename, "%s\\%s", game_dir, fn);
+		}
+		else
+		{
+			// No 
+			sprintf(fullpathsavefilename, "%s", fn);
+		}
+
+        if ((fil = fopen(fullpathsavefilename,"rb")) == NULL ) continue;
+        dfread(&dummy,4,1,fil);
+
+		//	FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116 and 117 only)
+        if(	dummy != BYTEVERSION	 && 
+			dummy != BYTEVERSION_27  &&
+			dummy != BYTEVERSION_28  &&
+			dummy != BYTEVERSION_116 &&
+			dummy != BYTEVERSION_117) continue;
+        // FIX_00092: corrupted saved files making the following saved files invisible (Bryzian)
+		dfread(&dummy,4,1,fil);
+        dfread(&ud.savegame[i][0],19,1,fil);
+        fclose(fil);
+    }
+}
+
+/*
+===================
+=
+= CONFIG_ReadSetup
+=
+===================
+*/
+
+//int32 dukever13;
+
+void CONFIG_ReadSetup( void )
+{
+   int32 dummy;
+   char commmacro[] = COMMMACRO;
+   FILE* setup_file_hdl;
+
+   printf("CONFIG_ReadSetup...\n");
+   
+   if (!SafeFileExists(setupfilename))
+      {
+		// FIX_00011: duke3d.cfg not needed anymore to start the game. Will create a default one
+		//            if not found and use default keys.
+      printf("%s does not exist. Don't forget to set it up!\n" ,setupfilename);
+	  setup_file_hdl = fopen (setupfilename, "w"); // create it...
+	  if(setup_file_hdl)
+		  fclose(setup_file_hdl);
+      }
+
+   CONFIG_SetDefaults();
+   scripthandle = SCRIPT_Load( setupfilename );
+
+   for(dummy = 0;dummy < 10;dummy++)
+   {
+       commmacro[13] = dummy+'0';
+       SCRIPT_GetString( scripthandle, "Comm Setup",commmacro,ud.ridecule[dummy]);
+   }
+
+//   /* DDOI - Check version */
+//   if (!SCRIPT_GetNumber( scripthandle, "Misc", "UseOldVersion", &dukever13))
+//	   dukever13 = 0; // assume 1.5
+//#ifdef VOLUMEONE
+//   dukever13 = 1;
+//#endif
+
+   SCRIPT_GetString( scripthandle, "Comm Setup","PlayerName",&myname[0]);
+
+   dummy = CheckParm("NAME");
+   if( dummy ) strcpy(myname,_argv[dummy+1]);
+   dummy = CheckParm("MAP");
+
+   boardfilename[0] = 0;
+
+	if( dummy )
+	{
+		if (!VOLUMEONE)
+		{
+			strcpy(boardfilename,_argv[dummy+1]);
+			if( strchr(boardfilename,'.') == 0)
+				strcat(boardfilename,".map");
+			printf("Using level: '%s'.\n",boardfilename);
+		}
+		else
+		{
+			Error(EXIT_SUCCESS, "The -map option does not work with the Shareware version of duke3d.grp\n"
+								"Change your duke3d.grp file to the 1.3d version or 1.5 Atomic version\n");
+		}
+	}
+
+   SCRIPT_GetString( scripthandle, "Comm Setup","RTSName",&ud.rtsname[0]);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "Shadows",&ud.shadows);
+   SCRIPT_GetString( scripthandle, "Screen Setup","Password",&ud.pwlockout[0]);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "Detail",&ud.detail);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "Tilt",&ud.screen_tilting);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "Messages",&ud.fta_on);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ScreenWidth",&ScreenWidth);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ScreenHeight",&ScreenHeight);
+   // SCRIPT_GetNumber( scripthandle, "Screen Setup", "ScreenMode",&ScreenMode);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ScreenGamma",&ud.brightness);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ScreenSize",&ud.screen_size);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ExtScreenSize",&ud.extended_screen_size);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "Out",&ud.lockout);
+   SCRIPT_GetNumber( scripthandle, "Screen Setup", "ShowFPS",&ud.tickrate);
+   ud.tickrate &= 1;
+   SCRIPT_GetNumber( scripthandle, "Misc", "Executions",&ud.executions);
+   ud.executions++;
+   SCRIPT_GetNumber( scripthandle, "Misc", "RunMode",&ud.auto_run);
+   SCRIPT_GetNumber( scripthandle, "Misc", "Crosshairs",&ud.crosshair);
+   SCRIPT_GetNumber( scripthandle, "Misc", "ShowCinematics",&ud.showcinematics);
+   SCRIPT_GetNumber( scripthandle, "Misc", "WeaponAutoSwitch",&ud.weaponautoswitch);
+   SCRIPT_GetNumber( scripthandle, "Misc", "HideWeapon",&ud.hideweapon);
+   SCRIPT_GetNumber( scripthandle, "Misc", "ShowWeapon",&ud.showweapons);
+   SCRIPT_GetNumber( scripthandle, "Misc", "AutoAim",&ud.auto_aim);
+	if(ud.auto_aim!=1 && ud.auto_aim != 2)
+		ud.auto_aim = 2; // avoid people missing with the cfg to go in a deadlock
+   SCRIPT_GetNumber( scripthandle, "Misc", "GitDatMdk",&ud.gitdat_mdk);
+   
+   if(ud.mywchoice[0] == 0 && ud.mywchoice[1] == 0)
+   {
+       ud.mywchoice[0] = 3;
+       ud.mywchoice[1] = 4;
+       ud.mywchoice[2] = 5;
+       ud.mywchoice[3] = 7;
+       ud.mywchoice[4] = 8;
+       ud.mywchoice[5] = 6;
+       ud.mywchoice[6] = 0;
+       ud.mywchoice[7] = 2;
+       ud.mywchoice[8] = 9;
+       ud.mywchoice[9] = 1;
+
+       for(dummy=0;dummy<10;dummy++)
+       {
+           sprintf(buf,"WeaponChoice%ld",dummy);
+           SCRIPT_GetNumber( scripthandle, "Misc", buf, &ud.mywchoice[dummy]);
+       }
+    }
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "FXDevice",&FXDevice);
+
+    #if !PLATFORM_DOS   // reimplementation of ASS expects a "SoundScape".
+    if (FXDevice != NumSoundCards)
+        FXDevice = SoundScape;
+    #endif
+
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "MusicDevice",&MusicDevice);
+
+   //#if !PLATFORM_DOS   // reimplementation of ASS expects a "SoundScape".
+   //  if (MusicDevice != NumSoundCards)
+   //     MusicDevice = SoundScape;
+   //#endif
+
+// FIX_00015: Forced NumVoices=8, NumChannels=2, NumBits=16, MixRate=44100, ScreenMode = x(
+//            (ScreenMode has no meaning anymore)
+
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "FXVolume",&FXVolume);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "MusicVolume",&MusicVolume);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "SoundToggle",&SoundToggle);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "MusicToggle",&MusicToggle);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "VoiceToggle",&VoiceToggle);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "AmbienceToggle",&AmbienceToggle);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "OpponentSoundToggle",&OpponentSoundToggle);   
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "NumVoices",&NumVoices);
+   NumVoices = 32;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "NumChannels",&NumChannels);
+   NumChannels = 2;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "NumBits",&NumBits);
+   NumBits = 16;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "MixRate",&MixRate);
+   MixRate = 44100;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "MidiPort",&MidiPort);
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterAddress",&dummy);
+   BlasterConfig.Address = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterType",&dummy);
+   BlasterConfig.Type = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterInterrupt",&dummy);
+   BlasterConfig.Interrupt = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterDma8",&dummy);
+   BlasterConfig.Dma8 = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterDma16",&dummy);
+   BlasterConfig.Dma16 = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "BlasterEmu",&dummy);
+   BlasterConfig.Emu = dummy;
+   SCRIPT_GetNumber( scripthandle, "Sound Setup", "ReverseStereo",&ReverseStereo);
+
+   SCRIPT_GetNumber( scripthandle, "Controls","ControllerType",&ControllerType);
+   SCRIPT_GetNumber( scripthandle, "Controls","MouseAimingFlipped",&ud.mouseflip);
+   SCRIPT_GetNumber( scripthandle, "Controls","MouseAiming",&MouseAiming);
+   SCRIPT_GetNumber( scripthandle, "Controls","GameMouseAiming",(int32 *)&ps[0].aim_mode);
+   SCRIPT_GetNumber( scripthandle, "Controls","AimingFlag",(int32 *)&myaimmode);
+
+   CONTROL_ClearAssignments();
+
+   CONFIG_ReadKeys();
+
+   switch (ControllerType)
+      {
+        case controltype_keyboardandmouse:
+            {
+				CONFIG_SetupMouse(scripthandle);
+			}
+            break;
+        case controltype_keyboardandjoystick:
+        case controltype_keyboardandflightstick:
+        case controltype_keyboardandthrustmaster:
+			{
+            CONTROL_JoystickEnabled = 1;
+            CONFIG_SetupJoystick(scripthandle);
+			}
+            break;
+        case controltype_keyboardandgamepad:
+            {
+				CONFIG_SetupGamePad(scripthandle);
+			}
+            break;
+        case controltype_joystickandmouse:
+            {
+
+                CONTROL_JoystickEnabled = 1;
+                CONFIG_SetupJoystick(scripthandle);
+                CONFIG_SetupMouse(scripthandle);
+            }
+            break;
+        default:
+            {
+				CONFIG_SetupMouse(scripthandle);
+			}
+      }
+   setupread = 1;
+   }
+
+/*
+===================
+=
+= CONFIG_WriteSetup
+=
+===================
+*/
+
+void CONFIG_WriteSetup( void )
+   {
+   int32 dummy, i;
+   char commmacro[] = COMMMACRO;
+
+   if (!setupread) return;
+
+   printf("CONFIG_WriteSetup...\n");
+
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Shadows",ud.shadows,false,false);
+   SCRIPT_PutString( scripthandle, "Screen Setup", "Password",ud.pwlockout);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Detail",ud.detail,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Tilt",ud.screen_tilting,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Messages",ud.fta_on,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Out",ud.lockout,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ShowFPS",ud.tickrate&1,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ScreenWidth",xdim,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ScreenHeight",ydim,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "Fullscreen",BFullScreen,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "FXVolume",FXVolume,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "MusicVolume",MusicVolume,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "FXDevice",FXDevice,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "MusicDevice",MusicDevice,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "SoundToggle",SoundToggle,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "VoiceToggle",VoiceToggle,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "AmbienceToggle",AmbienceToggle,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "OpponentSoundToggle",OpponentSoundToggle,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "MusicToggle",MusicToggle,false,false);
+   SCRIPT_PutNumber( scripthandle, "Sound Setup", "ReverseStereo",ReverseStereo,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ScreenSize",ud.screen_size,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ExtScreenSize",ud.extended_screen_size,false,false);
+   SCRIPT_PutNumber( scripthandle, "Screen Setup", "ScreenGamma",ud.brightness,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "Executions",ud.executions,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "RunMode",ud.auto_run,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "Crosshairs",ud.crosshair,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "ShowCinematics",ud.showcinematics,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "HideWeapon",ud.hideweapon,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "ShowWeapon",ud.showweapons,false,false);
+   SCRIPT_PutNumber( scripthandle, "Misc", "WeaponAutoSwitch",ud.weaponautoswitch,false,false);
+   if( nHostForceDisableAutoaim == 0) // do not save Host request to have AutoAim Off.
+	   SCRIPT_PutNumber( scripthandle, "Misc", "AutoAim",ud.auto_aim,false,false);
+   SCRIPT_PutNumber( scripthandle, "Controls", "MouseAimingFlipped",ud.mouseflip,false,false);
+   SCRIPT_PutNumber( scripthandle, "Controls","MouseAiming",MouseAiming,false,false);
+   SCRIPT_PutNumber( scripthandle, "Controls","GameMouseAiming",(int32) ps[myconnectindex].aim_mode,false,false);
+   SCRIPT_PutNumber( scripthandle, "Controls","AimingFlag",(long) myaimmode,false,false);
+   
+	// FIX_00016: Build in Keyboard/mouse setup. Mouse now faster.
+	for(i=0; i<MAXMOUSEBUTTONS; i++)
+	{
+		sprintf(tempbuf, "MouseButton%d", i);
+		SCRIPT_PutString(scripthandle, "Controls", tempbuf, 
+			(MouseMapping[i]!=-1)?CONFIG_FunctionNumToName(MouseMapping[i]):"");
+	}
+
+	for (i=0;i<MAXMOUSEAXES*2;i++)
+	{
+		sprintf(tempbuf, "MouseDigitalAxes%d_%d", i>>1, i&1);
+		SCRIPT_PutString(scripthandle, "Controls", tempbuf, 
+			(MouseDigitalAxeMapping[i>>1][i&1]!=-1)?CONFIG_FunctionNumToName(MouseDigitalAxeMapping[i>>1][i&1]):"");
+	}
+
+   for(i=0; i<NUMGAMEFUNCTIONS; i++) // write keys
+   {
+		SCRIPT_PutDoubleString(
+			scripthandle, 
+			"KeyDefinitions", 
+			gamefunctions[i], 
+			KB_ScanCodeToString( KeyMapping[i].key1 )?KB_ScanCodeToString( KeyMapping[i].key1 ):"", 
+			KB_ScanCodeToString( KeyMapping[i].key2 )?KB_ScanCodeToString( KeyMapping[i].key2 ):"");
+	}
+
+   for(dummy=0;dummy<10;dummy++)
+   {
+       sprintf(buf,"WeaponChoice%ld",dummy);
+       SCRIPT_PutNumber( scripthandle, "Misc",buf,ud.mywchoice[dummy],false,false);
+   }
+
+   dummy = CONTROL_GetMouseSensitivity_X();
+   SCRIPT_PutNumber( scripthandle, "Controls","MouseSensitivity_X_Rancid",dummy,false,false);
+
+   dummy = CONTROL_GetMouseSensitivity_Y();
+   SCRIPT_PutNumber( scripthandle, "Controls","MouseSensitivity_Y_Rancid",dummy,false,false);
+
+   SCRIPT_PutNumber( scripthandle, "Controls","ControllerType",ControllerType,false,false);
+
+   SCRIPT_PutString( scripthandle, "Comm Setup","PlayerName",myname);
+   SCRIPT_PutString( scripthandle, "Comm Setup","RTSName",ud.rtsname);
+
+   for(dummy = 0;dummy < 10;dummy++)
+   {
+       commmacro[13] = dummy+'0';
+       SCRIPT_PutString( scripthandle, "Comm Setup",commmacro,ud.ridecule[dummy]);
+   }
+
+   SCRIPT_Save (scripthandle, setupfilename);
+   SCRIPT_Free (scripthandle);
+   }
+
--- /dev/null
+++ b/Game/src/config.h
@@ -1,0 +1,44 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef _config_public
+#define _config_public
+
+#define SETUPNAMEPARM "SETUPFILE"
+#define COMMMACRO "CommbatMacro# "
+
+extern int32 ControllerType;
+extern int32 MouseAiming;
+extern int32 ScreenMode;
+extern int32 ScreenWidth;
+extern int32 ScreenHeight;
+extern int32 BFullScreen;
+
+void CONFIG_ReadSetup( void );
+void CONFIG_GetSetupFilename( void );
+void CONFIG_WriteSetup( void );
+
+#endif
--- /dev/null
+++ b/Game/src/console.c
@@ -1,0 +1,627 @@
+#include "duke3d.h"
+#include "console.h"
+#include "cvars.h"
+#include "cvar_defs.h"
+#include <stdarg.h>
+
+// For autoexec.cfg
+#include <stdio.h>
+
+#define MAX_STRING_LENGTH 64
+#define MAX_CONSOLE_STRING_LENGTH 70
+#define MAX_CVAR_ARGS 10
+#define MAX_CONSOLE_VIEWABLE 10
+
+#define BUILD_NUMBER "Build 19"
+
+typedef struct console_element
+{
+    char text[MAX_CONSOLE_STRING_LENGTH];
+    void* prev;
+    void* next;
+}CONSOLEELEMENT;
+
+// Private member functions
+void CONSOLE_InsertUsedCommand(const char* szUsedCommand);
+void CONSOLE_ClearUsedCommandList();
+void CONSOLE_RecalculateDirtyBuffer();
+
+// console argument tracker
+int argc;
+char argv[MAX_CVAR_ARGS][MAX_CONSOLE_STRING_LENGTH];
+// Console entries, prepending linked list
+CONSOLEELEMENT *console_buffer = NULL;
+// Current viewed setion of the console
+CONSOLEELEMENT *p_console_current_view = NULL;
+// Previously entered commands:
+CONSOLEELEMENT *console_used_command_list = NULL;
+// Previously entered commands:
+CONSOLEELEMENT *console_used_command_list_current = NULL;
+
+// dirty buffer
+char dirty_buffer[MAX_CONSOLE_STRING_LENGTH];
+
+// dirty buffer control vars
+int console_cursor_pos = 0; //without spaces
+int console_num_spaces = 0; //number of spaces
+
+// Toggle for the console state
+int nConsole_Active = 0;
+
+// Initialize the console
+void CONSOLE_Init()
+{
+    CONSOLE_Reset();
+
+    // Console Variable init
+    CVAR_RegisterDefaultCvarBindings();
+
+    // read in our startup.txt
+    CONSOLE_ParseStartupScript();
+
+    CONSOLE_Printf("Type \'HELP\' for help with console Commands.");
+}
+
+void CONSOLE_Reset()
+{
+    CONSOLEELEMENT *pElement;
+    CONSOLEELEMENT *pDelElement;
+
+    // Free the list if it exists
+    pElement = console_buffer;
+    while(pElement)
+    {
+        pDelElement = pElement;
+        pElement = (CONSOLEELEMENT*)pElement->next;
+
+        free(pDelElement);
+    }
+
+    console_buffer = NULL;
+    p_console_current_view = NULL;
+}
+
+void CONSOLE_Term()
+{
+    // Do any clean up releasing to the console
+    CONSOLE_Reset();
+	CONSOLE_ClearUsedCommandList();
+}
+
+void CONSOLE_ParseStartupScript()
+{
+	// FIX_00017: heavy autoexec.cfg not needed anymore.
+    char *sStartupScript = "startup.cfg";
+
+    FILE* fp = fopen(sStartupScript, "r");
+
+    // If the file exists
+    if(NULL != fp)
+    {
+        char line[128];
+        memset(line, 0, 128);
+
+        while(fgets(line ,128-1, fp) != NULL)
+        {
+            CONSOLE_ParseCommand(line);
+
+            memset(line, 0, 128);
+        }
+        fclose(fp);
+    }
+}
+
+void CONSOLE_HandleInput()
+{
+    char* lastKey;
+    int tmp;
+
+    if(g_CV_classic)
+    {
+        nConsole_Active = 0;
+        return;
+    }
+
+    // See if we should toggle the console
+    if(ACTION(gamefunc_Console))
+    {
+        nConsole_Active = !nConsole_Active;
+
+		CONTROL_ClearAction(gamefunc_Console);
+        //KB_ClearKeyDown(sc_Tilde);
+
+
+		if(nConsole_Active)
+		{
+			// If this is a singleplayer game, let's pause the game when the console is active
+			if (numplayers == 1)
+			{
+				if (!ud.recstat) //check if we are not playing a Demo and console is active
+				{
+					ud.pause_on = 1;
+				}else
+				{
+					ud.pause_on = 0;
+				}
+			}			
+		}
+		else
+        // Bug fix: make sure the frag bar displays after console
+        // is hidden
+        //if(!nConsole_Active)
+        {
+            if ( ud.multimode > 1 && ud.coop != 1 )
+            {
+                displayfragbar(); 
+            }
+			if(numplayers<2)
+				ud.pause_on = 0;
+			// FIX_00078: Out Of Synch error (multiplayer) when using console in pause mode
+        }
+
+        return;
+    }
+
+    // If the console is not active, there's no need to process input
+    if(!nConsole_Active)
+    {
+        return;
+    }
+
+    switch(KB_GetLastScanCode()) 
+    {
+    case sc_Space:
+        {
+            if(strlen(dirty_buffer) < MAX_CONSOLE_STRING_LENGTH-2)
+            {
+                strncat(dirty_buffer, " ", 1);
+                console_num_spaces++;
+            }
+        }
+        break;
+    case sc_Delete:
+    case sc_BackSpace:
+        {
+            tmp = strlen(dirty_buffer);
+            if(tmp > 0)
+            {
+                if( dirty_buffer[tmp - 1] == ' ' )
+                {
+                    dirty_buffer[tmp - 1] = '\0';
+                    console_num_spaces--;
+                }
+                else
+                {
+                    dirty_buffer[tmp - 1] = '\0';
+                    console_cursor_pos--;
+                }
+            }
+        }
+        break;
+        //Ignore list
+    case sc_LeftShift:
+    case sc_RightShift:
+    case sc_PrintScreen:
+    case sc_Tab:
+    case sc_NumLock:
+    case sc_LeftAlt:
+    case sc_LeftControl:
+    case sc_CapsLock:
+    case sc_Bad:
+    case sc_LeftArrow:
+    case sc_RightArrow:
+    case sc_Insert:
+    case sc_Home: //this should take us to the top of the list
+    case sc_RightAlt:
+    case sc_RightControl:
+    case sc_Tilde: //ignore
+        {
+        }break;
+    case sc_kpad_Enter:
+    case sc_Enter:
+        {
+            //If console_buffer[0] strlen() != 0            
+            //1. Push the dirty_buffer unto the console_buffer
+            //2. parse the text
+
+            CONSOLE_Printf("%s", dirty_buffer);
+            console_cursor_pos = 0;
+            console_num_spaces = 0;
+
+			CONSOLE_InsertUsedCommand(dirty_buffer);
+            CONSOLE_ParseCommand(dirty_buffer);
+
+            memset(dirty_buffer, 0, MAX_CONSOLE_STRING_LENGTH);
+
+        }break;
+    case sc_UpArrow:
+        {
+			/*
+            if(p_console_current_view->next != NULL)
+            {
+                p_console_current_view = p_console_current_view->next;
+            }
+			*/
+			if(console_used_command_list_current == NULL)
+			{
+				if(NULL == console_used_command_list)
+				{
+					break;
+				}
+				console_used_command_list_current = console_used_command_list;
+				sprintf(dirty_buffer, "%s", console_used_command_list_current->text);
+				CONSOLE_RecalculateDirtyBuffer();
+				break;
+			}
+
+            if(console_used_command_list_current->next != NULL)
+            {
+                console_used_command_list_current = console_used_command_list_current->next;
+				sprintf(dirty_buffer, "%s", console_used_command_list_current->text);
+				CONSOLE_RecalculateDirtyBuffer();
+            }else
+            if(console_used_command_list_current != NULL)
+			{
+				sprintf(dirty_buffer, "%s", console_used_command_list_current->text);
+				CONSOLE_RecalculateDirtyBuffer();
+			}
+
+        }break;
+    case sc_PgUp:
+        {
+            int i;
+            for(i = 0; i < MAX_CONSOLE_VIEWABLE; i++)
+            {
+                if(p_console_current_view->next != NULL)
+                {
+                    p_console_current_view = p_console_current_view->next;
+                } 
+                else
+                {
+                    break;
+                }
+            }
+        }break;
+    case sc_DownArrow:
+        {
+			/*
+            if(p_console_current_view->prev != NULL)
+            {
+                p_console_current_view = p_console_current_view->prev;
+            }
+			*/
+            if(console_used_command_list_current != NULL)
+            {
+				if(console_used_command_list_current->prev != NULL)
+				{
+					console_used_command_list_current = console_used_command_list_current->prev;
+					sprintf(dirty_buffer, "%s", console_used_command_list_current->text);
+					CONSOLE_RecalculateDirtyBuffer();
+				}
+			}
+        }break;
+    case sc_PgDn:
+        {
+            int i;
+            for(i = 0; i < MAX_CONSOLE_VIEWABLE; i++)
+            {
+                if(p_console_current_view->prev != NULL)
+                {
+                    p_console_current_view = p_console_current_view->prev;
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }break;
+    case sc_End:
+        {
+            p_console_current_view = console_buffer;
+        }break;
+    case sc_Escape:
+        {
+            nConsole_Active = 0;
+            KB_ClearKeyDown(sc_Escape);
+			// FIX_00057: Using ESC to get out of the console mode wouldn't take pause mode off
+			if(numplayers<2)
+				ud.pause_on = 0;
+			// FIX_00078: Out Of Synch error (multiplayer) when using console in pause mode
+        }break;
+    default:
+        {
+            if(strlen(dirty_buffer) < MAX_CONSOLE_STRING_LENGTH-2)
+            {
+                lastKey = KB_ScanCodeToString(KB_GetLastScanCode());
+
+                if(lastKey)
+                {
+                    strncat(dirty_buffer, lastKey, 1);
+                    console_cursor_pos++;
+                    //printf("Key %s : %s\n", lastKey, console_buffer[0]);
+                }
+            }
+
+        }
+        break;
+    }
+
+
+    KB_ClearLastScanCode(); 
+}
+
+void CONSOLE_Render()
+{
+    if(g_CV_classic)
+    {
+        return;
+    }
+
+    // Let the Cvars draw something if they need to.
+    CVAR_Render();
+
+    if(nConsole_Active)
+    {
+        int x,y, y1, y2;
+        int i, iCurHeight = 0, iCurWidth = 0;
+        int iYOffset = 3; //offset for the console text
+        CONSOLEELEMENT *pElement;
+
+        y1 = 0; 
+        y2 = (ydim / 2) - ((ydim / 2)/12);
+
+        // Draw console background
+        for(y=y1;y<y2;y+=128)
+        {
+            for(x=0;x<xdim;x+=128)
+            {
+                rotatesprite(x<<16,y<<16,65536L,0,BIGHOLE,8,0,(g_CV_TransConsole ? 1:0)+8+16+64+128,0,y1,xdim-1,y2-1);
+            }
+        }
+
+        // Draw bottom egde of console
+        rotatesprite(78<<16,94<<16,65536L,512,WINDOWBORDER1,24,0,2+8,0,0,xdim-1,ydim-1);
+        rotatesprite(238<<16,94<<16,65536L,512,WINDOWBORDER1,24,0,2+8,0,0,xdim-1,ydim-1);
+
+        // Draw the contents of the console buffer
+        pElement = p_console_current_view;//console_buffer;
+        for(i = 0; i < MAX_CONSOLE_VIEWABLE; i++)
+        {
+            if(!pElement)
+            {
+                break;
+            }
+
+            minitext(5,(8*(9-i)) + iYOffset,
+                     pElement->text,
+                     g_CV_console_text_color,
+                     10+16);
+
+             pElement = (CONSOLEELEMENT*)pElement->next;
+
+        }
+
+        // Set the height of the new line
+        iCurHeight = (8*10) + iYOffset;
+
+        // Draw dirty buffer
+        minitext(5, iCurHeight, dirty_buffer, 0,10+16);
+
+        // Calculate the location of the cursor
+        iCurWidth =  (console_cursor_pos*4) + (console_num_spaces*5) +5;
+
+        //Draw the version number
+        minitext(283, iCurHeight, BUILD_NUMBER, 17,10+16);
+
+        // Draw the cursor //Change the color every second
+        minitext(iCurWidth, iCurHeight,"_",(time(NULL)%2)+1,10+16);        
+    }
+    else 
+    if(g_CV_num_console_lines > 0)
+    {
+        int i, iYOffset = 3; //offset for the console text
+        CONSOLEELEMENT *pElement;
+        // Draw the contents of the console buffer
+        pElement = console_buffer;
+        for(i = 0; i < g_CV_num_console_lines; i++)
+        {
+            if(!pElement)
+            {
+                break;
+            }
+
+            minitext(5,(8*((g_CV_num_console_lines-1)-i)) + iYOffset,
+                     pElement->text,
+                     g_CV_console_text_color,
+                     10+16);
+
+             pElement = (CONSOLEELEMENT*)pElement->next;
+        }
+    }
+}
+
+void CONSOLE_ParseCommand(char* command)
+{
+    char *cvar;
+    char *token;
+    int i, numCvars;
+    argc = 0;
+
+    if( strlen(command) < 1)
+    {
+        return;
+    }
+
+    // Split the command into cvar and args
+    cvar = strtok(command, " \r\n");
+
+    for(i = 0; i < MAX_CVAR_ARGS; i++)
+    {
+        token = strtok(NULL, " ");
+
+        if(!token)
+        {
+            break;
+        }
+
+        sprintf(argv[i], "%s", token);
+        argc++;
+    }
+
+    // Cycle through our cvar list and look for this keyword
+    numCvars = CVAR_GetNumCvarBindings();
+    for(i = 0; i < numCvars; i++)
+    {
+        cvar_binding* binding = CVAR_GetCvarBinding(i);
+
+        // Did we find it?
+        if ( strcmpi(cvar, binding->name) == 0 )
+        {
+            binding->function(binding);
+            break;
+        }
+    }
+
+}
+
+
+void CONSOLE_InsertUsedCommand(const char* szUsedCommand)
+{
+    //create a new element in the list, and add it to the front
+    CONSOLEELEMENT *pElement = (CONSOLEELEMENT*)malloc(sizeof(CONSOLEELEMENT));
+    if(pElement)
+    {
+        //Store our newly created member as the prev address
+        if(NULL != console_used_command_list)
+        {
+            console_used_command_list->prev = pElement;
+        }
+        // Set the next pointer to the front of the list
+        pElement->next = console_used_command_list;
+
+        console_used_command_list_current = NULL;//pElement;
+
+        // Prepend the entry. This entry is now the head of the list.
+        console_used_command_list = pElement;
+
+        // Make sure we NULL out the prev for our top level element
+        pElement->prev = NULL;
+
+        //sprintf(console_buffer->text, "%s", msg);
+        memset(console_used_command_list->text, 0, MAX_CONSOLE_STRING_LENGTH);
+        strncpy(console_used_command_list->text, szUsedCommand, MAX_CONSOLE_STRING_LENGTH-2);
+    }
+}
+
+void CONSOLE_ClearUsedCommandList()
+{
+    CONSOLEELEMENT *pElement;
+    CONSOLEELEMENT *pDelElement;
+
+    // Free the list if it exists
+    pElement = console_used_command_list;
+    while(pElement)
+    {
+        pDelElement = pElement;
+        pElement = (CONSOLEELEMENT*)pElement->next;
+
+        free(pDelElement);
+    }
+
+    console_used_command_list = NULL;
+    console_used_command_list_current = NULL;
+}
+
+
+void CONSOLE_RecalculateDirtyBuffer()
+{
+	int len;
+	int l;
+
+	console_cursor_pos = 0; //without spaces
+	console_num_spaces = 0; //number of spac
+
+	len = strlen(dirty_buffer);
+
+	for(l = 0; l < len; ++l)
+	{
+		if(dirty_buffer[l] == ' ')
+		{
+			++console_num_spaces;
+		}
+		else
+		{
+			++console_cursor_pos;
+		}
+	}
+}
+
+
+void CONSOLE_Printf(const char *newmsg, ...)
+{
+    int tmp;
+    CONSOLEELEMENT *pElement;
+    va_list		argptr;
+    char		msg[512];//[MAX_CONSOLE_STRING_LENGTH];
+    va_start (argptr,newmsg);
+    vsprintf (msg, newmsg, argptr);
+    va_end (argptr);
+
+    //create a new element in the list, and add it to the front
+    pElement = (CONSOLEELEMENT*)malloc(sizeof(CONSOLEELEMENT));
+    if(pElement)
+    {
+        //Store our newly created member as the prev address
+        if(NULL != console_buffer)
+        {
+            console_buffer->prev = pElement;
+        }
+        // Set the next pointer to the front of the list
+        pElement->next = console_buffer;
+
+        // Set our view, if they are at the bottom of the list.
+        // Otherwise, if we set it everytime, Folks might lose
+        // what they were looking for in the output, if they
+        // were using pgup and pgdn to scroll through the entries.
+        if(p_console_current_view == console_buffer)
+        {
+            p_console_current_view = pElement;
+        }
+
+        // Prepend the entry. This entry is now the head of the list.
+        console_buffer = pElement;
+
+        // Make sure we NULL out the prev for our top level element
+        pElement->prev = NULL;
+
+        //sprintf(console_buffer->text, "%s", msg);
+        memset(console_buffer->text, 0, MAX_CONSOLE_STRING_LENGTH);
+        strncpy(console_buffer->text, msg, MAX_CONSOLE_STRING_LENGTH-2);
+    }
+
+}
+
+// Get the current number of args for this keyword
+int CONSOLE_GetArgc()
+{
+    return argc;
+}
+
+// Get the current list of args for this keyword
+char* CONSOLE_GetArgv(unsigned int var)
+{
+    return argv[var];
+}
+
+// Is our console showing?
+int CONSOLE_IsActive()
+{
+	
+    return nConsole_Active;
+}
+
+// Set our consople active or not.
+void CONSOLE_SetActive(int i)
+{
+	nConsole_Active = (i == 0) ? 0 : 1;
+}
\ No newline at end of file
--- /dev/null
+++ b/Game/src/console.h
@@ -1,0 +1,19 @@
+#ifndef _CONSOLE_H_
+#define _CONSOLE_H_
+
+
+// Public member functions
+void     CONSOLE_Init();
+void     CONSOLE_Reset();
+void     CONSOLE_Term();
+void     CONSOLE_ParseStartupScript();
+void     CONSOLE_HandleInput();
+void     CONSOLE_Render();
+void     CONSOLE_ParseCommand(char* command);
+void     CONSOLE_Printf(const char *newmsg, ...);
+int      CONSOLE_GetArgc();
+char*    CONSOLE_GetArgv(unsigned int var);
+int      CONSOLE_IsActive();
+void     CONSOLE_SetActive(int i); 
+
+#endif
--- /dev/null
+++ b/Game/src/control.c
@@ -1,0 +1,928 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+#include "duke3d.h"
+#include "control.h"
+#include "mouse.h"
+#include "joystick.h"
+
+//***************************************************************************
+//
+// GLOBALS
+//
+//***************************************************************************
+
+// FIX_00018: Removed the "smoothmouse" option. This was just a bad fix. Mouse is now faster,
+//            smoother.
+// extern int g_CV_EnableSmoothMouse;
+// extern int g_CV_SmoothMouseSensX;
+// extern int g_CV_SmoothMouseSensY;
+
+uint32   CONTROL_RudderEnabled;
+boolean  CONTROL_MousePresent;
+boolean  CONTROL_JoysPresent[ MaxJoys ];
+boolean  CONTROL_MouseEnabled;
+boolean  CONTROL_JoystickEnabled;
+byte     CONTROL_JoystickPort;
+uint32   CONTROL_MouseButtonState1;
+uint32   CONTROL_MouseButtonState2;
+// FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+uint32   CONTROL_MouseDigitalAxisState1;
+uint32   CONTROL_MouseDigitalAxisState2;
+//uint32   CONTROL_ButtonHeldState1;
+//uint32   CONTROL_ButtonHeldState2;
+uint32   CONTROL_JoyButtonState1;
+uint32   CONTROL_JoyButtonState2;
+
+uint32   CONTROL_JoyHatState1; //[MAXJOYHATS];
+uint32   CONTROL_JoyHatState2; //[MAXJOYHATS];
+
+
+static short mouseButtons = 0;
+static short lastmousebuttons = 0;
+
+static short joyHats[MAXJOYHATS];
+static short lastjoyHats[MAXJOYHATS];
+
+static int32 mousePositionX = 0;
+static int32 mousePositionY = 0;
+static int32 mouseRelativeX = 0;
+static int32 mouseRelativeY = 0;
+
+//***************************************************************************
+//
+// FUNCTIONS
+//
+//***************************************************************************
+
+// Joystick/Gamepad bindings
+static int32 JoyAxisMapping[MAXJOYAXES];
+static int32 JoyHatMapping[MAXJOYHATS][8];
+static int32 JoyButtonMapping[MAXJOYBUTTONS];
+static float JoyAnalogScale[MAXJOYAXES];
+static int32 JoyAnalogDeadzone[MAXJOYAXES];
+
+int ACTION(int i)
+{
+
+	//Keyboard input
+	if( (KB_KeyDown[KeyMapping[i].key1]) ||
+		(KB_KeyDown[KeyMapping[i].key2])
+	  )
+	{
+		return 1;
+	}
+
+
+	// Check mouse
+	if((ControllerType == controltype_keyboardandmouse) ||
+		(ControllerType == controltype_joystickandmouse))
+	{
+		//Mouse buttons	
+		if ((i)>31) 
+		{
+			if((CONTROL_MouseButtonState2>>( (i) - 32) ) & 1)
+			{
+				return 1;
+			}
+		}
+		else
+		{
+			if((CONTROL_MouseButtonState1>> (i) ) & 1)
+			{
+				return 1;
+			}
+		}
+
+		// FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+
+		//Mouse Digital Axes	
+		if ((i)>31) 
+		{
+			if((CONTROL_MouseDigitalAxisState2>>( (i) - 32) ) & 1)
+			{
+				return 1;
+			}
+		}
+		else
+		{
+			if((CONTROL_MouseDigitalAxisState1>> (i) ) & 1)
+			{
+				return 1;
+			}
+		}
+	}
+
+
+	// Check joystick
+	if((ControllerType == controltype_keyboardandjoystick) ||
+		(ControllerType == controltype_joystickandmouse)
+	  )
+	{
+
+		if ((i)>31) 
+		{
+			// Check the joystick
+			if( (CONTROL_JoyButtonState2 >> (i - 32)) & 1)
+			{
+				return 1;
+			}
+
+			// Check the hats
+			if( (CONTROL_JoyHatState2 >> (i - 32)) & 1)
+			{
+				return 1;
+			}
+
+		}
+		else
+		{
+			if( (CONTROL_JoyButtonState1 >> i) & 1)
+			{
+				return 1;
+			}
+
+			// Check the hats
+			if( (CONTROL_JoyHatState1 >> i) & 1)
+			{
+				return 1;
+			}
+		}
+
+	}
+	
+	return 0;
+}
+
+
+int RESET_ACTION(int i)
+{
+	KB_KeyDown[KeyMapping[i].key1] = 0;
+	KB_KeyDown[KeyMapping[i].key2] = 0;
+
+	return 0;
+}
+
+static void SETMOUSEBUTTON(int i)
+{
+
+	//CONTROL_MouseButtonState1 |= (1<<i);
+	
+	int b;
+	
+	if (i < 32) 
+	{
+		b = 1 << i;
+		
+		CONTROL_MouseButtonState1 |= b;
+	} 
+	else 
+	{
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_MouseButtonState2 |= b;
+	}
+	
+}
+
+void RESMOUSEBUTTON(int i)
+{
+
+	//CONTROL_MouseButtonState1 &= ~(1<<i);
+	
+	int b;
+	
+	if (i < 32) {
+		b = 1 << i;
+		
+		CONTROL_MouseButtonState1 &= ~b;
+	} else {
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_MouseButtonState2 &= ~b;
+	}
+	
+}
+
+// FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+
+void SETMOUSEDIGITALAXIS(int i)
+{
+ 
+	int b;
+
+	if (i<0) 
+		return;
+
+	if (i < 32) 
+	{
+		b = 1 << i;
+		CONTROL_MouseDigitalAxisState1 |= b;
+	} 
+	else 
+	{
+		i -= 32;
+		b = 1 << i;
+		CONTROL_MouseDigitalAxisState2 |= b;
+	}
+}
+
+void RESMOUSEDIGITALAXIS(int i)
+{
+ 
+	int b;
+
+	if (i<0) 
+		return;
+
+	if (i < 32) 
+	{
+		b = 1 << i;
+		CONTROL_MouseDigitalAxisState1 &= ~b;
+	} 
+	else 
+	{
+		i -= 32;
+		b = 1 << i;
+		CONTROL_MouseDigitalAxisState2 &= ~b;
+	}
+}
+
+static void SETJOYBUTTON(int i)
+{
+	//CONTROL_JoyButtonState |= (1<<i);
+	int b;
+	
+	if (i < 32) 
+	{
+		b = 1 << i;
+		
+		CONTROL_JoyButtonState1 |= b;
+	} 
+	else 
+	{
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_JoyButtonState2 |= b;
+	}
+}
+static void RESJOYBUTTON(int i)
+{
+	int b;
+	
+	if (i < 32) {
+		b = 1 << i;
+		
+		CONTROL_JoyButtonState1 &= ~b;
+	} else {
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_JoyButtonState2 &= ~b;
+	}
+}
+
+static void SETHATBUTTON(int i)
+{
+	//CONTROL_JoyHatState1 |= (1<<i);
+
+	int b;
+	
+	if (i < 32) 
+	{
+		b = 1 << i;
+		
+		CONTROL_JoyHatState1 |= b;
+	} 
+	else 
+	{
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_JoyHatState2 |= b;
+	}
+}
+
+static void RESHATBUTTON(int i)
+{
+
+	//CONTROL_JoyHatState1 &= ~(1<<i);
+	int b;
+	
+	if (i < 32) {
+		b = 1 << i;
+		
+		CONTROL_JoyHatState1 &= ~b;
+	} else {
+		i -= 32;
+		
+		b = 1 << i;
+		
+		CONTROL_JoyHatState2 &= ~b;
+	}
+}
+
+void CONTROL_UpdateKeyboardState(int key, int pressed)
+{
+	/*
+
+		if(pressed)
+		{
+			CONTROL_KeyStates[key] = 1;
+		}
+		else
+		{
+			CONTROL_KeyStates[key] = 0;
+		}
+		*/
+	/*
+	int i;
+	
+	for (i = 0; i < MAXGAMEBUTTONS; i++) 
+	{
+		if (KeyMapping[i].key_active == false) 
+		{
+			continue;
+		}
+	
+		if (KeyMapping[i].key1 == key || 
+			KeyMapping[i].key2 == key) 
+		{
+			
+			if (pressed) 
+			{
+				SETBUTTON(i);
+			} 
+			else 
+			{
+				RESBUTTON(i);
+			}
+		}
+	}
+	*/
+}
+
+void CONTROL_MapKey( int32 which, kb_scancode key1, kb_scancode key2 )
+{	
+	// FIX_00020: Protect you from assigning a function to the ESC key through duke3d.cfg
+	if(key1==sc_Escape || key2==sc_Escape)
+	{
+		if(key1==sc_Escape)
+			key1=0;
+		else
+			key2=0;
+
+		printf("Discarding ESCAPE key for function : %s\n", gamefunctions[which]);
+	}
+
+	if(key1 || key2)
+		KeyMapping[which].key_active = true;
+	else 
+		KeyMapping[which].key_active = false;
+
+	KeyMapping[which].key1 = key1;
+	KeyMapping[which].key2 = key2;
+}
+
+void CONTROL_MapButton
+        (
+        int32 whichfunction,
+        int32 whichbutton,
+        boolean clicked_or_doubleclicked
+        )
+{
+    if(clicked_or_doubleclicked)
+	return; // TODO
+
+    if(whichbutton < 0 || whichbutton >= MAXMOUSEBUTTONS)
+	    return;
+
+    MouseMapping[whichbutton] = whichfunction;
+}
+
+void CONTROL_MapJoyButton(int32 whichfunction, int32 whichbutton, boolean doubleclicked)
+{
+    if(whichbutton < 0 || whichbutton >= MAXJOYBUTTONS)
+    {
+        return;
+    }
+
+    if(doubleclicked)
+	return; // TODO
+
+    JoyButtonMapping[whichbutton] = whichfunction;
+}
+
+void CONTROL_MapJoyHat(int32 whichfunction, int32 whichhat, int32 whichvalue)
+{
+    if(whichhat < 0 || whichhat >= MAXJOYHATS)
+    {
+        return;
+    }
+
+    JoyHatMapping[whichhat][whichvalue] = whichfunction;
+}
+
+void CONTROL_DefineFlag( int32 which, boolean toggle )
+{
+	// STUBBED("CONTROL_DefineFlag");
+}
+
+boolean CONTROL_FlagActive( int32 which )
+{
+	STUBBED("CONTROL_FlagActive");
+	return false;
+}
+
+void CONTROL_ClearAssignments( void )
+{
+	STUBBED("CONTROL_ClearAssignments");
+}
+
+void CONTROL_GetUserInput( UserInput *info )
+{
+	STUBBED("CONTROL_GetUserInput");
+}
+
+void CONTROL_GetInput( ControlInfo *info )
+{
+    int32 sens_X = CONTROL_GetMouseSensitivity_X();
+	int32 sens_Y = CONTROL_GetMouseSensitivity_Y();
+    int32 mx = 0, my = 0;
+    int i, j;
+
+	memset(info, '\0', sizeof (ControlInfo));
+
+	//info->dx = info->dz = 0;
+
+    _handle_events(); // get the very last mouse position before reading it.
+	MOUSE_GetDelta(&mx,&my);
+	
+	//GetCursorPos(&point);
+	//	SDL_GetMouseState(&x, &y);
+	//	SDL_WarpMouse(160, 100);
+	//mx = (-xx+point.x)<<2;
+	//my = (-yy+point.y)<<2;
+	//xx=point.x; yy=point.y;
+
+	info->dyaw = (mx * sens_X);
+
+	switch(ControllerType)
+	{
+		case controltype_keyboardandjoystick:
+			{
+			}
+			break;
+
+		case controltype_joystickandmouse:
+			// Not sure what I was thinking here...
+			// Commented this out because it totally breaks smooth mouse etc...
+			/*
+			{
+				// Mouse should use pitch instead of forward movement.
+				info->dpitch = my * sens*2;
+			}
+			break;
+			*/
+
+		default:
+			{
+				// If mouse aim is active
+				if( myaimmode )
+				{  
+					// FIX_00052: Y axis for the mouse is now twice as sensitive as before
+					info->dpitch = (my * sens_Y * 2);
+				}
+				else
+				{
+					info->dz = (my * sens_Y * 2);
+				}
+			}
+			break;
+	}
+
+
+    // TODO: releasing the mouse button does not honor if a keyboard key with
+    // the same function is still pressed. how should it?
+    for(i=0; i<MAXMOUSEBUTTONS;++i)
+	{
+		if( MouseMapping[i] != -1 )
+		{
+			if(!(lastmousebuttons & (1<<i)) && mouseButtons & (1<<i))
+			{
+				SETMOUSEBUTTON(MouseMapping[i]); //MouseMapping[i]
+				//printf("mouse button: %d\n", i);
+			}
+			else if(lastmousebuttons & (1<<i) && !(mouseButtons & (1<<i)))
+			{
+				RESMOUSEBUTTON(MouseMapping[i]);//MouseMapping[i]
+			}
+		}
+    }
+    lastmousebuttons = mouseButtons;
+
+	// FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+	// update digital axis
+	RESMOUSEDIGITALAXIS(MouseDigitalAxeMapping[0][0]);
+	RESMOUSEDIGITALAXIS(MouseDigitalAxeMapping[0][1]);
+	RESMOUSEDIGITALAXIS(MouseDigitalAxeMapping[1][0]);
+	RESMOUSEDIGITALAXIS(MouseDigitalAxeMapping[1][1]);
+
+	if (mx < 0)
+	{
+		SETMOUSEDIGITALAXIS(MouseDigitalAxeMapping[0][0]);
+	} 
+	else if (mx > 0)
+	{
+		SETMOUSEDIGITALAXIS(MouseDigitalAxeMapping[0][1]);
+	} 
+
+	if (my < 0)
+	{
+		SETMOUSEDIGITALAXIS(MouseDigitalAxeMapping[1][0]);
+	} 
+	else if (my > 0)
+	{
+		SETMOUSEDIGITALAXIS(MouseDigitalAxeMapping[1][1]);
+	} 
+
+    // update stick state.
+    if ((CONTROL_JoystickEnabled) && (_joystick_update()))
+    {
+
+		// Check the hats
+		JOYSTICK_UpdateHats();
+
+		// TODO: make this NOT use the BUTTON() system for storing the hat input. (requires much game code changing)
+		for(i=0; i<MAXJOYHATS; i++)
+		{
+
+			for(j=0; j<8; j++)
+			{
+				if(!(lastjoyHats[i] & (1<<j)) && (joyHats[i] & (1<<j)))
+				{
+					SETHATBUTTON(JoyHatMapping[i][j]);
+				}
+				else if((lastjoyHats[i] & (1<<j)) && !(joyHats[i] & (1<<j)))
+				{
+					RESHATBUTTON(JoyHatMapping[i][j]);
+				}
+			}
+
+			lastjoyHats[i] = joyHats[i];
+		}
+		
+
+        for(i=0; i<MAXJOYAXES;i++)
+        {
+            switch(JoyAxisMapping[i])
+            {
+                case analog_turning:
+                    {
+                     info->dyaw +=  (int32)((float)CONTROL_FilterDeadzone
+                                    (
+                                        _joystick_axis(i),
+                                        JoyAnalogDeadzone[i]
+                                    ) 
+                                        * JoyAnalogScale[i]
+                                    );
+                    }
+            	    break;
+                case analog_strafing:
+                    {
+                        info->dx += (int32)((float)CONTROL_FilterDeadzone
+                                    (
+                                        _joystick_axis(i), 
+                                        JoyAnalogDeadzone[i]
+                                    )
+                                        * JoyAnalogScale[i]
+                                    );
+                                    //printf("Joy %d = %d\n", i, info->dx);
+                    }
+            	    break;
+                case analog_lookingupanddown:
+                        info->dpitch += (int32)((float)CONTROL_FilterDeadzone
+                                    (
+                                        _joystick_axis(i), 
+                                        JoyAnalogDeadzone[i]
+                                    )
+                                        * JoyAnalogScale[i]
+                                    );
+            	    break;
+                case analog_elevation: //STUB
+            	    break;
+                case analog_rolling: //STUB
+            	    break;
+                case analog_moving:
+                    {
+                        info->dz += (int32)((float)CONTROL_FilterDeadzone
+                                    (
+                                        _joystick_axis(i), 
+                                        JoyAnalogDeadzone[i]
+                                    )
+                                        * JoyAnalogScale[i]
+                                    );
+                    }
+            	    break;
+                default:
+                    break;
+            }
+        }
+
+        // !!! FIXME: Do this.
+        //SETBUTTON based on _joystick_button().
+        for(i=0; i<MAXJOYBUTTONS;++i)
+        {
+            if(_joystick_button(i))
+            {
+                SETJOYBUTTON(JoyButtonMapping[i]);
+            }
+            else
+            {
+                RESJOYBUTTON(JoyButtonMapping[i]);
+            }
+        }
+
+    }
+}
+
+void CONTROL_ClearAction( int32 whichbutton )
+{
+	//RESBUTTON(whichbutton);
+	KB_KeyDown[KeyMapping[whichbutton].key1] = 0;
+	KB_KeyDown[KeyMapping[whichbutton].key2] = 0;
+
+	RESJOYBUTTON(whichbutton);
+	RESHATBUTTON(whichbutton);
+	
+	RESMOUSEDIGITALAXIS(whichbutton);
+	
+}
+
+void CONTROL_ClearUserInput( UserInput *info )
+{
+	STUBBED("CONTROL_ClearUserInput");
+}
+
+void CONTROL_WaitRelease( void )
+{
+	STUBBED("CONTROL_WaitRelease");
+}
+
+void CONTROL_Ack( void )
+{
+	STUBBED("CONTROL_Ack");
+}
+
+void CONTROL_CenterJoystick
+   (
+   void ( *CenterCenter )( void ),
+   void ( *UpperLeft )( void ),
+   void ( *LowerRight )( void ),
+   void ( *CenterThrottle )( void ),
+   void ( *CenterRudder )( void )
+   )
+{
+	STUBBED("CONTROL_CenterJoystick");
+}
+
+
+int32 CONTROL_GetMouseSensitivity_X( void )
+{
+    return mouseSensitivity_X;
+}
+
+void CONTROL_SetMouseSensitivity_X( int32 newsensitivity )
+{
+	mouseSensitivity_X = newsensitivity;
+}
+
+// FIX_00014: Added Y cursor setup for mouse sensitivity in the menus 
+int32 CONTROL_GetMouseSensitivity_Y( void )
+{
+    return mouseSensitivity_Y;
+}
+
+void CONTROL_SetMouseSensitivity_Y( int32 newsensitivity )
+{
+	mouseSensitivity_Y = newsensitivity;
+}
+
+void CONTROL_Startup
+   (
+   controltype which,
+   int32 ( *TimeFunction )( void ),
+   int32 ticspersecond
+   )
+{
+	int i;
+
+	// Init the joystick
+    _joystick_init();
+
+	for(i=0; i < MAXJOYHATS; i++)
+    {
+		joyHats[i] = 0;
+		lastjoyHats[i] = 0;
+	}
+
+   CONTROL_MouseButtonState1 = 0;
+   CONTROL_MouseButtonState2 = 0;
+   CONTROL_MouseDigitalAxisState1 = 0;
+   CONTROL_MouseDigitalAxisState2 = 0;   
+   CONTROL_JoyButtonState1 = 0;
+   CONTROL_JoyButtonState2 = 0;
+   CONTROL_JoyHatState1 = 0;
+   CONTROL_JoyHatState2 = 0;
+}
+
+void CONTROL_Shutdown( void )
+{
+    _joystick_deinit();
+}
+
+
+void CONTROL_MapAnalogAxis
+   (
+   int32 whichaxis,
+   int32 whichanalog
+   )
+{
+	//STUBBED("CONTROL_MapAnalogAxis");
+    if(whichaxis < MAXJOYAXES)
+    {
+        JoyAxisMapping[whichaxis] = whichanalog;
+    }
+}
+
+// FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+void CONTROL_MapDigitalAxis
+   (
+   int32 whichaxis,
+   int32 whichfunction,
+   int32 direction
+   )
+{
+    if(whichaxis < 0 || whichaxis >= MAXMOUSEAXES || direction < 0 || direction >= 2)
+	    return;
+
+	MouseDigitalAxeMapping[whichaxis][direction] = whichfunction;
+
+}
+
+void CONTROL_SetAnalogAxisScale
+   (
+   int32 whichaxis,
+   float axisscale
+   )
+{
+    if(whichaxis < MAXJOYAXES)
+    {
+        // Set it... make sure we don't let them set it to 0.. div by 0 is bad.
+        JoyAnalogScale[whichaxis] = (axisscale == 0) ? 1.0f : axisscale;
+    }
+}
+
+void CONTROL_SetAnalogAxisDeadzone
+   (
+   int32 whichaxis,
+   int32 axisdeadzone
+   )
+{
+    if(whichaxis < MAXJOYAXES)
+    {
+        // Set it... 
+        JoyAnalogDeadzone[whichaxis] = axisdeadzone;
+    }
+}
+
+int32 CONTROL_FilterDeadzone
+   (
+   int32 axisvalue,
+   int32 axisdeadzone
+   )
+{
+    if((axisvalue < axisdeadzone) && (axisvalue > -axisdeadzone))
+    {
+        return 0;
+    }
+
+    return axisvalue;
+}
+
+int32 CONTROL_GetFilteredAxisValue(int32 axis)
+{
+return (int32)((float)CONTROL_FilterDeadzone
+                                    (
+                                        _joystick_axis(axis), 
+                                        JoyAnalogDeadzone[axis]
+                                    )
+                                        * JoyAnalogScale[axis]
+                                    );
+}
+
+
+void CONTROL_PrintAxes( void )
+{
+	STUBBED("CONTROL_PrintAxes");
+}
+
+boolean MOUSE_Init( void )
+{
+	memset(MouseMapping,-1,sizeof(MouseMapping));
+	memset(MouseDigitalAxeMapping, -1, sizeof(MouseDigitalAxeMapping));
+	return true;
+}
+
+void    MOUSE_Shutdown( void )
+{
+	STUBBED("MOUSE_Shutdown");
+}
+
+void    MOUSE_ShowCursor( void )
+{
+	STUBBED("MOUSE_ShowCursor");
+}
+
+void    MOUSE_HideCursor( void )
+{
+	STUBBED("MOUSE_HideCursor");
+}
+
+static void updateMouse(void)
+{
+    // this is in buildengine.
+    short x, y;
+    getmousevalues(&x, &y, &mouseButtons);
+
+    mouseRelativeX += x;
+    mouseRelativeY += y;
+    mousePositionX += x;
+    mousePositionY += y;
+}
+
+int32   MOUSE_GetButtons( void )
+{
+    //updateMouse();
+    return ((int32) mouseButtons);
+}
+
+void    MOUSE_GetPosition( int32*x, int32*y  )
+{
+    if (x) *x = mousePositionX;
+    if (y) *y = mousePositionY;
+}
+
+void    MOUSE_GetDelta( int32*x, int32*y )
+{
+    updateMouse();
+
+    if (x) *x = mouseRelativeX;
+    if (y) *y = mouseRelativeY;
+
+	mouseRelativeX = 0;
+	mouseRelativeY = 0;
+}
+
+void JOYSTICK_UpdateHats()
+{
+	int i;
+
+	for(i=0; i<MAXJOYHATS; i++)
+	{
+		//for(j=0; j<8; j++)
+		//{
+			joyHats[i] = _joystick_hat(i);
+		//}
+	}
+}
--- /dev/null
+++ b/Game/src/control.h
@@ -1,0 +1,265 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+//***************************************************************************
+//
+// Public header for CONTROL.C.
+//
+//***************************************************************************
+
+#ifndef _control_public
+#define _control_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+//***************************************************************************
+//
+// DEFINES
+//
+//***************************************************************************
+
+#define MaxJoys             2
+#define MAXGAMEBUTTONS      64
+
+	/*
+#define BUTTON(x) \
+    ( \
+    ((x)>31) ? \
+    ((CONTROL_ButtonState2>>( (x) - 32) ) & 1) :\
+    ((CONTROL_ButtonState1>> (x) ) & 1)          \
+    )
+	*/
+	/*
+#define BUTTONHELD(x) \
+    ( \
+    ((x)>31) ? \
+    ((CONTROL_ButtonHeldState2>>((x)-32)) & 1) :\
+    ((CONTROL_ButtonHeldState1>>(x)) & 1)\
+    )
+#define BUTTONJUSTPRESSED(x) \
+    ( BUTTON( x ) && !BUTTONHELD( x ) )
+#define BUTTONRELEASED(x) \
+    ( !BUTTON( x ) && BUTTONHELD( x ) )
+#define BUTTONSTATECHANGED(x) \
+    ( BUTTON( x ) != BUTTONHELD( x ) )
+	*/
+
+//***************************************************************************
+//
+// TYPEDEFS
+//
+//***************************************************************************
+typedef enum
+   {
+   axis_up,
+   axis_down,
+   axis_left,
+   axis_right
+   } axisdirection;
+
+typedef enum
+   {
+   analog_turning=0,
+   analog_strafing=1,
+   analog_lookingupanddown=2,
+   analog_elevation=3,
+   analog_rolling=4,
+   analog_moving=5,
+   analog_maxtype
+   } analogcontrol;
+
+typedef enum
+   {
+   dir_North,
+   dir_NorthEast,
+   dir_East,
+   dir_SouthEast,
+   dir_South,
+   dir_SouthWest,
+   dir_West,
+   dir_NorthWest,
+   dir_None
+   } direction;
+
+typedef struct
+   {
+   boolean   button0;
+   boolean   button1;
+   direction dir;
+   } UserInput;
+
+
+typedef struct
+   {
+   fixed     dx;
+   fixed     dy;
+   fixed     dz;
+   fixed     dyaw;
+   fixed     dpitch;
+   fixed     droll;
+   } ControlInfo;
+
+/*
+typedef struct
+   {
+   float     dx;
+   float     dy;
+   float     dz;
+   float     dyaw;
+   float     dpitch;
+   float     droll;
+   } ControlInfo;
+*/
+
+typedef enum
+   {
+   controltype_keyboard,
+   controltype_keyboardandmouse,
+   controltype_keyboardandjoystick,
+   controltype_keyboardandexternal,
+   controltype_keyboardandgamepad,
+   controltype_keyboardandflightstick,
+   controltype_keyboardandthrustmaster,
+   controltype_joystickandmouse
+   } controltype;
+
+
+//***************************************************************************
+//
+// GLOBALS
+//
+//***************************************************************************
+
+extern uint32   CONTROL_RudderEnabled;
+extern boolean  CONTROL_MousePresent;
+extern boolean  CONTROL_JoysPresent[ MaxJoys ];
+extern boolean  CONTROL_MouseEnabled;
+extern boolean  CONTROL_JoystickEnabled;
+extern byte     CONTROL_JoystickPort;
+extern uint32   CONTROL_MouseButtonState1;
+extern uint32   CONTROL_MouseButtonState2;
+//extern uint32   CONTROL_ButtonHeldState1;
+//extern uint32   CONTROL_ButtonHeldState2;
+extern uint32   CONTROL_JoyButtonState;
+
+//***************************************************************************
+//
+// PROTOTYPES
+//
+//***************************************************************************
+struct _KeyMapping
+{
+	boolean key_active;
+	kb_scancode key1;
+	kb_scancode key2;
+	
+	/* other mappings go here */
+} KeyMapping[MAXGAMEBUTTONS];
+
+int32 MouseMapping[MAXMOUSEBUTTONS];
+int32 MouseDigitalAxeMapping[MAXMOUSEAXES][2];  // [axesX/Y][directionLeft/Right or directionUp/Down]
+
+
+int  ACTION(int i);
+int  RESET_ACTION(int i);
+void RESBUTTON(int i);
+void CONTROL_MapKey( int32 which, kb_scancode key1, kb_scancode key2 );
+void CONTROL_MapButton
+        (
+        int32 whichfunction,
+        int32 whichbutton,
+        boolean clicked_or_doubleclicked
+        );
+void CONTROL_MapJoyButton(int32 whichfunction, int32 whichbutton, boolean doubleclicked);
+void CONTROL_MapJoyHat(int32 whichfunction, int32 whichhat, int32 whichvalue);
+void CONTROL_DefineFlag( int32 which, boolean toggle );
+boolean CONTROL_FlagActive( int32 which );
+void CONTROL_ClearAssignments( void );
+void CONTROL_GetUserInput( UserInput *info );
+void CONTROL_GetInput( ControlInfo *info );
+void CONTROL_ClearAction( int32 whichbutton );
+void CONTROL_ClearUserInput( UserInput *info );
+void CONTROL_WaitRelease( void );
+void CONTROL_Ack( void );
+void CONTROL_CenterJoystick
+   (
+   void ( *CenterCenter )( void ),
+   void ( *UpperLeft )( void ),
+   void ( *LowerRight )( void ),
+   void ( *CenterThrottle )( void ),
+   void ( *CenterRudder )( void )
+   );
+int32 CONTROL_GetMouseSensitivity_X( void );
+void CONTROL_SetMouseSensitivity_X( int32 newsensitivity );
+int32 CONTROL_GetMouseSensitivity_Y( void );
+void CONTROL_SetMouseSensitivity_Y( int32 newsensitivity );
+void CONTROL_Startup
+   (
+   controltype which,
+   int32 ( *TimeFunction )( void ),
+   int32 ticspersecond
+   );
+void CONTROL_Shutdown( void );
+
+void CONTROL_MapAnalogAxis
+   (
+   int32 whichaxis,
+   int32 whichanalog
+   );
+
+void CONTROL_MapDigitalAxis
+   (
+   int32 whichaxis,
+   int32 whichfunction,
+   int32 direction
+   );
+void CONTROL_SetAnalogAxisScale
+   (
+   int32 whichaxis,
+   float axisscale
+   );
+void CONTROL_SetAnalogAxisDeadzone
+   (
+   int32 whichaxis,
+   int32 axisdeadzone
+   );
+int32 CONTROL_FilterDeadzone
+   (
+   int32 axisvalue,
+   int32 axisdeadzone
+   );
+int32 CONTROL_GetFilteredAxisValue(int32 axis);
+void CONTROL_PrintAxes( void );
+
+void CONTROL_UpdateKeyboardState(int key, int pressed);
+
+#ifdef __cplusplus
+};
+#endif
+#endif
+
--- /dev/null
+++ b/Game/src/cvar_defs.c
@@ -1,0 +1,355 @@
+#include <stdlib.h>
+
+#include "cvars.h"
+#include "cvar_defs.h"
+#include "console.h"
+
+// Required for certain cvars
+#include "types.h"
+#include "build.h"
+#include "funct.h"
+
+//We need a way to access duke to change the level
+#include "duke3d.h"
+
+//We need to access debug soundinfo
+#include "sounddebugdefs.h"
+
+// Bind our Cvars at startup. You can still add bindings after this call, but
+// it is recommanded that you bind your default CVars here.
+void CVARDEFS_Init()
+{
+    g_CV_console_text_color = 0; // Set default value
+    REGCONVAR("SetConsoleColor", " - Change console color.",g_CV_console_text_color, CVARDEFS_DefaultFunction);
+
+    g_CV_num_console_lines = 0; // Set default value
+    REGCONVAR("NumConsoleLines", " - Number of visible console lines.", g_CV_num_console_lines, CVARDEFS_DefaultFunction);
+
+    g_CV_classic = 0; // Set default value
+    REGCONVAR("Classic", " - Enable Classic Mode.", g_CV_classic, CVARDEFS_DefaultFunction);
+
+	// FIX_00022b: Sound effects are now sharper and they sound as in the real DOS duke3d.
+    g_CV_CubicInterpolation = 0; // Set default value
+    REGCONVAR("EnableCubic", " - Turn on/off Cubic Interpolation for VOCs.", g_CV_CubicInterpolation, CVARDEFS_DefaultFunction);
+
+    g_CV_TransConsole = 1; // Set default value
+    REGCONVAR("TransConsole", " - Toggle the transparency of the console", g_CV_TransConsole, CVARDEFS_DefaultFunction);
+
+    g_CV_DebugJoystick = 0;
+    REGCONVAR("DebugJoystick", " - Displays info on the active Joystick", g_CV_DebugJoystick, CVARDEFS_DefaultFunction);
+
+	sounddebugActiveSounds = 0;
+	sounddebugAllocateSoundCalls = 0;
+	sounddebugDeallocateSoundCalls = 0;
+	g_CV_DebugSound = 0;
+    REGCONVAR("DebugSound", " - Displays info on the active Sounds", g_CV_DebugSound, CVARDEFS_DefaultFunction);
+
+	g_CV_DebugFileAccess = 0;
+    REGCONVAR("DebugFileAccess", " - Displays info on file access", g_CV_DebugFileAccess, CVARDEFS_DefaultFunction);
+	
+    REGCONVAR("TickRate", " - Changes the tick rate", g_iTickRate, CVARDEFS_DefaultFunction);
+    REGCONVAR("TicksPerFrame", " - Changes the ticks per frame", g_iTicksPerFrame, CVARDEFS_DefaultFunction);
+
+    REGCONFUNC("Quit", " - Quit game.", CVARDEFS_FunctionQuit);
+    REGCONFUNC("Clear", " - Clear the console.", CVARDEFS_FunctionClear);
+	REGCONFUNC("Name", " - Change player name.", CVARDEFS_FunctionName);
+    REGCONFUNC("Level", " - Change level. Args: Level <episode> <mission>", CVARDEFS_FunctionLevel);
+    REGCONFUNC("PlayMidi"," - Plays a MIDI file", CVARDEFS_FunctionPlayMidi);
+
+    REGCONFUNC("Help"," - Print out help commands for console", CVARDEFS_FunctionHelp);
+}
+
+// I any of the Cvars need to render.. to it here.
+void CVARDEFS_Render()
+{
+    if(g_CV_DebugJoystick)
+    {
+        int i;
+        char buf[128];
+         minitext(2, 2, "Debug Joystick", 17,10+16);
+
+         for(i = 0; i < MAXJOYAXES; i++)
+         {
+             sprintf(buf, "Joystick Axis%d: Raw: %d  Used:%d", i,
+                 _joystick_axis(i), CONTROL_GetFilteredAxisValue(i));
+         minitext(2, (i*8)+12, buf, 23,10+16);
+         }
+
+         for(i = 0; i < MAXJOYBUTTONS; i++)
+         {
+             sprintf(buf, "Button%d: %d", i, _joystick_button(i));
+             if(i < (MAXJOYBUTTONS/2))
+             {
+                 minitext(2, (i*8)+(MAXJOYAXES*8)+12, buf, 23,10+16);
+             }
+             else
+             {
+                 minitext(55, ((i-16)*8)+(MAXJOYAXES*8)+12, buf, 23,10+16);
+             }
+         }
+
+         for(i = 0; i < MAXJOYHATS; i++)
+         {
+			 sprintf(buf, "Hat%d: %d", i, _joystick_hat(i));
+             minitext(110, (i*8)+(MAXJOYAXES*8)+12, buf, 23,10+16);
+		 }
+
+
+    }
+
+	if(g_CV_DebugSound)
+	{
+        char buf[128];
+        minitext(2, 2, "Debug Sound", 17,10+16);
+
+		sprintf(buf, "Active sounds: %d", sounddebugActiveSounds);
+		minitext(2, 10, buf, 23,10+16);
+		
+		sprintf(buf, "Allocate Calls: %d", sounddebugAllocateSoundCalls);
+		minitext(2, 18, buf, 23,10+16);
+
+		sprintf(buf, "Deallocate Calls: %d", sounddebugDeallocateSoundCalls);
+		minitext(2, 26, buf, 23,10+16);
+	}
+
+}
+
+// For default int functions
+// If your CVAR should simply change a global 'int' variable,
+// Then, use this function.
+void CVARDEFS_DefaultFunction(void* var)
+{
+    int argc;
+    char* argv;
+    cvar_binding* binding = (cvar_binding*)var;
+
+    argc = CONSOLE_GetArgc();
+
+    if(argc < 1)
+    {
+        //print out the current state of the var if no args are given
+        CONSOLE_Printf("%s %d", binding->name, *(int*)binding->variable);
+        return;
+    }
+
+    //change the var
+    *((int*)binding->variable) = atoi( CONSOLE_GetArgv(0) );
+
+}
+
+// This function will quit the game
+void CVARDEFS_FunctionQuit(void* var)
+{
+  if( numplayers > 1 )
+  {
+	if(ps[myconnectindex].gm&MODE_GAME)
+	{
+		char quittimer = totalclock+1;
+		gamequit = 1;
+		CONSOLE_SetActive(0);
+    }
+    else
+    {
+		sendlogoff();
+		gameexit(" ");
+    }
+  }
+  else if( numplayers < 2 )
+   gameexit(" ");
+}
+
+// This function will quit the game
+void CVARDEFS_FunctionClear(void* var)
+{
+    CONSOLE_Reset();
+}
+
+//And the game will reflect the changes. Will also return the current name of
+//The player
+void CVARDEFS_FunctionName(void* var)
+{
+	int argc, length,i;
+
+	argc = CONSOLE_GetArgc();
+	
+	//Check to see if we're changing name's, or checking the name
+	if (argc==1) {
+
+		//The Fragbar up the top doesn't look very good with more than
+		//10 characters, so limit it to that
+		if (strlen(CONSOLE_GetArgv(0)) > 10) {
+			CONSOLE_Printf("User name must be 10 characters or less");
+			return;
+		}
+
+		//Loop through the length of the new name
+		for (i=0;CONSOLE_GetArgv(0)[i];i++)
+		{
+			//Copy it to the local copy of the name
+			ud.user_name[myconnectindex][i] = toupper(CONSOLE_GetArgv(0)[i]);
+			//And the packet we're going to send the other players
+			tempbuf[i+2] = toupper(CONSOLE_GetArgv(0)[i]);
+		}
+		
+		//Delimit the local copy with a null character
+		ud.user_name[myconnectindex][i] = 0;
+		
+		//If we are online
+		if(numplayers > 1)
+		{
+			//The packet descriptor is 6
+			tempbuf[0] = 6;
+			//We need to send the version of the game we're running
+			//Since names used to be only sent once, this was where they
+			//Checked that everyone was running the same version
+			tempbuf[1] = grpVersion;
+            //Delimit the buffer with a null character.
+			tempbuf[i+2] =0;
+			//The length will be 1 more than the last index
+			length = i +3;
+			
+	        for(i=connecthead;i>=0;i=connectpoint2[i])
+            {
+	            if (i != myconnectindex)
+		            //Send it to everyone
+					sendpacket(i,&tempbuf[0],length);
+            }
+        }	
+	} 
+    else 
+    {
+		//If there's no arguement, just print out our name
+		CONSOLE_Printf("Current Name: %s", ud.user_name[myconnectindex]);
+	}
+
+ 
+}
+
+
+// This function loads a new level
+void CVARDEFS_FunctionLevel(void* var)
+{
+	int argc;
+	short volnume,levnume, i;
+
+	//Find out how many arguements were passed
+	argc = CONSOLE_GetArgc();
+	
+	//If there's 2
+	if (argc == 2) {
+		//The episode number is the first arguement
+		volnume = atoi(CONSOLE_GetArgv(0));
+		
+		//The level is the second
+		levnume = atoi(CONSOLE_GetArgv(1));
+		volnume--;
+        levnume--;
+                            
+        // Make sure the number aren't out of range.
+        // This is based on the 1.5 data files.
+        if(levnume<0 || volnume<0) 
+            {return;}
+        if(volnume>3) 
+            {return;}
+        switch(volnume)
+        {
+        case 0: // ep1
+            {
+                if(levnume > 7)
+                {
+		            CONSOLE_Printf("Invalid Level Selection");
+                    return;
+                }
+            }
+            break;
+        case 1: // ep2
+        case 2: // ep3
+        case 3: // ep4
+            {
+                if(levnume > 10)
+                {
+		            CONSOLE_Printf("Invalid Level Selection");
+                    return;
+                }
+            }
+            break;
+   
+        default:
+            break;
+        }
+
+		ud.m_volume_number = ud.volume_number = volnume; //update the current volume
+		ud.m_level_number = ud.level_number = levnume; //And level
+
+		//If we're playing online
+		if(numplayers > 1 && myconnectindex == connecthead)
+		{
+			//Fill out the game data
+			tempbuf[0] = 5;
+			tempbuf[1] = ud.m_level_number;
+            tempbuf[2] = ud.m_volume_number;
+            tempbuf[3] = ud.m_player_skill;
+            tempbuf[4] = ud.m_monsters_off;
+            tempbuf[5] = ud.m_respawn_monsters;
+            tempbuf[6] = ud.m_respawn_items;
+            tempbuf[7] = ud.m_respawn_inventory;
+            tempbuf[8] = ud.m_coop;
+            tempbuf[9] = ud.m_marker;
+            tempbuf[10] = ud.m_ffire;
+
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+            {
+                sendpacket(i,tempbuf,11); //And send the packet to everyone
+            }
+        }
+		else ps[myconnectindex].gm |= MODE_RESTART; //Otherwise just restart the game
+
+	} 
+    else 
+    {
+		//If there's not 2 arguements, print out the error message
+		CONSOLE_Printf("Level (Episode Number) (Level Number)");
+	}
+}
+
+// Tries to load a external mid file... :)
+void CVARDEFS_FunctionPlayMidi(void* var)
+{
+    if(CONSOLE_GetArgc() < 1)
+    {
+        return;
+    }
+
+	PlayMusic(CONSOLE_GetArgv(0));		// Gets the first parameter and tries to load it in ( Doesn't crash if invalided )
+}
+
+
+// Help function and finds specific help commands...
+void CVARDEFS_FunctionHelp(void* var)
+{	
+	int i, numArgs, numCvars;
+	char *helpcmd = CONSOLE_GetArgv(0);	
+	numCvars = CVAR_GetNumCvarBindings();
+    numArgs = CONSOLE_GetArgc();
+
+	if(numArgs < 1)// If no extra arugment was passed print below..
+        CONSOLE_Printf("Console Command List:\n\n");
+	for(i = 0; i < numCvars; i++)
+	{
+		cvar_binding* binding = CVAR_GetCvarBinding(i);
+	    if(numArgs < 1)
+        {
+			CONSOLE_Printf("%s\t%5s",binding->name,binding->help);
+		}
+		else
+        {
+			// Did we find it?
+			if ( strcmpi(helpcmd, binding->name) == 0 )
+			{
+				CONSOLE_Printf("%s\t%5s",binding->name,binding->help);
+				break;
+			}	
+		}
+	}	
+
+}
--- /dev/null
+++ b/Game/src/cvar_defs.h
@@ -1,0 +1,37 @@
+#ifndef _CVARDEFS_H_
+#define _CVARDEFS_H_
+
+void CVARDEFS_Init();
+void CVARDEFS_Render();
+//
+// Function declarations
+//
+void CVARDEFS_DefaultFunction(void* var);
+void CVARDEFS_FunctionQuit(void* var);
+void CVARDEFS_FunctionClear(void* var);
+void CVARDEFS_FunctionLevel(void* var);
+void CVARDEFS_FunctionName(void* var);
+void CVARDEFS_FunctionPlayMidi(void* var);
+void CVARDEFS_FunctionFOV(void* var);
+void CVARDEFS_FunctionTickRate(void* var);
+void CVARDEFS_FunctionTicksPerFrame(void* var);
+void CVARDEFS_FunctionHelp(void* var);
+
+//
+// Variable declarations
+//
+int g_CV_console_text_color;
+int g_CV_num_console_lines;
+int g_CV_classic;
+int g_CV_TransConsole;
+int g_CV_DebugJoystick;
+int g_CV_DebugSound;
+int g_CV_DebugFileAccess;
+unsigned long sounddebugActiveSounds;
+unsigned long sounddebugAllocateSoundCalls;
+unsigned long sounddebugDeallocateSoundCalls;
+
+
+int g_CV_CubicInterpolation;
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/Game/src/cvars.c
@@ -1,0 +1,52 @@
+#include "cvars.h"
+#include "cvar_defs.h"
+#include <stdlib.h>
+
+#define MAX_CVARS 32
+
+#pragma message ( "We probably want this to be some sort of dynamic list at some point" )
+cvar_binding cvar_binding_list[MAX_CVARS];
+int num_cvar_bindings = 0;
+
+void CVAR_RegisterCvar(const char* varname, const char* varhelp, void* variable, function_t function)
+{
+    if(NULL == function)
+    {
+        return;
+    }
+
+    cvar_binding_list[num_cvar_bindings].variable = variable;
+    cvar_binding_list[num_cvar_bindings].function = function;
+    memset(cvar_binding_list[num_cvar_bindings].name, 0, 64);
+    strncpy(cvar_binding_list[num_cvar_bindings].name, varname, 63);    
+	memset(cvar_binding_list[num_cvar_bindings].help, 0, 64);
+    strncpy(cvar_binding_list[num_cvar_bindings].help, varhelp, 63);
+    num_cvar_bindings++;
+}
+
+int CVAR_GetNumCvarBindings()
+{
+    return num_cvar_bindings;
+}
+
+cvar_binding* CVAR_GetCvarBinding(unsigned int nBinding)
+{
+    if(nBinding > num_cvar_bindings -1)
+    {
+        return NULL;
+    }
+
+    return &cvar_binding_list[nBinding];
+}
+
+// Bind all standard CVars here
+void CVAR_RegisterDefaultCvarBindings()
+{
+    CVARDEFS_Init();
+}
+
+void CVAR_Render()
+{
+    CVARDEFS_Render();
+}
+
--- /dev/null
+++ b/Game/src/cvars.h
@@ -1,0 +1,23 @@
+#ifndef _CVARS_H_
+#define _CVARS_H_
+
+#define REGCONVAR(varname, varhelp, variable, function) CVAR_RegisterCvar(varname, varhelp, &variable, &function)
+#define REGCONFUNC(varname, varhelp, function) CVAR_RegisterCvar(varname, varhelp, NULL, &function)
+
+typedef void (*function_t) (void* binding);
+
+typedef struct t_cvar_binding
+{
+  char          name[64];
+  char          help[64];
+  void*         variable;
+  function_t	function;
+} cvar_binding;
+
+
+void            CVAR_RegisterCvar(const char* varname, const char* varhelp, void* variable, function_t function);
+int             CVAR_GetNumCvarBindings();
+cvar_binding*   CVAR_GetCvarBinding(unsigned int nBinding);
+void            CVAR_Render();
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/Game/src/develop.h
@@ -1,0 +1,65 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef _develop_public
+#define _develop_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEVELOPMENT          0
+#define SHAREWARE            0
+#define LOCATIONINFO         1
+#define SOFTWAREERROR        1
+#define MEMORYCORRUPTIONTEST 1
+#define PRECACHETEST         0
+#define DATACORRUPTIONTEST   0
+#define RANDOMNUMBERTEST     0
+
+
+#if ( LOCATIONINFO == 1 )
+
+#define funcstart() \
+   { \
+   SoftError( "funcstart : module '%s' at line %d.\n", __FILE__, __LINE__ );\
+   }
+
+#define funcend() \
+   { \
+   SoftError( "  funcend : module '%s' at line %d.\n", __FILE__, __LINE__ );\
+   }
+
+#else
+
+#define funcstart()
+#define funcend()
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/duke3d.h
@@ -1,0 +1,681 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+#ifndef _INCL_DUKE3D_H_
+#define _INCL_DUKE3D_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "cache1d.h"
+
+#if (!defined MAX_PATH)
+  #if (defined MAXPATHLEN)
+    #define MAX_PATH MAXPATHLEN
+  #elif (defined PATH_MAX)
+    #define MAX_PATH PATH_MAX
+  #else
+    #define MAX_PATH 256
+  #endif
+#endif
+
+#if PLATFORM_DOS
+#include <dos.h>
+#include <bios.h>
+#include <io.h>
+#define PATH_SEP_CHAR '\\'
+#define PATH_SEP_STR  "\\"
+#endif
+
+#if PLATFORM_UNIX
+#include "dukeunix.h"
+#endif
+
+#if PLATFORM_WIN32
+#include "dukewin.h"
+#endif
+
+#if USE_SDL
+#include "SDL.h"
+#include "SDL_mixer.h"
+#endif
+
+#include "build.h"
+#include "function.h"
+
+//  FIX_00022: Automatically recognize the shareware grp (v1.3) + full version (1.3d) +
+//             atomic (1.4/1.5 grp) and the con files version (either 1.3 or 1.4) (JonoF's idea)
+
+extern unsigned char conVersion;
+extern unsigned char grpVersion;
+extern long groupefil_crc32[MAXGROUPFILES];
+
+#define RANCID_ID 1
+#define	XDUKE_ID  2
+#define JONOF_ID  3
+
+#define DUKE_ID			XDUKE_ID
+#define	XDUKE_REV_X		19
+#define XDUKE_REV_DOT_Y	7   // rev is: vXDUKE_REV_X.XDUKE_REV_DOT_Y
+#define XDUKE_SUBVERSION 1
+
+#define MAX_KNOWN_GRP 4
+
+enum
+{
+	UNKNOWN_GRP = 0,
+	SHAREWARE_GRP13,
+	REGULAR_GRP13D,
+	ATOMIC_GRP14_15,
+	DUKEITOUTINDC_GRP
+};
+
+#define CRC_BASE_GRP_SHAREWARE_13	0x983AD923
+#define CRC_BASE_GRP_FULL_13		0xBBC9CE44
+#define CRC_BASE_GRP_PLUTONIUM_14	0xF514A6AC
+#define CRC_BASE_GRP_ATOMIC_15		0xFD3DCFF1 
+
+#define PLUTOPAK  (!VOLUMEONE && !VOLUMEALL) // implies  conVersion == 14 or conVersion == 15
+#define VOLUMEONE (groupefil_crc32[0]==CRC_BASE_GRP_SHAREWARE_13)
+// VOLUMEALL = 1.3d full
+#define VOLUMEALL (groupefil_crc32[0]==CRC_BASE_GRP_FULL_13 || conVersion == 13 && groupefil_crc32[0]!=CRC_BASE_GRP_SHAREWARE_13 && groupefil_crc32[0]!=CRC_BASE_GRP_PLUTONIUM_14 && groupefil_crc32[0]!=CRC_BASE_GRP_ATOMIC_15)
+
+#define SCREENSHOTPATH "screenshots"
+
+// #define ONELEVELDEMO
+
+// #define TEN
+// #define BETA
+
+// #define AUSTRALIA
+
+#define MAXSLEEPDIST  16384
+#define SLEEPTIME 24*64
+
+extern int BYTEVERSION;
+
+extern int BYTEVERSION_27;
+extern int BYTEVERSION_28;
+extern int BYTEVERSION_29; // really needed???
+extern int BYTEVERSION_116;
+extern int BYTEVERSION_117; 
+extern int BYTEVERSION_118; 
+extern int BYTEVERSION_1_3;
+
+
+#define NUMPAGES 1
+
+#define AUTO_AIM_ANGLE          48
+#define RECSYNCBUFSIZ 2520   //2520 is the (LCM of 1-8)*3
+#define MOVEFIFOSIZ 256
+
+#define FOURSLEIGHT (1<<8)
+
+struct player_struct;
+
+#include "types.h"
+#include "file_lib.h"
+#include "develop.h"
+#include "gamedefs.h"
+#include "keyboard.h"
+#include "util_lib.h"
+#include "function.h"
+#include "audiolib\fx_man.h"
+#include "config.h"
+#include "sounds.h"
+#include "control.h"
+#include "_rts.h"
+#include "rts.h"
+#include "soundefs.h"
+
+#if PLATFORM_DOS
+#include "audiolib\task_man.h"
+#include "audiolib\sndcards.h"
+#endif
+#include "audiolib\music.h"
+
+#include "names.h"
+
+#include "engine.h"
+#include "pragmas.h"
+
+//#define TICRATE (120)
+//#define TICSPERFRAME (TICRATE/26)
+
+extern int g_iTickRate;
+extern int g_iTicksPerFrame;
+
+#define TICRATE g_iTickRate
+#define TICSPERFRAME (TICRATE/g_iTicksPerFrame)
+
+// #define GC (TICSPERFRAME*44)
+
+#define NUM_SOUNDS 450
+
+#define    ALT_IS_PRESSED ( KB_KeyPressed( sc_RightAlt ) || KB_KeyPressed( sc_LeftAlt ) )
+#define    SHIFTS_IS_PRESSED ( KB_KeyPressed( sc_RightShift ) || KB_KeyPressed( sc_LeftShift ) )
+#define    RANDOMSCRAP EGS(s->sectnum,s->x+(TRAND&255)-128,s->y+(TRAND&255)-128,s->z-(8<<8)-(TRAND&8191),SCRAP6+(TRAND&15),-8,48,48,TRAND&2047,(TRAND&63)+64,-512-(TRAND&2047),i,5)
+
+#define    BLACK 0
+#define    DARKBLUE 1
+#define    DARKGREEN 2
+#define    DARKCYAN 3
+#define    DARKRED 4
+#define    DARKPURPLE 5
+#define    BROWN 6
+#define    LIGHTGRAY 7
+
+#define    DARKGRAY 8
+#define    BLUE 9
+#define    GREEN 10
+#define    CYAN 11
+#define    RED 12
+#define    PURPLE 13
+#define    YELLOW 14
+#define    WHITE 15
+
+#define    PHEIGHT (38<<8)
+
+// #define P(X) printf("%ld\n",X);
+
+#define WAIT(X) ototalclock=totalclock+(X);while(totalclock<ototalclock)
+
+
+#define MODE_MENU       1
+#define MODE_DEMO       2
+#define MODE_GAME       4
+#define MODE_EOL        8
+#define MODE_TYPE       16
+#define MODE_RESTART    32
+#define MODE_SENDTOWHOM 64
+#define MODE_END        128
+
+
+#define MAXANIMWALLS 512
+#define MAXINTERPOLATIONS 2048
+#define NUMOFFIRSTTIMEACTIVE 192
+
+#define MAXCYCLERS 256
+#define MAXSCRIPTSIZE 20460
+#define MAXANIMATES 64
+
+#define SP  sprite[i].yvel
+#define SX  sprite[i].x
+#define SY  sprite[i].y
+#define SZ  sprite[i].z
+#define SS  sprite[i].shade
+#define PN  sprite[i].picnum
+#define SA  sprite[i].ang
+#define SV  sprite[i].xvel
+#define ZV  sprite[i].zvel
+#define RX  sprite[i].xrepeat
+#define RY  sprite[i].yrepeat
+#define OW  sprite[i].owner
+#define CS  sprite[i].cstat
+#define SH  sprite[i].extra
+#define CX  sprite[i].xoffset
+#define CY  sprite[i].yoffset
+#define CD  sprite[i].clipdist
+#define PL  sprite[i].pal
+#define SLT  sprite[i].lotag
+#define SHT  sprite[i].hitag
+#define SECT sprite[i].sectnum
+
+#define face_player 1
+#define geth 2
+#define getv 4
+#define random_angle 8
+#define face_player_slow 16
+#define spin 32
+#define face_player_smart 64
+#define fleeenemy 128
+#define jumptoplayer 257
+#define seekplayer 512
+#define furthestdir 1024
+#define dodgebullet 4096
+
+#ifdef DBGRECORD
+#define TRAND krand(__LINE__,__FILE__)
+#else
+#define TRAND krand()
+#endif
+
+#define MAX_WEAPONS  12
+
+#define KNEE_WEAPON          0
+#define PISTOL_WEAPON        1
+#define SHOTGUN_WEAPON       2
+#define CHAINGUN_WEAPON      3
+#define RPG_WEAPON           4
+#define HANDBOMB_WEAPON      5
+#define SHRINKER_WEAPON      6
+#define DEVISTATOR_WEAPON    7
+#define TRIPBOMB_WEAPON      8
+#define FREEZE_WEAPON        9
+#define HANDREMOTE_WEAPON    10
+#define GROW_WEAPON          11
+
+#define T1  hittype[i].temp_data[0]
+#define T2  hittype[i].temp_data[1]
+#define T3  hittype[i].temp_data[2]
+#define T4  hittype[i].temp_data[3]
+#define T5  hittype[i].temp_data[4]
+#define T6  hittype[i].temp_data[5]
+
+#define ESCESCAPE if(KB_KeyPressed( sc_Escape ) ) gameexit(" ");
+
+#define IFWITHIN(B,E) if((PN)>=(B) && (PN)<=(E))
+#define KILLIT(KX) {deletesprite(KX);goto BOLT;}
+
+
+#define IFMOVING if(ssp(i,CLIPMASK0))
+#define IFHIT j=ifhitbyweapon(i);if(j >= 0)
+#define IFHITSECT j=ifhitsectors(s->sectnum);if(j >= 0)
+
+#define AFLAMABLE(X) (X==BOX||X==TREE1||X==TREE2||X==TIRE||X==CONE)
+
+
+#define IFSKILL1 if(player_skill<1)
+#define IFSKILL2 if(player_skill<2)
+#define IFSKILL3 if(player_skill<3)
+#define IFSKILL4 if(player_skill<4)
+
+#define rnd(X) ((TRAND>>8)>=(255-(X)))
+
+typedef struct
+{
+    short i;
+    int voice;
+} SOUNDOWNER;
+
+#define __USRHOOKS_H
+
+enum USRHOOKS_Errors
+   {
+   USRHOOKS_Warning = -2,
+   USRHOOKS_Error   = -1,
+   USRHOOKS_Ok      = 0
+   };
+
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct
+{
+    signed char avel, horz;
+    short fvel, svel;
+    unsigned long bits;
+} input;
+
+#pragma pack(pop)
+
+/* !!! FIXME: "sync" is defined in unistd.h ... :(  --ryan. */
+#define sync duke_sync
+extern input inputfifo[MOVEFIFOSIZ][MAXPLAYERS], sync[MAXPLAYERS];
+extern input recsync[RECSYNCBUFSIZ];
+
+extern long movefifosendplc;
+
+typedef struct
+{
+    char *ptr;
+    volatile char lock;
+    int  length, num;
+} SAMPLE;
+
+struct animwalltype
+{
+        short wallnum;
+        long tag;
+};
+extern struct animwalltype animwall[MAXANIMWALLS];
+extern short numanimwalls,probey,lastprobey;
+
+extern char *mymembuf;
+extern char typebuflen,typebuf[41];
+extern char MusicPtr[72000];
+extern long msx[2048],msy[2048];
+extern short cyclers[MAXCYCLERS][6],numcyclers;
+extern char myname[2048];
+
+struct user_defs
+{
+    char god,warp_on,cashman,eog,showallmap;
+    char show_help,scrollmode,clipping;
+    char user_name[MAXPLAYERS][32];
+    char ridecule[10][40];
+    char savegame[10][22];
+    char pwlockout[128],rtsname[128];
+    char overhead_on,last_overhead;
+
+    short pause_on,from_bonus;
+    short camerasprite,last_camsprite;
+    short last_level,secretlevel;
+
+    long const_visibility,uw_framerate;
+    long camera_time,folfvel,folavel,folx,foly,fola;
+    long reccnt;
+
+    int32 entered_name,screen_tilting,shadows,fta_on,executions,auto_run;
+    int32 coords,tickrate,m_coop,coop,screen_size,extended_screen_size,lockout,crosshair,showweapons;
+    int32 mywchoice[MAX_WEAPONS],wchoice[MAXPLAYERS][MAX_WEAPONS],playerai;
+
+    int32 respawn_monsters,respawn_items,respawn_inventory,recstat,monsters_off,brightness;
+    int32 m_respawn_items,m_respawn_monsters,m_respawn_inventory,m_recstat,m_monsters_off,detail;
+	// FIX_00082: /q option taken off when playing a demo (multimode_bot)    
+	int32 m_ffire,ffire,m_player_skill,m_level_number,m_volume_number,multimode,multimode_bot;
+    int32 player_skill,level_number,volume_number,m_marker,marker,mouseflip;
+
+	int32 showcinematics, hideweapon;
+	int32 auto_aim, gitdat_mdk; //AutoAim toggle variable.
+	int32 weaponautoswitch;
+
+	// FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116 and 117 only)
+	char playing_demo_rev;
+
+	unsigned long groupefil_crc32[MAXPLAYERS][MAXGROUPFILES];
+	unsigned short conSize[MAXPLAYERS];
+
+#ifdef CHECK_XDUKE_REV
+	char rev[MAXPLAYERS][10];
+#endif
+	unsigned long mapCRC[MAXPLAYERS];
+	unsigned long exeCRC[MAXPLAYERS];
+	unsigned long conCRC[MAXPLAYERS];
+};
+
+struct player_orig
+{
+    long ox,oy,oz;
+    short oa,os;
+};
+
+
+extern char numplayersprites;
+extern char picsiz[MAXTILES];
+
+typedef struct
+{
+	unsigned int crc32;
+	char* name;
+	unsigned long size;
+} crc32_t;
+
+extern crc32_t crc32lookup[];
+
+void add_ammo( short, short, short, short );
+
+
+extern long fricxv,fricyv;
+
+struct player_struct
+{
+    long zoom,exitx,exity,loogiex[64],loogiey[64],numloogs,loogcnt;
+    long posx, posy, posz, horiz, ohoriz, ohorizoff, invdisptime;
+    long bobposx,bobposy,oposx,oposy,oposz,pyoff,opyoff;
+    long posxv,posyv,poszv,last_pissed_time,truefz,truecz;
+    long player_par,visibility;
+    long bobcounter,weapon_sway;
+    long pals_time,randomflamex,crack_time;
+
+    int32 aim_mode;
+
+    short ang,oang,angvel,cursectnum,look_ang,last_extra,subweapon;
+    short ammo_amount[MAX_WEAPONS],wackedbyactor,frag,fraggedself;
+
+    short curr_weapon, last_weapon, tipincs, horizoff, wantweaponfire;
+    short holoduke_amount,newowner,hurt_delay,hbomb_hold_delay;
+    short jumping_counter,airleft,knee_incs,access_incs;
+    short fta,ftq,access_wallnum,access_spritenum;
+    short kickback_pic,got_access,weapon_ang,firstaid_amount;
+    short somethingonplayer,on_crane,i,one_parallax_sectnum;
+    short over_shoulder_on,random_club_frame,fist_incs;
+    short one_eighty_count,cheat_phase;
+    short dummyplayersprite,extra_extra8,quick_kick;
+    short heat_amount,actorsqu,timebeforeexit,customexitsound;
+
+    short weaprecs[16],weapreccnt;
+	unsigned long interface_toggle_flag;
+
+    short rotscrnang,dead_flag,show_empty_weapon;
+    short scuba_amount,jetpack_amount,steroids_amount,shield_amount;
+    short holoduke_on,pycount,weapon_pos,frag_ps;
+    short transporter_hold,last_full_weapon,footprintshade,boot_amount;
+
+    int scream_voice;
+
+    char gm,on_warping_sector,footprintcount;
+    char hbomb_on,jumping_toggle,rapid_fire_hold,on_ground;
+    char name[32],inven_icon,buttonpalette;
+
+    char jetpack_on,spritebridge,lastrandomspot;
+    char scuba_on,footprintpal,heat_on;
+
+    char  holster_weapon,falling_counter;
+    char  gotweapon[MAX_WEAPONS],refresh_inventory,*palette;
+
+    char toggle_key_flag,knuckle_incs; // ,select_dir;
+    char walking_snd_toggle, palookup, hard_landing;
+    char max_secret_rooms,secret_rooms,/*fire_flag,*/pals[3];
+    char max_actors_killed,actors_killed,return_to_center;
+
+	// local but synch variables (ud is local but not synch):
+
+	// FIX_00023: Moved Addfaz's autoaim handler to synch variables (to avoid out of synch)
+	int32 auto_aim; //AutoAim toggle variable.
+
+	// FIX_00012: added "weapon autoswitch" toggle allowing to turn the autoswitch off
+	//            when picking up new weapons. The weapon sound on pickup will remain on, to not 
+	//           affect the opponent's gameplay (so he can still hear you picking up new weapons)
+	int32 weaponautoswitch;
+
+	char fakeplayer;
+};
+
+extern unsigned char tempbuf[2048], packbuf[576];
+
+extern long gc,max_player_health,max_armour_amount,max_ammo_amount[MAX_WEAPONS];
+
+extern long impact_damage,respawnactortime,respawnitemtime;
+
+#define MOVFIFOSIZ 256
+
+extern short spriteq[1024],spriteqloc,spriteqamount;
+extern struct player_struct ps[MAXPLAYERS];
+extern struct player_orig po[MAXPLAYERS];
+extern struct user_defs ud;
+
+// ported build engine has this, too.  --ryan.
+#if PLATFORM_DOS
+extern short int moustat;
+#endif
+
+extern short int global_random;
+extern long scaredfallz;
+extern char buf[80]; //My own generic input buffer
+
+extern char fta_quotes[NUMOFFIRSTTIMEACTIVE][64];
+extern char scantoasc[128],ready2send;
+extern char scantoascwithshift[128];
+
+extern fx_device device;
+extern SAMPLE Sound[ NUM_SOUNDS ];
+extern int32 VoiceToggle,AmbienceToggle, OpponentSoundToggle;
+extern int32 mouseSensitivity_X, mouseSensitivity_Y;
+extern SOUNDOWNER SoundOwner[NUM_SOUNDS][4];
+
+extern char playerreadyflag[MAXPLAYERS],playerquitflag[MAXPLAYERS];
+extern char sounds[NUM_SOUNDS][14];
+
+extern long script[MAXSCRIPTSIZE],*scriptptr,*insptr,*labelcode,labelcnt;
+extern char *label,*textptr,error,warning,killit_flag;
+extern long *actorscrptr[MAXTILES],*parsing_actor;
+extern char actortype[MAXTILES];
+extern char *music_pointer;
+
+extern char ipath[80],opath[80];
+
+extern char music_fn[4][11][13],music_select;
+extern char env_music_fn[4][13];
+extern short camsprite;
+
+// extern char gotz;
+extern char inspace(short sectnum);
+
+
+struct weaponhit
+{
+    char cgg;
+    short picnum,ang,extra,owner,movflag;
+    short tempang,actorstayput,dispicnum;
+    short timetosleep;
+    long floorz,ceilingz,lastvx,lastvy,bposx,bposy,bposz;
+    long temp_data[6];
+};
+
+extern struct weaponhit hittype[MAXSPRITES];
+
+extern input loc;
+extern input recsync[RECSYNCBUFSIZ];
+extern long avgfvel, avgsvel, avgavel, avghorz, avgbits;
+
+extern short numplayers, myconnectindex;
+extern short connecthead, connectpoint2[MAXPLAYERS];   //Player linked list variables (indeces, not connection numbers)
+extern short screenpeek;
+
+extern int current_menu;
+extern long tempwallptr,animatecnt;
+extern long lockclock,frameplace;
+extern char display_mirror,rtsplaying;
+
+extern long movefifoend[MAXPLAYERS];
+extern long ototalclock;
+
+extern long *animateptr[MAXANIMATES], animategoal[MAXANIMATES];
+extern long animatevel[MAXANIMATES];
+// extern long oanimateval[MAXANIMATES];
+extern short neartagsector, neartagwall, neartagsprite;
+extern long neartaghitdist;
+extern short animatesect[MAXANIMATES];
+extern long movefifoplc, vel,svel,angvel,horiz;
+
+extern short mirrorwall[64], mirrorsector[64], mirrorcnt;
+
+#define NUMKEYS 19
+
+extern long frameplace, chainplace, chainnumpages;
+extern volatile long checksume;
+
+#include "funct.h"
+#include "engine_protos.h"
+
+extern char screencapt;
+extern short soundps[NUM_SOUNDS],soundpe[NUM_SOUNDS],soundvo[NUM_SOUNDS];
+extern char soundpr[NUM_SOUNDS],soundm[NUM_SOUNDS];
+extern long soundsiz[NUM_SOUNDS];
+extern char level_names[44][33];
+extern long partime[44],designertime[44];
+extern char volume_names[4][33];
+extern char skill_names[5][33];
+extern char level_file_names[44][128];
+
+extern int32 SoundToggle,MusicToggle;
+extern short last_threehundred,lastsavedpos;
+extern char restorepalette;
+
+extern short buttonstat;
+extern long cachecount;
+extern char boardfilename[128],waterpal[768],slimepal[768],titlepal[768],drealms[768],endingpal[768];
+extern char betaname[80];
+extern char cachedebug,earthquaketime;
+extern char networkmode;
+extern char lumplockbyte[11];
+
+    //DUKE3D.H - replace the end "my's" with this
+extern long myx, omyx, myxvel, myy, omyy, myyvel, myz, omyz, myzvel;
+extern short myhoriz, omyhoriz, myhorizoff, omyhorizoff, globalskillsound;
+extern short myang, omyang, mycursectnum, myjumpingcounter;
+extern char myjumpingtoggle, myonground, myhardlanding,myreturntocenter;
+extern long fakemovefifoplc;
+extern long myxbak[MOVEFIFOSIZ], myybak[MOVEFIFOSIZ], myzbak[MOVEFIFOSIZ];
+extern long myhorizbak[MOVEFIFOSIZ];
+extern short myangbak[MOVEFIFOSIZ];
+
+extern short weaponsandammosprites[15];
+
+
+
+
+//DUKE3D.H:
+typedef struct
+{
+        short frag[MAXPLAYERS], got_access, last_extra, shield_amount, curr_weapon;
+        short ammo_amount[MAX_WEAPONS], holoduke_on;
+        char gotweapon[MAX_WEAPONS], inven_icon, jetpack_on, heat_on;
+        short firstaid_amount, steroids_amount, holoduke_amount, jetpack_amount;
+        short heat_amount, scuba_amount, boot_amount;
+        short last_weapon, weapon_pos, kickback_pic;
+
+} STATUSBARTYPE;
+
+extern STATUSBARTYPE sbar;
+extern short frags[MAXPLAYERS][MAXPLAYERS];
+extern long cameradist, cameraclock, dukefriction,show_shareware;
+extern char networkmode, movesperpacket;
+extern char gamequit;
+
+extern char pus,pub,camerashitable,freezerhurtowner,lasermode;
+extern char syncstat, syncval[MAXPLAYERS][MOVEFIFOSIZ];
+extern signed char multiwho, multipos, multiwhat, multiflag;
+extern long syncvalhead[MAXPLAYERS], syncvaltail, syncvaltottail;
+extern long numfreezebounces,rpgblastradius,pipebombblastradius,tripbombblastradius,shrinkerblastradius,morterblastradius,bouncemineblastradius,seenineblastradius;
+// CTW - MODIFICATION
+// extern char stereo,eightytwofifty,playerswhenstarted,playonten,everyothertime;
+extern char stereo,eightytwofifty,playerswhenstarted,everyothertime;
+// CTW END - MODIFICATION
+extern long myminlag[MAXPLAYERS], mymaxlag, otherminlag, bufferjitter;
+
+extern long numinterpolations, startofdynamicinterpolations;
+extern long oldipos[MAXINTERPOLATIONS];
+extern long bakipos[MAXINTERPOLATIONS];
+extern long *curipos[MAXINTERPOLATIONS];
+
+extern short numclouds,clouds[128],cloudx[128],cloudy[128];
+extern long cloudtotalclock,totalmemory;
+
+extern long stereomode, stereowidth, stereopixelwidth;
+
+extern long myaimmode, myaimstat, omyaimstat;
+
+extern unsigned char nHostForceDisableAutoaim;
+
+#endif  // include-once header.
+
--- /dev/null
+++ b/Game/src/dukewin.h
@@ -1,0 +1,89 @@
+#ifndef _INCL_DUKEWIN_H_
+#define _INCL_DUKEWIN_H_ 1
+
+#ifndef _MSC_VER  /* might need this. */
+typedef long long __int64;
+#endif
+
+#pragma warning(disable:4761)
+
+#if USE_SDL
+#include "SDL.h"
+#endif
+
+#ifdef _DEBUG
+#define STUBBED(x) printf("STUB: %s in %s:%d\n", x, __FILE__, __LINE__)
+#else
+#define STUBBED(x)
+#endif
+
+#define __far
+#define __interrupt
+
+#define PATH_SEP_CHAR '\\'
+#define PATH_SEP_STR  "\\"
+
+#include <sys/stat.h>
+#include <io.h>
+#include <assert.h>
+
+struct find_t
+{
+	long handle;
+    struct _finddata_t data;
+	char name[MAX_PATH];
+};
+int _dos_findfirst(char *filename, int x, struct find_t *f);
+int _dos_findnext(struct find_t *f);
+
+struct dosdate_t
+{
+    unsigned char day;
+    unsigned char month;
+    unsigned int year;
+    unsigned char dayofweek;
+};
+
+void _dos_getdate(struct dosdate_t *date);
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef max
+#define max(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef FP_OFF
+#undef FP_OFF
+#endif
+#define FP_OFF(x) ((long) (x))
+
+// 64 megs should be enough for anybody.  :)  --ryan.
+#define Z_AvailHeap() ((64 * 1024) * 1024)
+
+#define printchrasm(x,y,ch) printf("%c", (char) (ch & 0xFF))
+
+#define cdecl
+
+#define open _open
+#define O_BINARY _O_BINARY
+#define O_RDONLY _O_RDONLY
+#define O_WRONLY _O_WRONLY
+#define O_RDWR _O_RDWR
+#define O_TRUNC _O_TRUNC
+#define O_CREAT _O_CREAT
+#define S_IREAD _S_IREAD
+#define S_IWRITE _S_IWRITE
+#define S_IRDWR _S_IRDWR
+
+#define S_IRUSR S_IREAD
+#define S_IWUSR S_IWRITE
+#define S_IRGRP 0
+#define S_IWGRP 0
+
+#define F_OK 0
+
+#define HAVE_PRAGMA_PACK 1
+
+#endif
--- /dev/null
+++ b/Game/src/file_lib.h
@@ -1,0 +1,260 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef _file_lib_public
+#define _file_lib_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+   {
+   filetype_binary,
+   filetype_text
+   };
+
+enum
+   {
+   access_read,
+   access_write,
+   access_append
+   };
+
+//==========================================================================
+//
+// SafeOpenWrite - Opens a file for writing, returns handle
+//
+//==========================================================================
+int32 SafeOpenWrite ( const char * filename, int32 filetype );
+
+//==========================================================================
+//
+// SafeOpenRead - Opens a file for reading, returns handle
+//
+//==========================================================================
+int32 SafeOpenRead ( const char * filename, int32 filetype );
+
+//==========================================================================
+//
+// SafeOpenAppend - Opens a file for appending, returns handle
+//
+//==========================================================================
+int32 SafeOpenAppend ( const char * filename, int32 filetype );
+
+//==========================================================================
+//
+// SafeClose - Close a file denoted by the file handle
+//
+//==========================================================================
+void SafeClose ( int32 handle );
+
+//==========================================================================
+//
+// SafeFileExists - Checks for existence of file
+//
+//==========================================================================
+boolean SafeFileExists ( const char * filename );
+
+//==========================================================================
+//
+// SafeFileLength - Get length of a file pointed to by handle
+//
+//==========================================================================
+int32 SafeFileLength ( int32 handle );
+
+//==========================================================================
+//
+// SafeRead - reads from a handle
+//
+//            handle - handle of file to read from
+//
+//            buffer - pointer of buffer to read into
+//
+//            count  - number of bytes to read
+//
+//==========================================================================
+void SafeRead (int32 handle, void *buffer, int32 count);
+
+//==========================================================================
+//
+// SafeWrite - writes to a handle
+//
+//             handle - handle of file to write to
+//
+//             buffer - pointer of buffer to write from
+//
+//             count  - number of bytes to write
+//
+//==========================================================================
+void SafeWrite (int32 handle, void *buffer, int32 count);
+
+//==========================================================================
+//
+// LoadFile - Load a file
+//
+//            filename - name of file
+//
+//            bufferptr - pointer to pointer of buffer to read into
+//
+//            returns number of bytes read
+//
+//==========================================================================
+int32 LoadFile ( const char * filename, void ** bufferptr );
+
+//==========================================================================
+//
+// SaveFile - Save a file
+//
+//            filename - name of file
+//
+//            bufferptr - pointer to buffer to write from
+//
+//            count - number of bytes to write
+//
+//==========================================================================
+void SaveFile ( const char * filename, void * bufferptr, int32 count );
+
+//==========================================================================
+//
+// GetPathFromEnvironment - Add a pathname described in an environment
+//                          variable to a standard filename.
+//
+//                          fullname - final string containing entire path
+//
+//                          envname - string naming enivronment variable
+//
+//                          filename - standard filename
+//
+//==========================================================================
+void GetPathFromEnvironment( char *fullname, const char *envname, const char *filename );
+
+//==========================================================================
+//
+// DefaultExtension - Add a default extension to a path
+//
+//                    path - a path
+//
+//                    extension - default extension should include '.'
+//
+//==========================================================================
+void DefaultExtension (char *path, const char *extension);
+
+//==========================================================================
+//
+// DefaultPath - Add the default path to a filename if it doesn't have one
+//
+//               path - filename
+//
+//               extension - default path
+//
+//==========================================================================
+void DefaultPath (char *path, const char *basepath);
+
+//==========================================================================
+//
+// ExtractFileBase - Extract the base filename from a path
+//
+//                   path - the path
+//
+//                   dest - where the file base name will be placed
+//
+//==========================================================================
+void ExtractFileBase (char *path, char *dest);
+
+//==========================================================================
+//
+// GetExtension - Extract the extension from a name
+//                returns true if an extension is found
+//                returns false otherwise
+//
+//==========================================================================
+boolean GetExtension( char *filename, char *extension );
+
+//==========================================================================
+//
+// SetExtension - Sets the extension from a name.  Assumes that enough
+// 					space is left at the end of the string to hold an extension.
+//
+//==========================================================================
+void SetExtension( char *filename, const char *extension );
+
+#ifdef __MSDOS__
+//******************************************************************************
+//
+// GetPath
+//
+// Purpose
+//    To parse the directory entered by the user to make the directory.
+//
+// Parms
+//    Path - the path to be parsed.
+//
+// Returns
+//    Pointer to next path
+//
+//******************************************************************************
+char * GetPath (char * path, char *dir);
+
+//******************************************************************************
+//
+// ChangeDirectory ()
+//
+// Purpose
+//    To change to a directory.  Checks for drive changes.
+//
+// Parms
+//    path - The path to change to.
+//
+// Returns
+//    TRUE  - If successful.
+//    FALSE - If unsuccessful.
+//
+//******************************************************************************
+boolean ChangeDirectory (char * path);
+
+//******************************************************************************
+//
+// ChangeDrive ()
+//
+// Purpose
+//    To change drives.
+//
+// Parms
+//    drive - The drive to change to.
+//
+// Returns
+//    TRUE  - If drive change successful.
+//    FALSE - If drive change unsuccessful.
+//
+//******************************************************************************
+boolean ChangeDrive (char *drive);
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/funct.h
@@ -1,0 +1,588 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+extern void sendscore(char *s);
+//#line "sounds.c" 25
+extern void SoundStartup(void );
+//#line "sounds.c" 95
+extern void SoundShutdown(void );
+//#line "sounds.c" 118
+extern void MusicStartup(void );
+//#line "sounds.c" 166
+extern void MusicShutdown(void );
+//#line "sounds.c" 181
+extern int USRHOOKS_GetMem(char **ptr,unsigned long size);
+//#line "sounds.c" 192
+extern int USRHOOKS_FreeMem(char *ptr);
+//#line "sounds.c" 200
+extern void intomenusounds(void );
+//#line "sounds.c" 227
+extern void playmusic(char *fn);
+//#line "sounds.c" 251
+extern char loadsound(unsigned short num);
+//#line "sounds.c" 277
+extern int xyzsound(short num,short i,long x,long y,long z);
+//#line "sounds.c" 407
+extern void sound(short num);
+//#line "sounds.c" 463
+extern int spritesound(unsigned short num,short i);
+//#line "sounds.c" 469
+extern void stopsound(short num);
+//#line "sounds.c" 478
+extern void stopenvsound(short num,short i);
+//#line "sounds.c" 494
+extern void pan3dsound(void );
+//#line "sounds.c" 571
+extern void TestCallBack(unsigned long num);
+//#line "sector.c" 9
+extern short callsound(short sn,short whatsprite);
+//#line "sector.c" 56
+extern short check_activator_motion(short lotag);
+//#line "sector.c" 93
+extern char isadoorwall(short dapic);
+//#line "sector.c" 124
+extern char isanunderoperator(short lotag);
+//#line "sector.c" 139
+extern char isanearoperator(short lotag);
+//#line "sector.c" 161
+extern short checkcursectnums(short sect);
+//#line "sector.c" 169
+extern long ldist(spritetype *s1,spritetype *s2);
+//#line "sector.c" 177
+extern long dist(spritetype *s1,spritetype *s2);
+//#line "sector.c" 186
+extern short findplayer(spritetype *s,long *d);
+//#line "sector.c" 214
+extern short findotherplayer(short p,long *d);
+//#line "sector.c" 240
+extern void doanimations(void );
+//#line "sector.c" 301
+extern long getanimationgoal(long *animptr);
+//#line "sector.c" 315
+extern long setanimation(short animsect,long *animptr,long thegoal,long thevel);
+//#line "sector.c" 348
+extern void animatecamsprite(void );
+//#line "sector.c" 369
+extern void animatewalls(void );
+//#line "sector.c" 457
+extern char activatewarpelevators(short s,short d);
+//#line "sector.c" 504
+extern void operatesectors(short sn,short ii);
+//#line "sector.c" 997
+extern void operaterespawns(short low);
+//#line "sector.c" 1020
+extern void operateactivators(short low,short snum);
+//#line "sector.c" 1089
+extern void operatemasterswitches(short low);
+//#line "sector.c" 1104
+extern void operateforcefields(short s,short low);
+//#line "sector.c" 1140
+extern char checkhitswitch(short snum,long w,char switchtype);
+//#line "sector.c" 1515
+extern void activatebysector(short sect,short j);
+//#line "sector.c" 1532
+extern void checkhitwall(short spr,short dawallnum,long x,long y,long z,short atwith);
+//#line "sector.c" 1746
+extern void checkplayerhurt(struct player_struct *p,short j);
+//#line "sector.c" 1816
+extern char checkhitceiling(short sn);
+//#line "sector.c" 1891
+extern void checkhitsprite(short i,short sn);
+//#line "sector.c" 2326
+extern void allignwarpelevators(void );
+//#line "sector.c" 2357
+extern void cheatkeys(short snum);
+//#line "sector.c" 2766
+extern void checksectors(short snum);
+//#line "rts.c" 36
+extern void RTS_AddFile(char *filename);
+//#line "rts.c" 93
+extern void RTS_Init(char *filename);
+//#line "rts.c" 126
+extern int32 RTS_NumSounds(void );
+//#line "rts.c" 141
+extern int32 RTS_SoundLength(int32 lump);
+//#line "rts.c" 157
+extern char *RTS_GetSoundName(int32 i);
+//#line "rts.c" 174
+extern void RTS_ReadLump(int32 lump,void *dest);
+//#line "rts.c" 194
+extern void *RTS_GetSound(int32 lump);
+//#line "premap.c" 7
+extern void xyzmirror(short i,short wn);
+//#line "premap.c" 20
+extern void vscrn(void );
+//#line "premap.c" 58
+int countfragbars(void);
+extern void pickrandomspot(short snum);
+//#line "premap.c" 80
+extern void resetplayerstats(short snum);
+//#line "premap.c" 187
+extern void resetweapons(short snum);
+//#line "premap.c" 212
+extern void resetinventory(short snum);
+//#line "premap.c" 232
+extern void resetprestat(short snum,char g);
+//#line "premap.c" 294
+extern void setupbackdrop(short backpicnum);
+//#line "premap.c" 329
+extern void cachespritenum(short i);
+//#line "premap.c" 443
+extern void cachegoodsprites(void );
+//#line "premap.c" 516
+extern void prelevel(char g);
+//#line "premap.c" 823
+extern void newgame(char vn,char ln,char sk);
+//#line "premap.c" 874
+extern void resetpspritevars(char g);
+//#line "premap.c" 1012
+extern void resettimevars(void );
+//#line "premap.c" 1043
+extern void genspriteremaps(void );
+//#line "premap.c" 1077
+extern void waitforeverybody(void);
+//#line "premap.c" 1131
+extern char checksum(long sum);
+//#line "premap.c" 1163
+extern char getsound(unsigned short num);
+//#line "premap.c" 1189
+extern void precachenecessarysounds(void );
+//#line "premap.c" 1201
+extern void cacheit(void );
+//#line "premap.c" 1244
+extern void dofrontscreens(void );
+//#line "premap.c" 1285
+extern void enterlevel(char g);
+//#line "player.c" 10
+extern void setpal(struct player_struct *p);
+//#line "player.c" 28
+extern void incur_damage(struct player_struct *p);
+//#line "player.c" 59
+extern void quickkill(struct player_struct *p);
+//#line "player.c" 73
+extern void forceplayerangle(struct player_struct *p);
+//#line "player.c" 85
+extern void tracers(long x1,long y1,long z1,long x2,long y2,long z2,long n);
+//#line "player.c" 114
+extern long hits(short i);
+//#line "player.c" 131
+extern long hitasprite(short i,short *hitsp);
+//#line "player.c" 152
+extern long hitawall(struct player_struct *p,short *hitw);
+//#line "player.c" 163
+extern short aim(spritetype *s,short aang, short auto_aim);
+//#line "player.c" 234
+extern void shoot(short i,short atwith);
+//#line "player.c" 939
+extern void displayloogie(short snum);
+//#line "player.c" 958
+extern char animatefist(short gs,short snum);
+//#line "player.c" 986
+extern char animateknee(short gs,short snum);
+//#line "player.c" 1002
+extern char animateknuckles(short gs,short snum);
+//#line "player.c" 1022
+extern void displaymasks(short snum);
+//#line "player.c" 1043
+extern char animatetip(short gs,short snum);
+//#line "player.c" 1064
+extern char animateaccess(short gs,short snum);
+//#line "player.c" 1090
+extern void displayweapon(short snum);
+//#line "player.c" 1501
+extern void getinput(short snum);
+//#line "player.c" 1742
+extern char doincrements(struct player_struct *p);
+//#line "player.c" 1935
+extern void checkweapons(struct player_struct *p);
+//#line "player.c" 1951
+extern void processinput(short snum);
+//#line "menues.c" 18
+extern void cmenu(short cm);
+//#line "menues.c" 38
+extern void savetemp(char *fn,long daptr,long dasiz);
+//#line "menues.c" 49
+extern void getangplayers(short snum);
+//#line "menues.c" 67
+extern int loadpheader(char spot,int32 *vn,int32 *ln,int32 *psk,int32 *numplr);
+//#line "menues.c" 105
+extern int loadplayer(signed char spot);
+//#line "menues.c" 276
+extern int saveplayer(signed char spot);
+//#line "menues.c" 421
+extern void sendgameinfo(void );
+//#line "menues.c" 434
+extern int probe(int x,int y,int i,int n);
+extern int probeXduke(int x,int y,int i,int n, long spriteSize);
+//#line "menues.c" 521
+extern int menutext(int x,int y,short s,short p,char *t);
+//#line "menues.c" 630
+extern int menutextc(int x,int y,short s,short p,char *t);
+//#line "menues.c" 727
+extern void bar(int x,int y,short *p,short dainc,char damodify,short s,short pa);
+//#line "menues.c" 806
+extern void dispnames(void );
+//#line "menues.c" 832
+extern int getfilenames(char kind[]);
+//#line "menues.c" 865
+extern void sortfilenames(void);
+//#line "menues.c" 886
+extern void menus(void );
+void gameexitanycase(void);
+//#line "menues.c" 2414
+extern void palto(char r,char g,char b,long e);
+//#line "menues.c" 2436
+extern void drawoverheadmap(long cposx,long cposy,long czoom,short cang);
+//#line "menues.c" 2685
+extern void playanm(char *fn,char);
+//#line "gamedef.c" 122
+extern short getincangle(short a,short na);
+//#line "gamedef.c" 140
+extern char ispecial(char c);
+//#line "gamedef.c" 154
+extern char isaltok(char c);
+//#line "gamedef.c" 159
+extern void getglobalz(short i);
+//#line "gamedef.c" 203
+extern void makeitfall(short i);
+//#line "gamedef.c" 243
+extern void getlabel(void );
+//#line "gamedef.c" 267
+extern long keyword(void );
+//#line "gamedef.c" 300
+extern long transword(void );
+//#line "gamedef.c" 342
+extern void transnum(void );
+//#line "gamedef.c" 402
+extern char parsecommand(int readfromGRP );
+//#line "gamedef.c" 1227
+extern void passone(int readfromGRP);
+//#line "gamedef.c" 1239
+extern void loadefs(char *fn,char *mptr, int readfromGRP);
+//#line "gamedef.c" 1342
+extern char dodge(spritetype *s);
+//#line "gamedef.c" 1374
+extern short furthestangle(short i,short angs);
+//#line "gamedef.c" 1404
+extern short furthestcanseepoint(short i,spritetype *ts,long *dax,long *day);
+//#line "gamedef.c" 1444
+extern void alterang(short a);
+//#line "gamedef.c" 1512
+extern void move(void);
+//#line "gamedef.c" 1711
+extern void parseifelse(long condition);
+//#line "gamedef.c" 1729
+extern char parse(void );
+//#line "gamedef.c" 2699
+extern void execute(short i,short p,long x);
+//#line "game.c" 63
+extern void overwritesprite(long thex,long they,short tilenum,signed char shade,char stat,char dapalnum);
+//#line "game.c" 70
+extern void timerhandler(void);
+//#line "game.c" 75
+extern int inittimer(int);
+//#line "game.c" 81
+extern void uninittimer(void );
+//#line "game.c" 89
+extern int gametext(int x,int y,char *t,char s,short dabits);
+//#line "game.c" 136
+extern int gametextpart(int x,int y,char *t,char s,short p);
+//#line "game.c" 192
+extern int minitext(int x,int y,char *t,char p,char sb);
+//#line "game.c" 231
+extern void gamenumber(long x,long y,long n,char s);
+//#line "game.c" 248
+extern void ShutDown(void );
+//#line "game.c" 260
+extern void allowtimetocorrecterrorswhenquitting(void );
+//#line "game.c" 280
+extern void getpackets(void );
+//#line "game.c" 502
+extern void faketimerhandler(void);
+//#line "game.c" 788
+extern void checksync(void );
+//#line "game.c" 815
+extern void check_fta_sounds(short i);
+//#line "game.c" 869
+extern short inventory(spritetype *s);
+//#line "game.c" 886
+extern short badguy(spritetype *s);
+//#line "game.c" 924
+extern short badguypic(short pn);
+//#line "game.c" 963
+extern void myos(long x,long y,short tilenum,signed char shade,char orientation);
+//#line "game.c" 976
+extern void myospal(long x,long y,short tilenum,signed char shade,char orientation,char p);
+//#line "game.c" 991
+extern void invennum(long x,long y,char num1,char ha,char sbits);
+//#line "game.c" 1021
+extern void weaponnum(short ind,long x,long y,long num1,long num2,char ha);
+//#line "game.c" 1049
+extern void weaponnum999(char ind,long x,long y,long num1,long num2,char ha);
+//#line "game.c" 1088
+extern void weapon_amounts(struct player_struct *p,long x,long y,long u);
+//#line "game.c" 1197
+extern void digitalnumber(long x,long y,long n,char s,char cs);
+//#line "game.c" 1223
+extern void scratchmarks(long x,long y,long n,char s,char p);
+//#line "game.c" 1238
+extern void displayinventory(struct player_struct *p);
+//#line "game.c" 1296
+extern void displayfragbar(void );
+//#line "game.c" 1318
+void drawsmallweapon(short weapon, float scale, short x, short y); // xduke
+extern void coolgaugetext(short snum);
+//#line "game.c" 1557
+extern void tics(short offx, short offy , short color);
+//#line "game.c" 1572
+extern void clocks(void );
+//#line "game.c" 1582
+extern void coords(short snum);
+//#line "game.c" 1607
+extern void operatefta(void);
+//#line "game.c" 1654
+extern void FTA(short q,struct player_struct *p, int mode);
+//#line "game.c" 1668
+extern void showtwoscreens(void );
+//#line "game.c" 1691
+extern void binscreen(void );
+//#line "game.c" 1705
+extern void gameexit(char *t);
+//#line "game.c" 1752
+extern short strget(short x,short y,char *t,short dalen,short c);
+//#line "game.c" 1819
+extern void displayrest(long smoothratio);
+//#line "game.c" 2047
+extern void updatesectorz(long x,long y,long z,short *sectnum);
+//#line "game.c" 2085
+extern void view(struct player_struct *pp,long *vx,long *vy,long *vz,short *vsectnum,short ang,short horiz);
+//#line "game.c" 2137
+extern void drawbackground(void );
+//#line "game.c" 2200
+extern void displayrooms(short snum,long smoothratio);
+//#line "game.c" 2445
+extern short LocateTheLocator(short n,short sn);
+//#line "game.c" 2459
+extern short EGS(short whatsect,long s_x,long s_y,long s_z,short s_pn,signed char s_s,signed char s_xr,signed char s_yr,short s_a,short s_ve,long s_zv,short s_ow,signed char s_ss);
+//#line "game.c" 2537
+extern char wallswitchcheck(short i);
+//#line "game.c" 2588
+extern short spawn(short j,short pn);
+//#line "game.c" 4181
+extern void animatesprites(long x,long y,short a,long smoothratio);
+//#line "game.c" 4859
+extern void cheats(void );
+//#line "game.c" 5303
+extern void nonsharedkeys(void );
+//#line "game.c" 5863
+extern void comlinehelp(char **argv);
+//#line "game.c" 5889
+extern void checkcommandline(int argc,char **argv);
+//#line "game.c" 6078
+extern void printstr(short x,short y,char string[],char attribute);
+//#line "game.c" 6104
+extern void Logo(void );
+//#line "game.c" 6187
+extern void loadtmb(void );
+//#line "game.c" 6210
+extern void compilecons(void );
+//#line "game.c" 6230
+extern void Startup(void );
+//#line "game.c" 6284
+extern void getnames(void );
+//#line "game.c" 6309
+extern int main(int argc,char **argv);
+//#line "game.c" 6563
+extern char opendemoread(char which_demo);
+//#line "game.c" 6589
+extern void opendemowrite(void );
+//#line "game.c" 6608
+extern void record(void );
+//#line "game.c" 6626
+extern void closedemowrite(void );
+//#line "game.c" 6642
+extern long playback(void );
+//#line "game.c" 6777
+extern char moveloop(void);
+//#line "game.c" 6796
+extern void fakedomovethingscorrect(void);
+//#line "game.c" 6829
+extern void fakedomovethings(void );
+//#line "game.c" 7247
+extern char domovethings(void );
+//#line "game.c" 7373
+extern void displaybonuspics(short x,short y,short p);
+//#line "game.c" 7396
+extern void doorders(void );
+//#line "game.c" 7432
+extern void dobonus(char bonusonly);
+//#line "game.c" 7846
+extern void cameratext(short i);
+//#line "game.c" 7869
+extern void vglass(long x,long y,short a,short wn,short n);
+//#line "game.c" 7882
+extern void lotsofglass(short i,short wallnum,short n);
+//#line "game.c" 7924
+extern void spriteglass(short i,short n);
+//#line "game.c" 7937
+extern void ceilingglass(short i,short sectnum,short n);
+//#line "game.c" 7966
+extern void lotsofcolourglass(short i,short wallnum,short n);
+//#line "game.c" 8004
+extern void SetupGameButtons(void );
+//#line "game.c" 8068
+extern long GetTime(void );
+//#line "game.c" 8082
+extern void CenterCenter(void );
+//#line "game.c" 8095
+extern void UpperLeft(void );
+//#line "game.c" 8108
+extern void LowerRight(void );
+//#line "game.c" 8121
+extern void CenterThrottle(void );
+//#line "game.c" 8134
+extern void CenterRudder(void );
+//#line "game.c" ????
+extern void takescreenshot(void );
+//#line "config.c" 57
+extern void CONFIG_GetSetupFilename(void );
+//#line "config.c" 170
+extern int32 CONFIG_FunctionNameToNum(char *func);
+//#line "config.c" 192
+extern char *CONFIG_FunctionNumToName(int32 func);
+//#line "config.c" 211
+extern int32 CONFIG_AnalogNameToNum(char *func);
+//#line "config.c" 240
+extern void CONFIG_SetDefaults(void );
+//#line "config.c" 264
+extern void CONFIG_ReadKeys(void );
+//#line "config.c" 314
+extern void CONFIG_SetupMouse(int32 scripthandle);
+//#line "config.c" 376
+extern void CONFIG_SetupGamePad(int32 scripthandle);
+//#line "config.c" 427
+extern void CONFIG_SetupJoystick(int32 scripthandle);
+//#line "config.c" 485
+extern void readsavenames(void );
+//#line "config.c" 512
+extern void CONFIG_ReadSetup(void );
+//#line "config.c" 613
+extern void CONFIG_WriteSetup(void );
+//#line "animlib.c" 34
+extern void CheckAnimStarted(char *funcname);
+//#line "animlib.c" 46
+extern uint16 findpage(uint16 framenumber);
+//#line "animlib.c" 71
+extern void loadpage(uint16 pagenumber,uint16 *pagepointer);
+//#line "animlib.c" 97
+extern void CPlayRunSkipDump(char *srcP,char *dstP);
+//#line "animlib.c" 177
+extern void renderframe(uint16 framenumber,uint16 *pagepointer);
+//#line "animlib.c" 214
+extern void drawframe(uint16 framenumber);
+//#line "animlib.c" 228
+extern void ANIM_LoadAnim(char *buffer);
+//#line "animlib.c" 260
+extern void ANIM_FreeAnim(void );
+//#line "animlib.c" 275
+extern int32 ANIM_NumFrames(void );
+//#line "animlib.c" 287
+extern byte *ANIM_DrawFrame(int32 framenumber);
+//#line "animlib.c" 312
+extern byte *ANIM_GetPalette(void );
+//#line "actors.c" 7
+extern void updateinterpolations(void);
+//#line "actors.c" 15
+extern void setinterpolation(long *posptr);
+//#line "actors.c" 27
+extern void stopinterpolation(long *posptr);
+//#line "actors.c" 41
+extern void dointerpolations(long smoothratio);
+//#line "actors.c" 55
+extern void restoreinterpolations(void);
+//#line "actors.c" 63
+extern long ceilingspace(short sectnum);
+//#line "actors.c" 77
+extern long floorspace(short sectnum);
+//#line "actors.c" 91
+extern void addammo(short weapon,struct player_struct *p,short amount);
+//#line "actors.c" 99
+extern void addweapon(struct player_struct *p,short weapon);
+//#line "actors.c" 132
+extern void checkavailinven(struct player_struct *p);
+//#line "actors.c" 152
+extern void checkavailweapon(struct player_struct *p);
+//#line "actors.c" 301
+extern long ifsquished(short i,short p);
+//#line "actors.c" 326
+extern void hitradius(short i,long r,long hp1,long hp2,long hp3,long hp4);
+//#line "actors.c" 556
+extern int movesprite(short spritenum,long xchange,long ychange,long zchange,unsigned long cliptype);
+//#line "actors.c" 643
+extern short ssp(short i,unsigned long cliptype);
+//#line "actors.c" 658
+extern void insertspriteq(short i);
+//#line "actors.c" 666
+extern void lotsofmoney(spritetype *s,short n);
+//#line "actors.c" 676
+extern void guts(spritetype *s,short gtype,short n,short p);
+//#line "actors.c" 718
+extern void setsectinterpolate(short i);
+//#line "actors.c" 742
+extern void clearsectinterpolate(short i);
+//#line "actors.c" 760
+extern void ms(short i);
+//#line "actors.c" 791
+extern void movefta(void );
+//#line "actors.c" 882
+extern short ifhitsectors(short sectnum);
+//#line "actors.c" 896
+extern short ifhitbyweapon(short sn);
+//#line "actors.c" 970
+extern void movecyclers(void );
+//#line "actors.c" 1007
+extern void movedummyplayers(void );
+//#line "actors.c" 1053
+extern void moveplayers(void );
+//#line "actors.c" 1192
+extern void movefx(void );
+//#line "actors.c" 1294
+extern void movefallers(void );
+//#line "actors.c" 1389
+extern void movestandables(void );
+//#line "actors.c" 2234
+extern void bounce(short i);
+//#line "actors.c" 2273
+extern void moveweapons(void );
+//#line "actors.c" 2613
+extern void movetransports(void );
+//#line "actors.c" 2887
+extern void moveeffectors(void );
+//#line "actors.c" 4840
+extern void moveactors(void );
+//#line "actors.c" 6005
+extern void moveexplosions(void );
+
--- /dev/null
+++ b/Game/src/function.h
@@ -1,0 +1,106 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+// function.h
+
+// file created by makehead.exe
+// these headers contain default key assignments, as well as
+// default button assignments and game function names
+// axis defaults are also included
+
+
+#ifndef _function_public_
+#define _function_public_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+	#define NUMGAMEFUNCTIONS 55 //dont forget to check NUMKEYENTRIES
+
+extern char * gamefunctions[];
+
+enum
+   {
+   gamefunc_Move_Forward,
+   gamefunc_Move_Backward,
+   gamefunc_Turn_Left,
+   gamefunc_Turn_Right,
+   gamefunc_Strafe,
+   gamefunc_Fire,
+   gamefunc_Open,
+   gamefunc_Run,
+   gamefunc_AutoRun,
+   gamefunc_Jump,
+   gamefunc_Crouch,
+   gamefunc_Look_Up,
+   gamefunc_Look_Down,
+   gamefunc_Look_Left,
+   gamefunc_Look_Right,
+   gamefunc_Strafe_Left,
+   gamefunc_Strafe_Right,
+   gamefunc_Aim_Up,
+   gamefunc_Aim_Down,
+   gamefunc_Weapon_1,
+   gamefunc_Weapon_2,
+   gamefunc_Weapon_3,
+   gamefunc_Weapon_4,
+   gamefunc_Weapon_5,
+   gamefunc_Weapon_6,
+   gamefunc_Weapon_7,
+   gamefunc_Weapon_8,
+   gamefunc_Weapon_9,
+   gamefunc_Weapon_10,
+   gamefunc_Inventory,
+   gamefunc_Inventory_Left,
+   gamefunc_Inventory_Right,
+   gamefunc_Holo_Duke,
+   gamefunc_Jetpack,
+   gamefunc_NightVision,
+   gamefunc_MedKit,
+   gamefunc_TurnAround,
+   gamefunc_SendMessage,
+   gamefunc_Map,
+   gamefunc_Shrink_Screen,
+   gamefunc_Enlarge_Screen,
+   gamefunc_Center_View,
+   gamefunc_Holster_Weapon,
+   gamefunc_Show_Opponents_Weapon,
+   gamefunc_Map_Follow_Mode,
+   gamefunc_See_Coop_View,
+   gamefunc_Mouse_Aiming,
+   gamefunc_Toggle_Crosshair,
+   gamefunc_Steroids,
+   gamefunc_Quick_Kick,
+   gamefunc_Next_Weapon,
+   gamefunc_Previous_Weapon,
+   gamefunc_Hide_Weapon,
+   gamefunc_Auto_Aim,
+   gamefunc_Console
+   };
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/game.c
@@ -1,0 +1,11028 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include <windows.h>
+#include "types.h"
+
+#include "develop.h"
+#include "scriplib.h"
+#include "file_lib.h"
+#include "gamedefs.h"
+#include "keyboard.h"
+#include "util_lib.h"
+#include "function.h"
+#include "control.h"
+#include "sounds.h"
+#include "config.h"
+#include "audiolib\sndcards.h"
+
+#include "duke3d.h"
+
+#include "console.h"
+#include "cvars.h"
+#include "cvar_defs.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#pragma message("game.c this is So lame")
+#include "cache1d.h"
+
+#define MINITEXT_BLUE	0
+#define MINITEXT_RED	2
+#define MINITEXT_YELLOW	23
+#define MINITEXT_GRAY	17
+
+#define COLOR_ON  MINITEXT_YELLOW
+#define COLOR_OFF MINITEXT_BLUE
+
+#define IDFSIZE 479985668
+// #define IDFSIZE 9961476
+// #define IDFSIZE 16384
+#define IDFILENAME "DUKE3D.IDF"
+
+#define TIMERUPDATESIZ 32
+
+long cameradist = 0, cameraclock = 0;
+char eightytwofifty = 0;
+char playerswhenstarted;
+char qe,cp;
+
+unsigned char nHostForceDisableAutoaim = 0;
+
+// Game play speed
+int g_iTickRate = 120;
+int g_iTicksPerFrame = 26;
+
+int32 CommandSoundToggleOff = 0;
+int32 CommandMusicToggleOff = 0;
+
+// For addfaz's stun server. use /stun to activate
+unsigned short g_bStun = 0;
+
+char confilename[128] = {"GAME.CON"},boardfilename[128] = {0};
+char waterpal[768], slimepal[768], titlepal[768], drealms[768], endingpal[768];
+char firstdemofile[80] = { '\0' };
+
+#define patchstatusbar(x1,y1,x2,y2)                                        \
+{                                                                          \
+    rotatesprite(0,(200-34)<<16,65536L,0,BOTTOMSTATUSBAR,4,0,10+16+64+128, \
+        scale(x1,xdim,320),scale(y1,ydim,200),                             \
+        scale(x2,xdim,320)-1,scale(y2,ydim,200)-1);                        \
+}
+
+void __interrupt __far newint24( int errval, int ax, int bp, int si );
+
+int recfilep,totalreccnt;
+char debug_on = 0,actor_tog = 0,*rtsptr,memorycheckoveride=0;
+
+
+
+extern char syncstate;
+extern int32 numlumps;
+
+FILE *frecfilep = (FILE *)NULL;
+void pitch_test( void );
+
+char restorepalette,screencapt,nomorelogohack;
+int sendmessagecommand = -1;
+
+#if PLATFORM_DOS
+task *TimerPtr=NULL;
+#endif
+
+extern long lastvisinc;
+
+// Build Engine port implements this.  --ryan.
+#if PLATFORM_DOS
+static void timerhandler(task *unused)
+{
+    totalclock++;
+}
+
+void inittimer()
+{
+    TimerPtr = TS_ScheduleTask( timerhandler,TICRATE, 1, NULL );
+    TS_Dispatch();
+}
+
+void uninittimer(void)
+{
+   if (TimerPtr)
+      TS_Terminate( TimerPtr );
+   TimerPtr = NULL;
+   TS_Shutdown();
+}
+#else
+void timerhandler(void)
+{
+    totalclock++;
+}
+#endif
+
+int gametext(int x,int y,char *t,char s,short dabits)
+{
+    short ac,newx;
+    char centre, *oldt;
+
+    centre = ( x == (320>>1) );
+    newx = 0;
+    oldt = t;
+
+    if(centre)
+    {
+        while(*t)
+        {
+            if(*t == 32) {newx+=5;t++;continue;}
+            else ac = *t - '!' + STARTALPHANUM;
+
+            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;
+
+            if(*t >= '0' && *t <= '9')
+                newx += 8;
+            else newx += tilesizx[ac];
+            t++;
+        }
+
+        t = oldt;
+        x = (320>>1)-(newx>>1);
+    }
+
+    while(*t)
+    {
+        if(*t == 32) {x+=5;t++;continue;}
+        else ac = *t - '!' + STARTALPHANUM;
+
+        if( ac < STARTALPHANUM || ac > ENDALPHANUM )
+            break;
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,s,0,dabits,0,0,xdim-1,ydim-1);
+
+        if(*t >= '0' && *t <= '9')
+            x += 8;
+        else x += tilesizx[ac];
+
+        t++;
+    }
+
+    return (x);
+}
+
+int gametextpal(int x,int y,char *t,char s,char p)
+{
+    short ac,newx;
+    char centre, *oldt;
+
+    centre = ( x == (320>>1) );
+    newx = 0;
+    oldt = t;
+
+    if(centre)
+    {
+        while(*t)
+        {
+            if(*t == 32) {newx+=5;t++;continue;}
+            else ac = *t - '!' + STARTALPHANUM;
+
+            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;
+
+            if(*t >= '0' && *t <= '9')
+                newx += 8;
+            else newx += tilesizx[ac];
+            t++;
+        }
+
+        t = oldt;
+        x = (320>>1)-(newx>>1);
+    }
+
+    while(*t)
+    {
+        if(*t == 32) {x+=5;t++;continue;}
+        else ac = *t - '!' + STARTALPHANUM;
+
+        if( ac < STARTALPHANUM || ac > ENDALPHANUM )
+            break;
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,2+8+16,0,0,xdim-1,ydim-1);
+        if(*t >= '0' && *t <= '9')
+            x += 8;
+        else x += tilesizx[ac];
+
+        t++;
+    }
+
+    return (x);
+}
+
+int gametextpart(int x,int y,char *t,char s,short p)
+{
+    short ac,newx, cnt;
+    char centre, *oldt;
+
+    centre = ( x == (320>>1) );
+    newx = 0;
+    oldt = t;
+    cnt = 0;
+
+    if(centre)
+    {
+        while(*t)
+        {
+            if(cnt == p) break;
+
+            if(*t == 32) {newx+=5;t++;continue;}
+            else ac = *t - '!' + STARTALPHANUM;
+
+            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;
+
+            newx += tilesizx[ac];
+            t++;
+            cnt++;
+
+        }
+
+        t = oldt;
+        x = (320>>1)-(newx>>1);
+    }
+
+    cnt = 0;
+    while(*t)
+    {
+        if(*t == 32) {x+=5;t++;continue;}
+        else ac = *t - '!' + STARTALPHANUM;
+
+        if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;
+
+        if(cnt == p)
+        {
+            rotatesprite(x<<16,y<<16,65536L,0,ac,s,1,2+8+16,0,0,xdim-1,ydim-1);
+            break;
+        }
+        else
+            rotatesprite(x<<16,y<<16,65536L,0,ac,s,0,2+8+16,0,0,xdim-1,ydim-1);
+
+        x += tilesizx[ac];
+
+        t++;
+        cnt++;
+    }
+
+    return (x);
+}
+
+int minitext(int x,int y,char *str,char p,char sb)
+{
+    short ac;
+    char buf[128];
+    char *t;
+
+    strncpy (buf, str, 128);
+    buf[127] = 0;
+    t = buf;
+
+    while(*t)
+    {
+        *t = toupper(*t);
+        if(*t == 32) {x+=5;t++;continue;}
+        else ac = *t - '!' + MINIFONT;
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,0,p,sb,0,0,xdim-1,ydim-1);
+        x += 4; // tilesizx[ac]+1;
+
+        t++;
+    }
+    return (x);
+}
+
+int minitextshade(int x,int y,char *str,char s,char p,char sb)
+{
+    short ac;
+    char buf[128];
+    char *t;
+
+    strncpy (buf, str, 128);
+    buf[127] = 0;
+    t = buf;
+
+    while(*t)
+    {
+        *t = toupper(*t);
+        if(*t == 32) {x+=5;t++;continue;}
+        else ac = *t - '!' + MINIFONT;
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,sb,0,0,xdim-1,ydim-1);
+        x += 4; // tilesizx[ac]+1;
+
+        t++;
+    }
+    return (x);
+}
+
+void gamenumber(long x,long y,long n,char s)
+{
+    char b[10];
+    ltoa(n,b,10);
+    gametext(x,y,b,s,2+8+16);
+}
+
+
+char recbuf[80];
+void allowtimetocorrecterrorswhenquitting(void)
+{
+     long i, j, oldtotalclock;
+
+     ready2send = 0;
+
+     for(j=0;j<8;j++)
+     {
+          oldtotalclock = totalclock;
+
+          while (totalclock < oldtotalclock+TICSPERFRAME)
+              getpackets();
+
+          if(KB_KeyPressed(sc_Escape)) return;
+
+          packbuf[0] = 127;
+          for(i=connecthead;i>=0;i=connectpoint2[i])
+                if (i != myconnectindex)
+                     sendpacket(i,packbuf,1);
+     }
+}
+
+#define MAXUSERQUOTES 4
+long quotebot, quotebotgoal;
+short user_quote_time[MAXUSERQUOTES];
+char user_quote[MAXUSERQUOTES][128];
+// char typebuflen,typebuf[41];
+
+static void adduserquote(char *daquote)
+{
+    long i;
+
+    for(i=MAXUSERQUOTES-1;i>0;i--)
+    {
+        strcpy(user_quote[i],user_quote[i-1]);
+        user_quote_time[i] = user_quote_time[i-1];
+    }
+    strcpy(user_quote[0],daquote);
+    user_quote_time[0] = 180;
+    pub = NUMPAGES;
+}
+
+char *grpVersion2char_from_crc(unsigned int crc32_grp_to_identify)
+{
+	char *id;
+	int i=0;
+
+	id = crc32lookup[MAX_KNOWN_GRP].name; // unknown version
+
+	for(i=0; i<MAX_KNOWN_GRP; i++)
+	{
+		if(crc32lookup[i].crc32==crc32_grp_to_identify)
+			id = crc32lookup[i].name;
+	}
+
+	return(id);
+}
+
+char *grpVersion2char(unsigned char grp_to_identify)
+{
+	char *id;
+
+	switch(grp_to_identify)
+	{
+		case DUKEITOUTINDC_GRP:
+			id = "v1.5 DC PACK";
+			break;
+		case SHAREWARE_GRP13:
+			id = "v1.3 SHAREW.";
+			break;
+		case ATOMIC_GRP14_15:
+			id = "v1.5 ATOMIC";
+			break;
+		case REGULAR_GRP13D:
+			id = "v1.3D FULL";
+			break;
+		case UNKNOWN_GRP:
+			id = "vX.X UNKNOWN";
+			break;
+		default:
+			Error(EXIT_FAILURE,"Failed the GRP Identification\n");
+			break;
+	}
+
+	return(id);
+}
+
+void getpackets(void)
+{
+    long i, j, k, l;
+    FILE *fp;
+    short other, packbufleng;
+    input *osyn, *nsyn;
+
+	sampletimer();
+    if(qe == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_Delete))
+    {
+        qe = 1;
+        gameexit("Quick Exit.");
+    }
+
+	// not a net game
+    if (numplayers < 2) 
+	{
+		//printf("getpackets() numplayers < 2");
+		return;
+	}
+
+    while ((packbufleng = getpacket(&other,packbuf)) > 0)
+    {
+#ifdef _DEBUG_NETWORKING_
+		printf("RECEIVED PACKET: type: %d : len %d\n", packbuf[0], packbufleng);
+#endif
+
+        switch(packbuf[0])
+        {
+			case 253:
+				// This should have already been handled by mmulti.cpp so ignore it
+				printf("Invalid Packet: %d", packbuf[0]);
+			break;
+
+            case 125:
+                cp = 0;
+                break;
+
+            case 126:
+                multiflag = 2;
+                multiwhat = 0;
+                multiwho = other;
+                multipos = packbuf[1];
+                loadplayer( multipos );
+                multiflag = 0;
+                break;
+            case 0:  //[0] (receive master sync buffer)
+                j = 1;
+
+                if ((movefifoend[other]&(TIMERUPDATESIZ-1)) == 0)
+                    for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+                    {
+                        if (playerquitflag[i] == 0) continue;
+                        if (i == myconnectindex)
+                            otherminlag = (long)((signed char)packbuf[j]);
+                        j++;
+                    }
+
+                osyn = (input *)&inputfifo[(movefifoend[connecthead]-1)&(MOVEFIFOSIZ-1)][0];
+                nsyn = (input *)&inputfifo[(movefifoend[connecthead])&(MOVEFIFOSIZ-1)][0];
+
+                k = j;
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+                    j += playerquitflag[i];
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+                {
+                    if (playerquitflag[i] == 0) continue;
+
+                    l = packbuf[k++];
+                    if (i == myconnectindex)
+                        { j += ((l&1)<<1)+(l&2)+((l&4)>>2)+((l&8)>>3)+((l&16)>>4)+((l&32)>>5)+((l&64)>>6)+((l&128)>>7); continue; }
+
+                    copybufbyte(&osyn[i],&nsyn[i],sizeof(input));
+                    if (l&1)   nsyn[i].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                    if (l&2)   nsyn[i].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                    if (l&4)   nsyn[i].avel = (signed char)packbuf[j++];
+                    if (l&8)   nsyn[i].bits = ((nsyn[i].bits&0xffffff00)|((long)packbuf[j++]));
+                    if (l&16)  nsyn[i].bits = ((nsyn[i].bits&0xffff00ff)|((long)packbuf[j++])<<8);
+                    if (l&32)  nsyn[i].bits = ((nsyn[i].bits&0xff00ffff)|((long)packbuf[j++])<<16);
+                    if (l&64)  nsyn[i].bits = ((nsyn[i].bits&0x00ffffff)|((long)packbuf[j++])<<24);
+                    if (l&128) nsyn[i].horz = (signed char)packbuf[j++];
+
+                    if (nsyn[i].bits&(1<<26)) playerquitflag[i] = 0;
+                    movefifoend[i]++;
+                }
+
+                while (j != packbufleng)
+                {
+                    for(i=connecthead;i>=0;i=connectpoint2[i])
+                        if(i != myconnectindex)
+                    {
+                        syncval[i][syncvalhead[i]&(MOVEFIFOSIZ-1)] = packbuf[j];
+                        syncvalhead[i]++;
+                    }
+                    j++;
+                }
+
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+                    if (i != myconnectindex)
+                        for(j=1;j<movesperpacket;j++)
+                        {
+                            copybufbyte(&nsyn[i],&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i],sizeof(input));
+                            movefifoend[i]++;
+                        }
+
+                 movefifosendplc += movesperpacket;
+
+                break;
+            case 1:  //[1] (receive slave sync buffer)
+                j = 2; k = packbuf[1];
+
+                osyn = (input *)&inputfifo[(movefifoend[other]-1)&(MOVEFIFOSIZ-1)][0];
+                nsyn = (input *)&inputfifo[(movefifoend[other])&(MOVEFIFOSIZ-1)][0];
+
+                copybufbyte(&osyn[other],&nsyn[other],sizeof(input));
+                if (k&1)   nsyn[other].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                if (k&2)   nsyn[other].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                if (k&4)   nsyn[other].avel = (signed char)packbuf[j++];
+                if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((long)packbuf[j++]));
+                if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((long)packbuf[j++])<<8);
+                if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((long)packbuf[j++])<<16);
+                if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((long)packbuf[j++])<<24);
+                if (k&128) nsyn[other].horz = (signed char)packbuf[j++];
+                movefifoend[other]++;
+
+                while (j != packbufleng)
+                {
+                    syncval[other][syncvalhead[other]&(MOVEFIFOSIZ-1)] = packbuf[j++];
+                    syncvalhead[other]++;
+                }
+
+                for(i=1;i<movesperpacket;i++)
+                {
+                    copybufbyte(&nsyn[other],&inputfifo[movefifoend[other]&(MOVEFIFOSIZ-1)][other],sizeof(input));
+                    movefifoend[other]++;
+                }
+
+                break;
+
+            case 4: // message talk T
+                strcpy(recbuf,packbuf+1);
+                recbuf[packbufleng-1] = 0;
+
+                adduserquote(recbuf);
+                sound(EXITMENUSOUND);
+
+                pus = NUMPAGES;
+                pub = NUMPAGES;
+
+                break;
+
+            case 5:
+                ud.m_level_number = ud.level_number = packbuf[1];
+                ud.m_volume_number = ud.volume_number = packbuf[2];
+                ud.m_player_skill = ud.player_skill = packbuf[3];
+                ud.m_monsters_off = ud.monsters_off = packbuf[4];
+                ud.m_respawn_monsters = ud.respawn_monsters = packbuf[5];
+                ud.m_respawn_items = ud.respawn_items = packbuf[6];
+                ud.m_respawn_inventory = ud.respawn_inventory = packbuf[7];
+                ud.m_coop = packbuf[8];
+                ud.m_marker = ud.marker = packbuf[9];
+                ud.m_ffire = ud.ffire = packbuf[10];
+
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+                {
+                    resetweapons(i);
+                    resetinventory(i);
+                }
+
+                newgame(ud.volume_number,ud.level_number,ud.player_skill);
+                ud.coop = ud.m_coop;
+
+                enterlevel(MODE_GAME);
+
+                break;
+
+            case 6: // get names
+                for (i=2;packbuf[i] && i<=11;i++) // limit size of name
+                    ud.user_name[other][i-2] = packbuf[i];
+                ud.user_name[other][i-2] = 0;
+
+				// we allow the old rancidmeat 19.1 to connect, using the old grpVersion system w/ BYTEVERSION
+				if(packbuf[1] == BYTEVERSION_27 || packbuf[1] == BYTEVERSION_117)
+				{
+					// Old rancid was using either BYTEVERSION_27 or BYTEVERSION_117
+					Error(EXIT_SUCCESS,	"STOP: Your opponent is using an obsolete version\n"
+										"Please ask him to update to xDuke v%d.%d!\n", XDUKE_REV_X, XDUKE_REV_DOT_Y);
+				}
+				break;
+
+            case 9:
+                for (i=1;i<packbufleng;i++)
+                    ud.wchoice[other][i-1] = packbuf[i];
+                break;
+
+            case 7:
+
+                if(numlumps == 0) break;
+
+                if (SoundToggle == 0 || ud.lockout == 1 || FXDevice == NumSoundCards)
+                    break;
+                rtsptr = (char *)RTS_GetSound(packbuf[1]-1);
+                if (*rtsptr == 'C')
+                    FX_PlayVOC3D(rtsptr,0,0,0,255,-packbuf[1]);
+                else
+                    FX_PlayWAV3D(rtsptr,0,0,0,255,-packbuf[1]);
+                rtsplaying = 7;
+                break;
+            case 8:
+                ud.m_level_number = ud.level_number = packbuf[1];
+                ud.m_volume_number = ud.volume_number = packbuf[2];
+                ud.m_player_skill = ud.player_skill = packbuf[3];
+                ud.m_monsters_off = ud.monsters_off = packbuf[4];
+                ud.m_respawn_monsters = ud.respawn_monsters = packbuf[5];
+                ud.m_respawn_items = ud.respawn_items = packbuf[6];
+                ud.m_respawn_inventory = ud.respawn_inventory = packbuf[7];
+                ud.m_coop = ud.coop = packbuf[8];
+                ud.m_marker = ud.marker = packbuf[9];
+                ud.m_ffire = ud.ffire = packbuf[10];
+
+                copybufbyte(packbuf+10,boardfilename,packbufleng-11);
+                boardfilename[packbufleng-11] = 0;
+
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+                {
+                    resetweapons(i);
+                    resetinventory(i);
+                }
+
+                newgame(ud.volume_number,ud.level_number,ud.player_skill);
+                enterlevel(MODE_GAME);
+                break;
+
+            case 16:
+                movefifoend[other] = movefifoplc = movefifosendplc = fakemovefifoplc = 0;
+                syncvalhead[other] = syncvaltottail = 0L;
+
+            case 17:
+                j = 1;
+
+                if ((movefifoend[other]&(TIMERUPDATESIZ-1)) == 0)
+                    if (other == connecthead)
+                        for(i=connectpoint2[connecthead]; i>=0; i=connectpoint2[i])
+                        {
+                            if (i == myconnectindex)
+							{
+								otherminlag = (long)((signed char)packbuf[j]);
+							}
+							
+                            j++;
+                        }
+
+                osyn = (input *)&inputfifo[(movefifoend[other]-1)&(MOVEFIFOSIZ-1)][0];
+                nsyn = (input *)&inputfifo[(movefifoend[other])&(MOVEFIFOSIZ-1)][0];
+
+                copybufbyte(&osyn[other],&nsyn[other],sizeof(input));
+                k = packbuf[j++];
+                if (k&1)   nsyn[other].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                if (k&2)   nsyn[other].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
+                if (k&4)   nsyn[other].avel = (signed char)packbuf[j++];
+                if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((long)packbuf[j++]));
+                if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((long)packbuf[j++])<<8);
+                if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((long)packbuf[j++])<<16);
+                if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((long)packbuf[j++])<<24);
+                if (k&128) nsyn[other].horz = (signed char)packbuf[j++];
+                movefifoend[other]++;
+
+                for(i=1;i<movesperpacket;i++)
+                {
+                    copybufbyte(&nsyn[other],&inputfifo[movefifoend[other]&(MOVEFIFOSIZ-1)][other],sizeof(input));
+                    movefifoend[other]++;
+                }
+
+                if (j > packbufleng)
+				{
+					printf("INVALID GAME PACKET!!! (%ld too many bytes) (j= %d, packbuflen= %d, type: %d)\n",j-packbufleng, j, packbufleng, packbuf[0]);
+				}
+
+                while (j != packbufleng)
+                {
+                    syncval[other][syncvalhead[other]&(MOVEFIFOSIZ-1)] = packbuf[j++];
+                    syncvalhead[other]++;
+                }
+
+                break;
+            case 127:
+                break;
+
+#ifdef CHECK_XDUKE_REV
+			case 131: // xDuke Rev ID
+				memcpy(ud.rev[other], packbuf, 10);
+				break;
+#endif
+
+			case 132: // get map CRC of opponents (to debug out of synch) 
+				ud.mapCRC[other] = (unsigned short)packbuf[1] + (unsigned short)(packbuf[2]<<8);
+				break;
+
+			case 133: // client refused to disable the autoaim by host
+
+				Error(EXIT_SUCCESS,	"One or more players refused to play with AutoAim OFF because this breaks\n"
+									"the official Duke's gameplay. Please restart without this option...\n");
+
+				break;
+
+			case 134: // Get GRP CRC32 + Con size + exeCRC + conCRC
+				memcpy(ud.groupefil_crc32[other], packbuf+1, sizeof(groupefil_crc32));
+				memcpy(ud.conSize+other, packbuf+1+sizeof(groupefil_crc32), sizeof(ud.conSize[0]));
+				memcpy(ud.conCRC+other, packbuf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0]), sizeof(ud.conCRC[0]));
+				memcpy(ud.exeCRC+other, packbuf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0]), sizeof(ud.exeCRC[0]));			
+				break;
+
+            case 250:
+                {
+					playerreadyflag[other]++;
+					printf("Player %d '%s' is ready...\n", other, ud.user_name[other]);
+				}
+                break;
+            case 255:
+                gameexitanycase();
+                break;
+        }
+    }
+}
+
+void faketimerhandler()
+{
+    long i, j, k, l;
+//    short who;
+    input *osyn, *nsyn;
+
+    if(qe == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_Delete))
+    {
+        qe = 1;
+        gameexit("Quick Exit.");
+    }
+
+    if ((totalclock < ototalclock+TICSPERFRAME) || (ready2send == 0)) 
+		return; // Returns here when playing a demo.
+    ototalclock += TICSPERFRAME;
+
+    getpackets(); if (getoutputcirclesize() >= 16) return;
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if (i != myconnectindex)
+            if (movefifoend[i] < movefifoend[myconnectindex]-200) return;
+
+     if( !CONSOLE_IsActive())
+     {
+        getinput(myconnectindex);
+     }
+
+     avgfvel += loc.fvel; // x
+     avgsvel += loc.svel; // y
+     avgavel += loc.avel;
+     avghorz += loc.horz;
+     avgbits |= loc.bits;
+     if (movefifoend[myconnectindex]&(movesperpacket-1))
+     {
+          copybufbyte(&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex],
+                          &inputfifo[movefifoend[myconnectindex]&(MOVEFIFOSIZ-1)][myconnectindex],sizeof(input));
+          movefifoend[myconnectindex]++;
+          return;
+     }
+     nsyn = &inputfifo[movefifoend[myconnectindex]&(MOVEFIFOSIZ-1)][myconnectindex];
+     nsyn[0].fvel = avgfvel/movesperpacket;
+     nsyn[0].svel = avgsvel/movesperpacket;
+     nsyn[0].avel = avgavel/movesperpacket;
+     nsyn[0].horz = avghorz/movesperpacket;
+     nsyn[0].bits = avgbits;
+     avgfvel = avgsvel = avgavel = avghorz = avgbits = 0;
+     movefifoend[myconnectindex]++;
+
+     if (numplayers < 2)
+     {
+          if (ud.multimode > 1) for(i=connecthead;i>=0;i=connectpoint2[i])
+              if(i != myconnectindex)
+              {
+                  //clearbufbyte(&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i],sizeof(input),0L);
+                  if(ud.playerai)
+                      computergetinput(i,&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i]);
+                  movefifoend[i]++;
+              }
+          return;
+     }
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if (i != myconnectindex)
+        {
+            k = (movefifoend[myconnectindex]-1)-movefifoend[i];
+            myminlag[i] = min(myminlag[i],k);
+            mymaxlag = max(mymaxlag,k);
+        }
+
+    if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
+    {
+        i = mymaxlag-bufferjitter; mymaxlag = 0;
+        if (i > 0) bufferjitter += ((3+i)>>2);
+        else if (i < 0) bufferjitter -= ((1-i)>>2);
+    }
+
+    if (networkmode == 1)
+    {
+        packbuf[0] = 17;
+        
+		if ((movefifoend[myconnectindex]-1) == 0) 
+		{
+			packbuf[0] = 16;
+		}
+
+        j = 1;
+
+            //Fix timers and buffer/jitter value
+        if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
+        {
+            if (myconnectindex != connecthead)
+            {
+                i = myminlag[connecthead]-otherminlag;
+                if (klabs(i) > 8)
+				{
+					i >>= 1;
+				}
+                else 
+				if (klabs(i) > 2) 
+				{
+					i = ksgn(i);
+				}
+                else 
+				{
+					i = 0;
+				}
+
+                totalclock -= TICSPERFRAME*i;
+                myminlag[connecthead] -= i; otherminlag += i;
+            }
+
+            if (myconnectindex == connecthead)
+                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+                    packbuf[j++] = min(max(myminlag[i],-128),127);
+
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                myminlag[i] = 0x7fffffff;
+        }
+
+        osyn = (input *)&inputfifo[(movefifoend[myconnectindex]-2)&(MOVEFIFOSIZ-1)][myconnectindex];
+        nsyn = (input *)&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex];
+
+        k = j;
+        packbuf[j++] = 0;
+
+        if (nsyn[0].fvel != osyn[0].fvel)
+        {
+            packbuf[j++] = (char)nsyn[0].fvel;
+            packbuf[j++] = (char)(nsyn[0].fvel>>8);
+            packbuf[k] |= 1;
+        }
+        if (nsyn[0].svel != osyn[0].svel)
+        {
+            packbuf[j++] = (char)nsyn[0].svel;
+            packbuf[j++] = (char)(nsyn[0].svel>>8);
+            packbuf[k] |= 2;
+        }
+        if (nsyn[0].avel != osyn[0].avel)
+        {
+            packbuf[j++] = (signed char)nsyn[0].avel;
+            packbuf[k] |= 4;
+        }
+        if ((nsyn[0].bits^osyn[0].bits)&0x000000ff) packbuf[j++] = (nsyn[0].bits&255), packbuf[k] |= 8;
+        if ((nsyn[0].bits^osyn[0].bits)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits>>8)&255), packbuf[k] |= 16;
+        if ((nsyn[0].bits^osyn[0].bits)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits>>16)&255), packbuf[k] |= 32;
+        if ((nsyn[0].bits^osyn[0].bits)&0xff000000) packbuf[j++] = ((nsyn[0].bits>>24)&255), packbuf[k] |= 64;
+        if (nsyn[0].horz != osyn[0].horz)
+        {
+            packbuf[j++] = (char)nsyn[0].horz;
+            packbuf[k] |= 128;
+        }
+
+        while (syncvalhead[myconnectindex] != syncvaltail)
+        {
+            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
+            syncvaltail++;
+        }
+
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (i != myconnectindex)
+                sendpacket(i,packbuf,j);
+
+        return;
+    }
+    if (myconnectindex != connecthead)   //Slave
+    {
+            //Fix timers and buffer/jitter value
+        if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
+        {
+            i = myminlag[connecthead]-otherminlag;
+            if (klabs(i) > 8) i >>= 1;
+            else if (klabs(i) > 2) i = ksgn(i);
+            else i = 0;
+
+            totalclock -= TICSPERFRAME*i;
+            myminlag[connecthead] -= i; otherminlag += i;
+
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                myminlag[i] = 0x7fffffff;
+        }
+
+        packbuf[0] = 1; packbuf[1] = 0; j = 2;
+
+        osyn = (input *)&inputfifo[(movefifoend[myconnectindex]-2)&(MOVEFIFOSIZ-1)][myconnectindex];
+        nsyn = (input *)&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex];
+
+        if (nsyn[0].fvel != osyn[0].fvel)
+        {
+            packbuf[j++] = (char)nsyn[0].fvel;
+            packbuf[j++] = (char)(nsyn[0].fvel>>8);
+            packbuf[1] |= 1;
+        }
+        if (nsyn[0].svel != osyn[0].svel)
+        {
+            packbuf[j++] = (char)nsyn[0].svel;
+            packbuf[j++] = (char)(nsyn[0].svel>>8);
+            packbuf[1] |= 2;
+        }
+        if (nsyn[0].avel != osyn[0].avel)
+        {
+            packbuf[j++] = (signed char)nsyn[0].avel;
+            packbuf[1] |= 4;
+        }
+        if ((nsyn[0].bits^osyn[0].bits)&0x000000ff) packbuf[j++] = (nsyn[0].bits&255), packbuf[1] |= 8;
+        if ((nsyn[0].bits^osyn[0].bits)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits>>8)&255), packbuf[1] |= 16;
+        if ((nsyn[0].bits^osyn[0].bits)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits>>16)&255), packbuf[1] |= 32;
+        if ((nsyn[0].bits^osyn[0].bits)&0xff000000) packbuf[j++] = ((nsyn[0].bits>>24)&255), packbuf[1] |= 64;
+        if (nsyn[0].horz != osyn[0].horz)
+        {
+            packbuf[j++] = (char)nsyn[0].horz;
+            packbuf[1] |= 128;
+        }
+
+        while (syncvalhead[myconnectindex] != syncvaltail)
+        {
+            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
+            syncvaltail++;
+        }
+
+        sendpacket(connecthead,packbuf,j);
+        return;
+    }
+
+        //This allows allow packet-resends
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if (movefifoend[i] <= movefifosendplc)
+        {
+            packbuf[0] = 127;
+            for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+               sendpacket(i,packbuf,1);
+            return;
+        }
+
+    while (1)  //Master
+    {
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (playerquitflag[i] && (movefifoend[i] <= movefifosendplc)) return;
+
+        osyn = (input *)&inputfifo[(movefifosendplc-1)&(MOVEFIFOSIZ-1)][0];
+        nsyn = (input *)&inputfifo[(movefifosendplc  )&(MOVEFIFOSIZ-1)][0];
+
+            //MASTER -> SLAVE packet
+        packbuf[0] = 0; j = 1;
+
+            //Fix timers and buffer/jitter value
+        if ((movefifosendplc&(TIMERUPDATESIZ-1)) == 0)
+        {
+            for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+               if (playerquitflag[i])
+                packbuf[j++] = min(max(myminlag[i],-128),127);
+
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                myminlag[i] = 0x7fffffff;
+        }
+
+        k = j;
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+           j += playerquitflag[i];
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+        {
+            if (playerquitflag[i] == 0) continue;
+
+            packbuf[k] = 0;
+            if (nsyn[i].fvel != osyn[i].fvel)
+            {
+                packbuf[j++] = (char)nsyn[i].fvel;
+                packbuf[j++] = (char)(nsyn[i].fvel>>8);
+                packbuf[k] |= 1;
+            }
+            if (nsyn[i].svel != osyn[i].svel)
+            {
+                packbuf[j++] = (char)nsyn[i].svel;
+                packbuf[j++] = (char)(nsyn[i].svel>>8);
+                packbuf[k] |= 2;
+            }
+            if (nsyn[i].avel != osyn[i].avel)
+            {
+                packbuf[j++] = (signed char)nsyn[i].avel;
+                packbuf[k] |= 4;
+            }
+            if ((nsyn[i].bits^osyn[i].bits)&0x000000ff) packbuf[j++] = (nsyn[i].bits&255), packbuf[k] |= 8;
+            if ((nsyn[i].bits^osyn[i].bits)&0x0000ff00) packbuf[j++] = ((nsyn[i].bits>>8)&255), packbuf[k] |= 16;
+            if ((nsyn[i].bits^osyn[i].bits)&0x00ff0000) packbuf[j++] = ((nsyn[i].bits>>16)&255), packbuf[k] |= 32;
+            if ((nsyn[i].bits^osyn[i].bits)&0xff000000) packbuf[j++] = ((nsyn[i].bits>>24)&255), packbuf[k] |= 64;
+            if (nsyn[i].horz != osyn[i].horz)
+            {
+                packbuf[j++] = (char)nsyn[i].horz;
+                packbuf[k] |= 128;
+            }
+            k++;
+        }
+
+        while (syncvalhead[myconnectindex] != syncvaltail)
+        {
+            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
+            syncvaltail++;
+        }
+
+        for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+            if (playerquitflag[i])
+            {
+                 sendpacket(i,packbuf,j);
+                 if (nsyn[i].bits&(1<<26))
+                    playerquitflag[i] = 0;
+            }
+
+        movefifosendplc += movesperpacket;
+    }
+}
+
+extern long cacnum;
+typedef struct { long *hand, leng; char *lock; } cactype;
+extern cactype cac[];
+
+void caches(void)
+{
+     short i,k;
+
+     k = 0;
+     for(i=0;i<cacnum;i++)
+          if ((*cac[i].lock) >= 200)
+          {
+                sprintf(tempbuf,"Locked- %ld: Leng:%ld, Lock:%ld",i,cac[i].leng,*cac[i].lock);
+                printext256(0L,k,31,-1,tempbuf,1); k += 6;
+          }
+
+     k += 6;
+
+     for(i=1;i<11;i++)
+          if (lumplockbyte[i] >= 200)
+          {
+                sprintf(tempbuf,"RTS Locked %ld:",i);
+                printext256(0L,k,31,-1,tempbuf,1); k += 6;
+          }
+
+
+}
+
+// FIX_00024: A key can be assigned to the new SHOW_INFO function. Display map CRC when
+//             in deathmatch. Usefull to identify who loaded a wrong map in multiplayer.
+void dispVersion(void)
+{	
+	int i;
+	int offx, offy, stepx, stepy;
+
+	offx = 21; offy = 30;
+	stepx = 73; stepy = 20;
+
+	// black translucent background underneath lists
+	rotatesprite(0<<16, 0<<16, 65536l<<5, 0, BLANK, 8, 0, 1+2+8+16+64,
+		scale(0,xdim,320),scale(26,ydim,200),
+		scale(320-0,xdim,320)-1,scale(200-((ud.multimode>4)?(161-1*7)-stepy:(161-1*7)),ydim,200)-1);
+
+	// FIX_00009: Show map CRC and GRP file version of each player in case of Out Of Synch
+	for(i=connecthead;i>=0;i=connectpoint2[i])
+	{	
+		// Disp name
+		sprintf(tempbuf,"%s", ud.user_name[i]);
+		minitext(offx+(stepx*(i&3)),offy+0+((i&4)>>2)*stepy, tempbuf, sprite[ps[i].i].pal, 2+8+16);
+
+		// Disp MAP CRC
+		if(ps[i].fakeplayer)
+			sprintf(tempbuf,"MAP CRC: (bot)");
+		else
+			sprintf(tempbuf,"MAP CRC: %X", ud.mapCRC[i]);
+		minitext(offx+(stepx*(i&3)),offy+7+((i&4)>>2)*stepy, tempbuf, COLOR_ON,2+8+16);
+
+	}
+}
+
+void checksync(void)
+{
+      long i, k;
+
+      for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (syncvalhead[i] == syncvaltottail) break;
+      if (i < 0)
+      {
+             syncstat = 0;
+             do
+             {
+                     for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
+					 {
+						 if (syncval[i][syncvaltottail&(MOVEFIFOSIZ-1)] != syncval[connecthead][syncvaltottail&(MOVEFIFOSIZ-1)])
+						 {
+                                 syncstat = 1;
+						}
+					 }
+
+                     syncvaltottail++;
+                     for(i=connecthead;i>=0;i=connectpoint2[i])
+                     {
+						 if (syncvalhead[i] == syncvaltottail) 
+						 {
+							 break;
+						 }
+					 }
+             } while (i < 0);
+      }
+
+      if (connectpoint2[connecthead] < 0) 
+	  {
+		  syncstat = 0;
+	  }
+
+      if (syncstat)
+      {
+			minitext(21,30+35+30, "Out Of Sync - Please restart game", COLOR_ON,2+8+16);
+			// FIX_00090: Removed info key. FPS were shown after CRC msg. CRC not always removed. (Turrican)
+			for(i=connecthead;i>=0;i=connectpoint2[i])
+			{	
+				if (ud.mapCRC[connecthead]!=ud.mapCRC[i])
+				{
+					minitext(21,30+42+30, "Map CRC mismatching. Please use exactly the same map.", COLOR_ON,2+8+16);
+					dispVersion();
+				}
+				else
+				minitext(21,30+42+30, "Verify the con files. Close your P2P if any", COLOR_ON,2+8+16);
+
+			}
+	  }
+
+      if (syncstate)
+      {
+          //printext256(4L,160L,31,0,"Missed Network packet!",0);
+          //printext256(4L,138L,31,0,"RUN DN3DHELP.EXE for information.",0);
+          minitext(21,30+35+30, "Missed Network packet!", COLOR_ON,2+8+16);
+      }
+}
+
+
+void check_fta_sounds(short i)
+{
+    if(sprite[i].extra > 0) switch(PN)
+    {
+        case LIZTROOPONTOILET:
+        case LIZTROOPJUSTSIT:
+        case LIZTROOPSHOOT:
+        case LIZTROOPJETPACK:
+        case LIZTROOPDUCKING:
+        case LIZTROOPRUNNING:
+        case LIZTROOP:
+            spritesound(PRED_RECOG,i);
+            break;
+        case LIZMAN:
+        case LIZMANSPITTING:
+        case LIZMANFEEDING:
+        case LIZMANJUMP:
+            spritesound(CAPT_RECOG,i);
+            break;
+        case PIGCOP:
+        case PIGCOPDIVE:
+            spritesound(PIG_RECOG,i);
+            break;
+        case RECON:
+            spritesound(RECO_RECOG,i);
+            break;
+        case DRONE:
+            spritesound(DRON_RECOG,i);
+            break;
+        case COMMANDER:
+        case COMMANDERSTAYPUT:
+            spritesound(COMM_RECOG,i);
+            break;
+        case ORGANTIC:
+            spritesound(TURR_RECOG,i);
+            break;
+        case OCTABRAIN:
+        case OCTABRAINSTAYPUT:
+            spritesound(OCTA_RECOG,i);
+            break;
+        case BOSS1:
+            sound(BOS1_RECOG);
+            break;
+        case BOSS2:
+            if(sprite[i].pal == 1)
+                sound(BOS2_RECOG);
+            else sound(WHIPYOURASS);
+            break;
+        case BOSS3:
+            if(sprite[i].pal == 1)
+                sound(BOS3_RECOG);
+            else sound(RIPHEADNECK);
+            break;
+        case BOSS4:
+        case BOSS4STAYPUT:
+            if(sprite[i].pal == 1)
+                sound(BOS4_RECOG);
+            sound(BOSS4_FIRSTSEE);
+            break;
+        case GREENSLIME:
+            spritesound(SLIM_RECOG,i);
+            break;
+    }
+}
+
+short inventory(spritetype *s)
+{
+    switch(s->picnum)
+    {
+        case FIRSTAID:
+        case STEROIDS:
+        case HEATSENSOR:
+        case BOOTS:
+        case JETPACK:
+        case HOLODUKE:
+        case AIRTANK:
+            return 1;
+    }
+    return 0;
+}
+
+
+short badguy(spritetype *s)
+{
+
+    switch(s->picnum)
+    {
+            case SHARK:
+            case RECON:
+            case DRONE:
+            case LIZTROOPONTOILET:
+            case LIZTROOPJUSTSIT:
+            case LIZTROOPSTAYPUT:
+            case LIZTROOPSHOOT:
+            case LIZTROOPJETPACK:
+            case LIZTROOPDUCKING:
+            case LIZTROOPRUNNING:
+            case LIZTROOP:
+            case OCTABRAIN:
+            case COMMANDER:
+            case COMMANDERSTAYPUT:
+            case PIGCOP:
+            case EGG:
+            case PIGCOPSTAYPUT:
+            case PIGCOPDIVE:
+            case LIZMAN:
+            case LIZMANSPITTING:
+            case LIZMANFEEDING:
+            case LIZMANJUMP:
+            case ORGANTIC:
+            case BOSS1:
+            case BOSS2:
+            case BOSS3:
+            case BOSS4:
+            case GREENSLIME:
+            case GREENSLIME+1:
+            case GREENSLIME+2:
+            case GREENSLIME+3:
+            case GREENSLIME+4:
+            case GREENSLIME+5:
+            case GREENSLIME+6:
+            case GREENSLIME+7:
+            case RAT:
+            case ROTATEGUN:
+                return 1;
+    }
+    if( actortype[s->picnum] ) return 1;
+
+    return 0;
+}
+
+
+short badguypic(short pn)
+{
+
+    switch(pn)
+    {
+            case SHARK:
+            case RECON:
+            case DRONE:
+            case LIZTROOPONTOILET:
+            case LIZTROOPJUSTSIT:
+            case LIZTROOPSTAYPUT:
+            case LIZTROOPSHOOT:
+            case LIZTROOPJETPACK:
+            case LIZTROOPDUCKING:
+            case LIZTROOPRUNNING:
+            case LIZTROOP:
+            case OCTABRAIN:
+            case COMMANDER:
+            case COMMANDERSTAYPUT:
+            case PIGCOP:
+            case EGG:
+            case PIGCOPSTAYPUT:
+            case PIGCOPDIVE:
+            case LIZMAN:
+            case LIZMANSPITTING:
+            case LIZMANFEEDING:
+            case LIZMANJUMP:
+            case ORGANTIC:
+            case BOSS1:
+            case BOSS2:
+            case BOSS3:
+            case BOSS4:
+            case GREENSLIME:
+            case GREENSLIME+1:
+            case GREENSLIME+2:
+            case GREENSLIME+3:
+            case GREENSLIME+4:
+            case GREENSLIME+5:
+            case GREENSLIME+6:
+            case GREENSLIME+7:
+            case RAT:
+            case ROTATEGUN:
+                return 1;
+    }
+
+    if( actortype[pn] ) return 1;
+
+    return 0;
+}
+
+
+
+void myos(long x, long y, short tilenum, signed char shade, char orientation)
+{
+    char p;
+    short a;
+
+    if(orientation&4)
+        a = 1024;
+    else a = 0;
+
+    p = sector[ps[screenpeek].cursectnum].floorpal;
+    rotatesprite(x<<16,y<<16,65536L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
+}
+
+void myospal(long x, long y, short tilenum, signed char shade, char orientation, char p)
+{
+    char fp;
+    short a;
+
+    if(orientation&4)
+        a = 1024;
+    else a = 0;
+
+    fp = sector[ps[screenpeek].cursectnum].floorpal;
+
+    rotatesprite(x<<16,y<<16,65536L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
+
+}
+
+void invennum(long x,long y,char num1,char ha,char sbits)
+{
+    char dabuf[80] = {0};
+    sprintf(dabuf,"%ld",num1);
+    if(num1 > 99)
+    {
+        rotatesprite((x-4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[2]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+    }
+    else if(num1 > 9)
+    {
+        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+    }
+    else
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
+}
+
+void orderweaponnum(short ind,long x,long y,long num1, long num2,char ha)
+{
+    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
+    rotatesprite((x-3)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);
+
+    minitextshade(x+1,y-4,"ORDER",26,6,2+8+16+128);
+}
+
+void weaponnum(short ind,long x,long y,long num1, long num2,char ha)
+{
+    char dabuf[80] = {0};
+
+    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
+    rotatesprite((x-3)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);
+    rotatesprite((x+9)<<16,y<<16,65536L,0,THREEBYFIVE+11,ha,0,10+128,0,0,xdim-1,ydim-1);
+
+    if(num1 > 99) num1 = 99;
+    if(num2 > 99) num2 = 99;
+
+    sprintf(dabuf,"%ld",num1);
+    if(num1 > 9)
+    {
+        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+
+    sprintf(dabuf,"%ld",num2);
+    if(num2 > 9)
+    {
+        rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+}
+
+void weaponnum999(char ind,long x,long y,long num1, long num2,char ha)
+{
+    char dabuf[80] = {0};
+
+    rotatesprite((x-7)<<16,y<<16,65536L,0,THREEBYFIVE+ind+1,ha-10,7,10+128,0,0,xdim-1,ydim-1);
+    rotatesprite((x-4)<<16,y<<16,65536L,0,THREEBYFIVE+10,ha,0,10+128,0,0,xdim-1,ydim-1);
+    rotatesprite((x+13)<<16,y<<16,65536L,0,THREEBYFIVE+11,ha,0,10+128,0,0,xdim-1,ydim-1);
+
+    sprintf(dabuf,"%ld",num1);
+    if(num1 > 99)
+    {
+        rotatesprite((x)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else if(num1 > 9)
+    {
+        rotatesprite((x+4)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else rotatesprite((x+8)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+
+    sprintf(dabuf,"%ld",num2);
+    if(num2 > 99)
+    {
+        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+21)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+25)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else if(num2 > 9)
+    {
+        rotatesprite((x+17)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+        rotatesprite((x+21)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+    }
+    else rotatesprite((x+25)<<16,y<<16,65536L,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10+128,0,0,xdim-1,ydim-1);
+}
+
+
+    //REPLACE FULLY
+void weapon_amounts(struct player_struct *p,long x,long y,long u)
+{
+     int cw;
+
+     cw = p->curr_weapon;
+
+     if (u&4)
+     {
+         if (u != 0xffffffff) patchstatusbar(96,178,96+12,178+6);
+         weaponnum999(PISTOL_WEAPON,x,y,
+                     p->ammo_amount[PISTOL_WEAPON],max_ammo_amount[PISTOL_WEAPON],
+                     12-20*(cw == PISTOL_WEAPON) );
+     }
+     if (u&8)
+     {
+         if (u != 0xffffffff) patchstatusbar(96,184,96+12,184+6);
+         weaponnum999(SHOTGUN_WEAPON,x,y+6,
+                     p->ammo_amount[SHOTGUN_WEAPON],max_ammo_amount[SHOTGUN_WEAPON],
+                     (!p->gotweapon[SHOTGUN_WEAPON]*9)+12-18*
+                     (cw == SHOTGUN_WEAPON) );
+     }
+     if (u&16)
+     {
+         if (u != 0xffffffff) patchstatusbar(96,190,96+12,190+6);
+         weaponnum999(CHAINGUN_WEAPON,x,y+12,
+                      p->ammo_amount[CHAINGUN_WEAPON],max_ammo_amount[CHAINGUN_WEAPON],
+                      (!p->gotweapon[CHAINGUN_WEAPON]*9)+12-18*
+                      (cw == CHAINGUN_WEAPON) );
+     }
+     if (u&32)
+     {
+         if (u != 0xffffffff) patchstatusbar(135,178,135+8,178+6);
+         weaponnum(RPG_WEAPON,x+39,y,
+                  p->ammo_amount[RPG_WEAPON],max_ammo_amount[RPG_WEAPON],
+                  (!p->gotweapon[RPG_WEAPON]*9)+12-19*
+                  (cw == RPG_WEAPON) );
+     }
+     if (u&64)
+     {
+         if (u != 0xffffffff) patchstatusbar(135,184,135+8,184+6);
+         weaponnum(HANDBOMB_WEAPON,x+39,y+6,
+                     p->ammo_amount[HANDBOMB_WEAPON],max_ammo_amount[HANDBOMB_WEAPON],
+                     (((!p->ammo_amount[HANDBOMB_WEAPON])|(!p->gotweapon[HANDBOMB_WEAPON]))*9)+12-19*
+                     ((cw == HANDBOMB_WEAPON) || (cw == HANDREMOTE_WEAPON)));
+     }
+     if (u&128)
+     {
+         if (u != 0xffffffff) patchstatusbar(135,190,135+8,190+6);
+
+		if(VOLUMEONE)
+		{
+			orderweaponnum(SHRINKER_WEAPON,x+39,y+12,
+	                     p->ammo_amount[SHRINKER_WEAPON],max_ammo_amount[SHRINKER_WEAPON],
+	                     (!p->gotweapon[SHRINKER_WEAPON]*9)+12-18*
+	                     (cw == SHRINKER_WEAPON) );
+		}
+		else
+		{
+			if(p->subweapon&(1<<GROW_WEAPON))
+	             weaponnum(SHRINKER_WEAPON,x+39,y+12,
+	                 p->ammo_amount[GROW_WEAPON],max_ammo_amount[GROW_WEAPON],
+	                 (!p->gotweapon[GROW_WEAPON]*9)+12-18*
+	                 (cw == GROW_WEAPON) );
+	         else
+	             weaponnum(SHRINKER_WEAPON,x+39,y+12,
+	                 p->ammo_amount[SHRINKER_WEAPON],max_ammo_amount[SHRINKER_WEAPON],
+	                 (!p->gotweapon[SHRINKER_WEAPON]*9)+12-18*
+	                 (cw == SHRINKER_WEAPON) );
+		}
+     }
+     if (u&256)
+     {
+		if (u != 0xffffffff) patchstatusbar(166,178,166+8,178+6);
+
+		if(VOLUMEONE)
+		{
+	        orderweaponnum(DEVISTATOR_WEAPON,x+70,y,
+                     p->ammo_amount[DEVISTATOR_WEAPON],max_ammo_amount[DEVISTATOR_WEAPON],
+                     (!p->gotweapon[DEVISTATOR_WEAPON]*9)+12-18*
+                     (cw == DEVISTATOR_WEAPON) );
+		}
+		else
+		{
+	         weaponnum(DEVISTATOR_WEAPON,x+70,y,
+	                     p->ammo_amount[DEVISTATOR_WEAPON],max_ammo_amount[DEVISTATOR_WEAPON],
+	                     (!p->gotweapon[DEVISTATOR_WEAPON]*9)+12-18*
+	                     (cw == DEVISTATOR_WEAPON) );
+		}
+     }
+     if (u&512)
+     {
+		if (u != 0xffffffff) patchstatusbar(166,184,166+8,184+6);
+		if(VOLUMEONE)
+		{
+	         orderweaponnum(TRIPBOMB_WEAPON,x+70,y+6,
+	                 p->ammo_amount[TRIPBOMB_WEAPON],max_ammo_amount[TRIPBOMB_WEAPON],
+	                 (!p->gotweapon[TRIPBOMB_WEAPON]*9)+12-18*
+	                 (cw == TRIPBOMB_WEAPON) );
+		}
+		else
+		{
+			weaponnum(TRIPBOMB_WEAPON,x+70,y+6,
+		                     p->ammo_amount[TRIPBOMB_WEAPON],max_ammo_amount[TRIPBOMB_WEAPON],
+		                     (!p->gotweapon[TRIPBOMB_WEAPON]*9)+12-18*
+		                     (cw == TRIPBOMB_WEAPON) );
+		}
+     }
+
+     if (u&65536L)
+     {
+         if (u != 0xffffffff) patchstatusbar(166,190,166+8,190+6);
+		if(VOLUMEONE)
+		{
+			orderweaponnum(-1,x+70,y+12,
+	                     p->ammo_amount[FREEZE_WEAPON],max_ammo_amount[FREEZE_WEAPON],
+	                     (!p->gotweapon[FREEZE_WEAPON]*9)+12-18*
+	                     (cw == FREEZE_WEAPON) );
+		}
+		else
+		{
+	         weaponnum(-1,x+70,y+12,
+	                     p->ammo_amount[FREEZE_WEAPON],max_ammo_amount[FREEZE_WEAPON],
+	                     (!p->gotweapon[FREEZE_WEAPON]*9)+12-18*
+	                     (cw == FREEZE_WEAPON) );
+		}
+     }
+}
+
+void digitalnumber(long x,long y,long n,char s,char cs)
+{
+    short i, j, k, p, c;
+    char b[10];
+
+    ltoa(n,b,10);
+    i = strlen(b);
+    j = 0;
+
+    for(k=0;k<i;k++)
+    {
+        p = DIGITALNUM+*(b+k)-'0';
+        j += tilesizx[p]+1;
+    }
+    c = x-(j>>1);
+
+    j = 0;
+    for(k=0;k<i;k++)
+    {
+        p = DIGITALNUM+*(b+k)-'0';
+        rotatesprite((c+j)<<16,y<<16,65536L,0,p,s,0,cs,0,0,xdim-1,ydim-1);
+        j += tilesizx[p]+1;
+    }
+}
+
+/*
+
+void scratchmarks(long x,long y,long n,char s,char p)
+{
+    long i, ni;
+
+    ni = n/5;
+    for(i=ni;i >= 0;i--)
+    {
+        overwritesprite(x-2,y,SCRATCH+4,s,0,0);
+        x += tilesizx[SCRATCH+4]-1;
+    }
+
+    ni = n%5;
+    if(ni) overwritesprite(x,y,SCRATCH+ni-1,s,p,0);
+}
+  */
+void displayinventory(struct player_struct *p)
+{
+    short n, j, xoff, y;
+
+    j = xoff = 0;
+
+    n = (p->jetpack_amount > 0)<<3; if(n&8) j++;
+    n |= ( p->scuba_amount > 0 )<<5; if(n&32) j++;
+    n |= (p->steroids_amount > 0)<<1; if(n&2) j++;
+    n |= ( p->holoduke_amount > 0)<<2; if(n&4) j++;
+    n |= (p->firstaid_amount > 0); if(n&1) j++;
+    n |= (p->heat_amount > 0)<<4; if(n&16) j++;
+    n |= (p->boot_amount > 0)<<6; if(n&64) j++;
+
+    xoff = 160-(j*11);
+
+    j = 0;
+
+    if(ud.screen_size > 4)
+        y = 154;
+    else y = 172;
+
+    if(ud.screen_size == 4)
+    {
+        if(ud.multimode > 1)
+            xoff += 56;
+        else xoff += 65;
+    }
+
+    while( j <= 9 )
+    {
+        if( n&(1<<j) )
+        {
+            switch( n&(1<<j) )
+            {
+                case   1:
+                rotatesprite(xoff<<16,y<<16,65536L,0,FIRSTAID_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case   2:
+                rotatesprite((xoff+1)<<16,y<<16,65536L,0,STEROIDS_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case   4:
+                rotatesprite((xoff+2)<<16,y<<16,65536L,0,HOLODUKE_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case   8:
+                rotatesprite(xoff<<16,y<<16,65536L,0,JETPACK_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case  16:
+                rotatesprite(xoff<<16,y<<16,65536L,0,HEAT_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case  32:
+                rotatesprite(xoff<<16,y<<16,65536L,0,AIRTANK_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+                case 64:
+                rotatesprite(xoff<<16,(y-1)<<16,65536L,0,BOOT_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
+            }
+
+            xoff += 22;
+
+            if(p->inven_icon == j+1)
+                rotatesprite((xoff-2)<<16,(y+19)<<16,65536L,1024,ARROW,-32,0,2+16,windowx1,windowy1,windowx2,windowy2);
+        }
+
+        j++;
+    }
+}
+
+
+
+void displayfragbar(void)
+{
+    short i, j;
+
+    j = 0;
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if(i > j) j = i;
+
+    rotatesprite(0,0,65600L,0,FRAGBAR,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+    if(j >= 4) rotatesprite(319,(8)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,0,0,xdim-1,ydim-1);
+    if(j >= 8) rotatesprite(319,(16)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,0,0,xdim-1,ydim-1);
+    if(j >= 12) rotatesprite(319,(24)<<16,65600L,0,FRAGBAR,0,0,10+16+64+128,0,0,xdim-1,ydim-1);
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+    {
+        minitext(21+(73*(i&3)),2+((i&28)<<1),&ud.user_name[i][0],sprite[ps[i].i].pal,2+8+16+128);
+        sprintf(tempbuf,"%d",ps[i].frag-ps[i].fraggedself);
+        minitext(17+50+(73*(i&3)),2+((i&28)<<1),tempbuf,sprite[ps[i].i].pal,2+8+16+128);
+    }
+}
+
+void display_boardfilename_FPS_weapon(short *offx, short *offy, short *stepx, short *stepy)
+{
+
+	short i;
+
+	// FIX_00025: Can toggle FPS and map name during a game (use dnrate OR toggle
+	//            from menu when in deathmatch). 
+
+	// Display boardfilename and FPS
+	if(ud.tickrate&1)
+	{
+		tics(*offx, *offy, COLOR_ON);
+		*offy += *stepy;
+	}
+	if(ud.tickrate&2)
+		dispVersion();
+
+	// We display the weapons here instead of changing the function
+	// displayweapon() because the display will be much faster
+	for(i=connecthead;i>=0;i=connectpoint2[i])
+	{	
+		if (ud.hideweapon && i==screenpeek)
+			drawsmallweapon(ps[i].curr_weapon, 1, 130, (ud.screen_size<=4)?170:140);
+	}
+}
+
+
+// FIX_00026: Weapon can now be hidden (on your screen only).
+void drawsmallweapon(short weapon, float scale, short x, short y)
+{
+	float t = 60000;
+	int s;
+	float offsetx, offsety;
+
+	switch(weapon)
+	{	
+		case  KNEE_WEAPON			: s=0;					break;
+		case  PISTOL_WEAPON			: s=FIRSTGUNSPRITE;
+										offsetx = 8;
+										offsety = 7;
+										break;
+		case  SHOTGUN_WEAPON		: s=SHOTGUNSPRITE;
+										t = 45000;
+										offsetx = -1;
+										offsety = 9;
+										break;
+		case  CHAINGUN_WEAPON		: s=CHAINGUNSPRITE;  	
+										t = 45000;
+										offsetx = -1;
+										offsety = 9;
+										break;
+		case  RPG_WEAPON			: s=RPGSPRITE;
+										t = 45000;
+										offsetx = 4;
+										offsety = 9;
+										break;
+		case  HANDBOMB_WEAPON		: s=HEAVYHBOMB;	
+										t=20000;
+										offsetx = 16;
+										offsety = 13;
+										break;
+		case  SHRINKER_WEAPON		: s=SHRINKERSPRITE;		
+										t = 30000;
+										offsetx = 6;
+										offsety = 14;
+										break;
+		case  DEVISTATOR_WEAPON		: s=DEVISTATORSPRITE;
+										t = 45000;
+										offsetx = 3;
+										offsety = 9;
+										break;
+		case  TRIPBOMB_WEAPON		: s=TRIPBOMBSPRITE;		
+										t = 75000;			
+										offsetx = 10;
+										offsety = 12;
+										break;
+		case  FREEZE_WEAPON			:	s=FREEZESPRITE;	
+										t = 45000;
+										offsetx = 1;
+										offsety = 6;
+										break;
+		case  HANDREMOTE_WEAPON		: s=0;					
+										break;
+		case  GROW_WEAPON			: s=GROWSPRITEICON;		
+										t = 30000;
+										offsetx = 6;
+										offsety = 4;
+										break;
+		default						: s=0;
+	}
+
+	if(s)
+		rotatesprite((x+(short)(offsetx*scale))<<16,(y+(short)(offsety*scale))<<16,(int)(t*scale),0,s,0,0,2+8+16,0,0,xdim-1,ydim-1);
+
+	return;
+}
+
+void coolgaugetext(short snum)
+{
+    struct player_struct *p;
+    long i, j, o, ss, u;
+    char c, permbit;
+	short offx = 3, offy = 3, stepx=60, stepy=6;
+
+    p = &ps[snum];
+
+    if (p->invdisptime > 0) 
+    {
+        displayinventory(p);
+    }
+
+
+    if(ps[snum].gm&MODE_MENU)
+        if( (current_menu >= 400  && current_menu <= 405) )
+            return;
+
+	offy += countfragbars(); //add fragbars
+	display_boardfilename_FPS_weapon(&offx, &offy, &stepx, &stepy);
+
+
+    ss = ud.screen_size; if (ss < 4) return;
+
+    // Draw the multi player frag status bar
+    if ( ud.multimode > 1 && ud.coop != 1 )
+    {
+        if (pus)
+            { 
+                displayfragbar(); 
+            }
+        else
+        {
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+            {
+                if (ps[i].frag != sbar.frag[i]) 
+                { 
+                    displayfragbar(); 
+                    break; 
+                }
+            }
+        }
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (i != myconnectindex)
+                sbar.frag[i] = ps[i].frag;
+    }
+
+    if (ss == 4)   //DRAW MINI STATUS BAR:
+    {
+    	// FIX_00027: Added an extra small statusbar (HUD)
+		if(ud.extended_screen_size>0)
+		{
+			offx = 5; offy = 160;
+
+			sprintf(tempbuf,"%d", ps[screenpeek].ammo_amount[ps[screenpeek].curr_weapon]);
+			minitext(offx+26,offy+21,tempbuf,COLOR_ON,2+8+16); //minitext: 2 red light, 23 yellow
+			sprintf(tempbuf,"%d", ps[screenpeek].last_extra); 
+			gametext(offx,offy+20,tempbuf,ps[screenpeek].last_extra<=50?15:0,2+8+16); //minitext: 2 red light, 23 yellow
+			rotatesprite((offx+0*10)<<16,(offy+28)<<16,20000,0,SHIELD,ps[screenpeek].shield_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+0*10)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].shield_amount)*20000/10,0,SHIELD,0,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+1*10)<<16,(offy+28)<<16,35000,0,JETPACK_ICON,ps[screenpeek].jetpack_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+1*10)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].jetpack_amount)*35000/40,0,JETPACK_ICON,0,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+2*10-1)<<16,(offy+28)<<16,35000,0,STEROIDS_ICON,ps[screenpeek].steroids_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+2*10-1)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].steroids_amount)*35000/20,0,STEROIDS_ICON,5,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+3*10-3)<<16,(offy+28)<<16,40000,0,FIRSTAID_ICON,ps[screenpeek].firstaid_amount?25:100,0,2+8+16,0,0,xdim-1,ydim-1);
+			rotatesprite((offx+3*10-3)<<16,(offy+28)<<16,ksqrt(ps[screenpeek].firstaid_amount)*40000/10,0,FIRSTAID_ICON,0,0,2+8+16,0,0,xdim-1,ydim-1);
+		}
+		else
+		{
+			if (p->inven_icon)
+				rotatesprite(69<<16,(200-30)<<16,65536L,0,INVENTORYBOX,0,21,10+16,0,0,xdim-1,ydim-1);
+			rotatesprite(5<<16,(200-28)<<16,65536L,0,HEALTHBOX,0,21,10+16,0,0,xdim-1,ydim-1);
+
+			if(sprite[p->i].pal == 1 && p->last_extra < 2)
+				digitalnumber(20,200-17,1,-16,10+16);
+			else digitalnumber(20,200-17,p->last_extra,-16,10+16);
+
+			rotatesprite(37<<16,(200-28)<<16,65536L,0,AMMOBOX,0,21,10+16,0,0,xdim-1,ydim-1);
+
+			if (p->curr_weapon == HANDREMOTE_WEAPON) i = HANDBOMB_WEAPON; else i = p->curr_weapon;
+			digitalnumber(53,200-17,p->ammo_amount[i],-16,10+16);
+
+			o = 158; permbit = 0;
+			if (p->inven_icon)
+			{
+				switch(p->inven_icon)
+				{
+					case 1: i = FIRSTAID_ICON; break;
+					case 2: i = STEROIDS_ICON; break;
+					case 3: i = HOLODUKE_ICON; break;
+					case 4: i = JETPACK_ICON; break;
+					case 5: i = HEAT_ICON; break;
+					case 6: i = AIRTANK_ICON; break;
+					case 7: i = BOOT_ICON; break;
+					default: i = -1;
+				}
+				if (i >= 0) rotatesprite((231-o)<<16,(200-21)<<16,65536L,0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);
+
+				minitext(292-30-o,190,"%",6,10+16+permbit);
+
+				j = 0x80000000;
+				switch(p->inven_icon)
+				{
+					case 1: i = p->firstaid_amount; break;
+					case 2: i = ((p->steroids_amount+3)>>2); break;
+					case 3: i = ((p->holoduke_amount+15)/24); j = p->holoduke_on; break;
+					case 4: i = ((p->jetpack_amount+15)>>4); j = p->jetpack_on; break;
+					case 5: i = p->heat_amount/12; j = p->heat_on; break;
+					case 6: i = ((p->scuba_amount+63)>>6); break;
+					case 7: i = (p->boot_amount>>1); break;
+				}
+				invennum(284-30-o,200-6,(char)i,0,10+permbit);
+				if (j > 0) minitext(288-30-o,180,"ON",0,10+16+permbit);
+				else if (j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit);
+				if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit);
+			}
+		}
+        return;
+    }
+
+        //DRAW/UPDATE FULL STATUS BAR:
+
+    if (pus) { pus = 0; u = 0xffffffff; } else u = 0;
+
+    if (sbar.frag[myconnectindex] != p->frag) { sbar.frag[myconnectindex] = p->frag; u |= 32768; }
+    if (sbar.got_access != p->got_access) { sbar.got_access = p->got_access; u |= 16384; }
+    if (sbar.last_extra != p->last_extra) { sbar.last_extra = p->last_extra; u |= 1; }
+    if (sbar.shield_amount != p->shield_amount) { sbar.shield_amount = p->shield_amount; u |= 2; }
+    if (sbar.curr_weapon != p->curr_weapon) { sbar.curr_weapon = p->curr_weapon; u |= (4+8+16+32+64+128+256+512+1024+65536L); }
+    for(i=1;i < 10;i++)
+    {
+        if (sbar.ammo_amount[i] != p->ammo_amount[i]) {
+        sbar.ammo_amount[i] = p->ammo_amount[i]; if(i < 9) u |= ((2<<i)+1024); else u |= 65536L+1024; }
+        if (sbar.gotweapon[i] != p->gotweapon[i]) { sbar.gotweapon[i] =
+        p->gotweapon[i]; if(i < 9 ) u |= ((2<<i)+1024); else u |= 65536L+1024; }
+    }
+    if (sbar.inven_icon != p->inven_icon) { sbar.inven_icon = p->inven_icon; u |= (2048+4096+8192); }
+    if (sbar.holoduke_on != p->holoduke_on) { sbar.holoduke_on = p->holoduke_on; u |= (4096+8192); }
+    if (sbar.jetpack_on != p->jetpack_on) { sbar.jetpack_on = p->jetpack_on; u |= (4096+8192); }
+    if (sbar.heat_on != p->heat_on) { sbar.heat_on = p->heat_on; u |= (4096+8192); }
+    if (sbar.firstaid_amount != p->firstaid_amount) { sbar.firstaid_amount = p->firstaid_amount; u |= 8192; }
+    if (sbar.steroids_amount != p->steroids_amount) { sbar.steroids_amount = p->steroids_amount; u |= 8192; }
+    if (sbar.holoduke_amount != p->holoduke_amount) { sbar.holoduke_amount = p->holoduke_amount; u |= 8192; }
+    if (sbar.jetpack_amount != p->jetpack_amount) { sbar.jetpack_amount = p->jetpack_amount; u |= 8192; }
+    if (sbar.heat_amount != p->heat_amount) { sbar.heat_amount = p->heat_amount; u |= 8192; }
+    if (sbar.scuba_amount != p->scuba_amount) { sbar.scuba_amount = p->scuba_amount; u |= 8192; }
+    if (sbar.boot_amount != p->boot_amount) { sbar.boot_amount = p->boot_amount; u |= 8192; }
+    if (u == 0) return;
+
+    //0 - update health
+    //1 - update armor
+    //2 - update PISTOL_WEAPON ammo
+    //3 - update SHOTGUN_WEAPON ammo
+    //4 - update CHAINGUN_WEAPON ammo
+    //5 - update RPG_WEAPON ammo
+    //6 - update HANDBOMB_WEAPON ammo
+    //7 - update SHRINKER_WEAPON ammo
+    //8 - update DEVISTATOR_WEAPON ammo
+    //9 - update TRIPBOMB_WEAPON ammo
+    //10 - update ammo display
+    //11 - update inventory icon
+    //12 - update inventory on/off
+    //13 - update inventory %
+    //14 - update keys
+    //15 - update kills
+    //16 - update FREEZE_WEAPON ammo
+
+    if (u == 0xffffffff)
+    {
+        patchstatusbar(0,0,320,200);
+        if (ud.multimode > 1 && ud.coop != 1)
+            rotatesprite(277<<16,(200-27)<<16,65536L,0,KILLSICON,0,0,10+16+128,0,0,xdim-1,ydim-1);
+    }
+    if (ud.multimode > 1 && ud.coop != 1)
+    {
+        if (u&32768)
+        {
+            if (u != 0xffffffff) patchstatusbar(276,183,299,193);
+            digitalnumber(287,200-17,max(p->frag-p->fraggedself,0),-16,10+16+128);
+        }
+    }
+    else
+    {
+        if (u&16384)
+        {
+            if (u != 0xffffffff) patchstatusbar(275,182,299,194);
+            if (p->got_access&4) rotatesprite(275<<16,182<<16,65536L,0,ACCESS_ICON,0,23,10+16+128,0,0,xdim-1,ydim-1);
+            if (p->got_access&2) rotatesprite(288<<16,182<<16,65536L,0,ACCESS_ICON,0,21,10+16+128,0,0,xdim-1,ydim-1);
+            if (p->got_access&1) rotatesprite(281<<16,189<<16,65536L,0,ACCESS_ICON,0,0,10+16+128,0,0,xdim-1,ydim-1);
+        }
+    }
+    if (u&(4+8+16+32+64+128+256+512+65536L)) weapon_amounts(p,96,182,u);
+
+    if (u&1)
+    {
+        if (u != 0xffffffff) patchstatusbar(20,183,43,193);
+        if(sprite[p->i].pal == 1 && p->last_extra < 2)
+            digitalnumber(32,200-17,1,-16,10+16+128);
+        else digitalnumber(32,200-17,p->last_extra,-16,10+16+128);
+    }
+    if (u&2)
+    {
+        if (u != 0xffffffff) patchstatusbar(52,183,75,193);
+        digitalnumber(64,200-17,p->shield_amount,-16,10+16+128);
+    }
+
+    if (u&1024)
+    {
+        if (u != 0xffffffff) patchstatusbar(196,183,219,193);
+        if (p->curr_weapon != KNEE_WEAPON)
+        {
+            if (p->curr_weapon == HANDREMOTE_WEAPON) i = HANDBOMB_WEAPON; else i = p->curr_weapon;
+            digitalnumber(230-22,200-17,p->ammo_amount[i],-16,10+16+128);
+        }
+    }
+
+    if (u&(2048+4096+8192))
+    {
+        if (u != 0xffffffff)
+        {
+            if (u&(2048+4096)) { patchstatusbar(231,179,265,197); }
+                              else { patchstatusbar(250,190,261,195); }
+        }
+        if (p->inven_icon)
+        {
+            o = 0; permbit = 128;
+
+            if (u&(2048+4096))
+            {
+                switch(p->inven_icon)
+                {
+                    case 1: i = FIRSTAID_ICON; break;
+                    case 2: i = STEROIDS_ICON; break;
+                    case 3: i = HOLODUKE_ICON; break;
+                    case 4: i = JETPACK_ICON; break;
+                    case 5: i = HEAT_ICON; break;
+                    case 6: i = AIRTANK_ICON; break;
+                    case 7: i = BOOT_ICON; break;
+                }
+                rotatesprite((231-o)<<16,(200-21)<<16,65536L,0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);
+                minitext(292-30-o,190,"%",6,10+16+permbit);
+                if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit);
+            }
+            if (u&(2048+4096))
+            {
+                switch(p->inven_icon)
+                {
+                    case 3: j = p->holoduke_on; break;
+                    case 4: j = p->jetpack_on; break;
+                    case 5: j = p->heat_on; break;
+                    default: j = 0x80000000;
+                }
+                if (j > 0) minitext(288-30-o,180,"ON",0,10+16+permbit);
+                else if (j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit);
+            }
+            if (u&8192)
+            {
+                switch(p->inven_icon)
+                {
+                    case 1: i = p->firstaid_amount; break;
+                    case 2: i = ((p->steroids_amount+3)>>2); break;
+                    case 3: i = ((p->holoduke_amount+15)/24); break;
+                    case 4: i = ((p->jetpack_amount+15)>>4); break;
+                    case 5: i = p->heat_amount/12; break;
+                    case 6: i = ((p->scuba_amount+63)>>6); break;
+                    case 7: i = (p->boot_amount>>1); break;
+                }
+                invennum(284-30-o,200-6,(char)i,0,10+permbit);
+            }
+        }
+    }
+}
+  
+
+#define AVERAGEFRAMES 16
+static long frameval[AVERAGEFRAMES], framecnt = 0;
+
+void tics(short offx, short offy, short color)
+{
+    long i;
+	char fps[512], mapname[512];
+	long currentFps;
+	static long fpsAvg = 0, savedFps = 0;
+	static boolean toggle = true;
+
+	strcpy(mapname,boardfilename);
+	for(i=0;i<512;i++)
+		if(mapname[i]=='.')
+			mapname[i]=0;
+
+	if( mapname[0] != 0 && ud.m_level_number == 7 && ud.m_volume_number == 0 )
+		sprintf(tempbuf, "%s", mapname);
+    else
+		//sprintf(tempbuf, "%s", level_names[ud.volume_number*11 + ud.level_number]);
+		sprintf(tempbuf, "e%dl%d", ud.volume_number+1, ud.level_number+1);
+		
+
+    i = totalclock;
+
+    if (i != frameval[framecnt])
+    {
+		currentFps = (TICRATE*AVERAGEFRAMES)/(i-frameval[framecnt]);
+		fpsAvg = ((fpsAvg<<3)+(fpsAvg<<2) + (currentFps<<2))>>4;
+
+		frameval[framecnt] = i;
+    }
+		
+    framecnt = ((framecnt+1)&(AVERAGEFRAMES-1));
+
+	// refresh screen and update visible FPS. This is to allow a refresh
+	// of the screen when the screensize > 4 w/o compromising the FPS.
+	if(ud.screen_size>8)
+		if ((totalclock%64) < 32)
+		{
+			if(toggle)
+			{
+				vscrn();
+				savedFps = fpsAvg;
+			}
+			toggle = false;
+		}
+		else
+		{
+			toggle = true;
+		}
+	else
+		savedFps = fpsAvg;
+
+	sprintf(fps," %ld", savedFps);
+	strcat(tempbuf, fps);
+
+	minitext(offx,offy,tempbuf,color,2+8+16+128);
+}
+
+void coords(short snum)
+{
+    short x = 200, y = 0;
+    // x = 250 is too much on the right and
+    // will make the text going out of the screen 
+    // if screen <= (320x200)
+    // This will also *write beyond the video 
+    // buffer limit* and will crash the game.
+
+    if(ud.coop != 1)
+    {
+        if(ud.multimode > 1 && ud.multimode < 5)
+            y = 8;
+        else if(ud.multimode > 4)
+            y = 16;
+    }
+
+    sprintf(tempbuf,"X= %ld",ps[snum].posx);
+    printext256(x,y,31,-1,tempbuf,1);
+    sprintf(tempbuf,"Y= %ld",ps[snum].posy);
+    printext256(x,y+7L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"Z= %ld",ps[snum].posz);
+    printext256(x,y+14L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"A= %ld",ps[snum].ang);
+    printext256(x,y+21L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"ZV= %ld",ps[snum].poszv);
+    printext256(x,y+28L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"OG= %ld",ps[snum].on_ground);
+    printext256(x,y+35L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"AM= %ld",ps[snum].ammo_amount[GROW_WEAPON]);
+    printext256(x,y+43L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"LFW= %ld",ps[snum].last_full_weapon);
+    printext256(x,y+50L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"SECTL= %ld",sector[ps[snum].cursectnum].lotag);
+    printext256(x,y+57L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"SEED= %ld",randomseed);
+    printext256(x,y+64L,31,-1,tempbuf,1);
+    sprintf(tempbuf,"THOLD= %ld",ps[snum].transporter_hold);
+    printext256(x,y+64L+7,31,-1,tempbuf,1);
+}
+
+void operatefta(void)
+{
+     long i, j, k;
+
+     if(ud.screen_size > 0) j = 200-45; else j = 200-8;
+     quotebot = min(quotebot,j);
+     quotebotgoal = min(quotebotgoal,j);
+     if(ps[myconnectindex].gm&MODE_TYPE) j -= 8;
+     quotebotgoal = j; j = quotebot;
+     for(i=0;i<MAXUSERQUOTES;i++)
+     {
+         k = user_quote_time[i]; if (k <= 0) break;
+
+         if (k > 4)
+              gametext(320>>1,j,user_quote[i],0,2+8+16);
+         else if (k > 2) gametext(320>>1,j,user_quote[i],0,2+8+16+1);
+             else gametext(320>>1,j,user_quote[i],0,2+8+16+1+32);
+         j -= 8;
+     }
+
+     if (ps[screenpeek].fta <= 1) return;
+
+     if (ud.coop != 1 && ud.screen_size > 0 && ud.multimode > 1)
+     {
+         j = 0; k = 8;
+         for(i=connecthead;i>=0;i=connectpoint2[i])
+             if (i > j) j = i;
+
+         if (j >= 4 && j <= 8) k += 8;
+         else if (j > 8 && j <= 12) k += 16;
+         else if (j > 12) k += 24;
+     }
+     else k = 0;
+
+     if (ps[screenpeek].ftq == 115 || ps[screenpeek].ftq == 116)
+     {
+         k = quotebot;
+         for(i=0;i<MAXUSERQUOTES;i++)
+         {
+             if (user_quote_time[i] <= 0) break;
+             k -= 8;
+         }
+         k -= 4;
+     }
+
+     j = ps[screenpeek].fta;
+     if (j > 4)
+          gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16);
+     else
+         if (j > 2) gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16+1);
+     else
+         gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16+1+32);
+}
+
+void FTA(short q,struct player_struct *p, int mode)
+{
+    if( ud.fta_on == 1 || mode)
+    {
+        if( p->fta > 0 && q != 115 && q != 116 )
+            if( p->ftq == 115 || p->ftq == 116 ) return;
+        
+        p->fta = 100;
+
+        if( p->ftq != q || q == 26 )
+        // || q == 26 || q == 115 || q ==116 || q == 117 || q == 122 )
+        {
+            p->ftq = q;
+            pub = NUMPAGES;
+            pus = NUMPAGES;
+        }
+    }
+}
+
+void showtwoscreens(void)
+{
+    short i;
+
+	if(VOLUMEONE)
+	{
+	    setview(0,0,xdim-1,ydim-1);
+	    flushperms();
+	    ps[myconnectindex].palette = palette;
+	    for(i=0;i<64;i+=7) palto(0,0,0,i);
+	    KB_FlushKeyboardQueue();
+	    rotatesprite(0,0,65536L,0,3291,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+	    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+	    while( !KB_KeyWaiting() ); // getpackets(); // Net already off. Trying to get packets here makes sporadic crash..
+	
+	    for(i=0;i<64;i+=7) palto(0,0,0,i);
+	    KB_FlushKeyboardQueue();
+	    rotatesprite(0,0,65536L,0,3290,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+	    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+	    while( !KB_KeyWaiting() ); // getpackets();
+	}
+	else if(PLUTOPAK)
+	{
+		setview(0,0,xdim-1,ydim-1);
+	    flushperms();
+	    ps[myconnectindex].palette = palette;
+	    for(i=0;i<64;i+=7) palto(0,0,0,i);
+	    KB_FlushKeyboardQueue();
+	    clearview(0L);
+	    rotatesprite(0,0,65536L,0,TENSCREEN,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+	    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+	    totalclock = 0;
+	    while( !KB_KeyWaiting() /*&& totalclock < 2400*/); // getpackets();
+	// re-added --mk. This makes nice souvenirs!
+	}
+}
+
+void binscreen(void)
+{
+#ifdef PLATFORM_DOS
+    long fil;
+	if (VOLUMEONE)
+	    fil = kopen4load("dukesw.bin",1);
+	else
+	    fil = kopen4load("duke3d.bin",1);
+
+	if(fil == -1) return;
+    kread(fil,(char *)0xb8000,4000);
+    kclose(fil);
+#endif
+}
+
+
+void gameexit(char *msg)
+{
+    short i;
+    char t[256];
+    
+    strncpy(t,msg,256); t[255] = 0;
+
+    if(*t != 0) ps[myconnectindex].palette = (char *) &palette[0];
+
+    if(numplayers > 1)
+        allowtimetocorrecterrorswhenquitting();
+
+    if(ud.recstat == 1)
+        closedemowrite();
+
+    if(frecfilep != NULL)
+    {
+        fclose(frecfilep);
+        frecfilep = NULL;
+    }
+
+    if(qe || cp)
+        goto GOTOHERE;
+
+	// FIX_00089: scoreboard not shown for last player who quits a DM. Only 19.7 affected. (Sarah)
+    if( ud.m_recstat != 2 && ud.last_level >= 0 && playerswhenstarted > 1 && ud.coop != 1 && *t == ' ')
+    {
+        dobonus(1);
+// CTW - MODIFICATION
+//      setgamemode();
+// FIX_00028: No need to call the videodriver on gameexit()
+//		setgamemode(ScreenMode,ScreenWidth,ScreenHeight);
+// CTW END - MODIFICATION
+    }
+
+    if(playerswhenstarted > 1)
+        uninitmultiplayers();  /* deinits network transport. */
+
+#ifdef ONELEVELDEMO
+    doorders();
+    // DDOI
+    //t = "You have been playing a ONE LEVEL demo of Duke Nukem 3D.";
+    strncpy(t, "You have been playing a ONE LEVEL demo of Duke Nukem 3D.", 256);
+    t[255] = 0;
+    // DDOI
+#endif
+
+// CTW - MODIFICATION
+/*  if( *t != 0 && *(t+1) != 'V' && *(t+1) != 'Y' && playonten == 0 )
+        showtwoscreens();*/
+    if( *t != 0 && *(t+1) != 'V' && *(t+1) != 'Y' && true)
+		if(ud.showcinematics) // FIX_00029: toggle cinematics on / off
+			showtwoscreens();
+// CTW END - MODIFICATION
+
+    GOTOHERE:
+
+    Shutdown();
+
+    if(*t != 0)
+    {
+        setvmode(0x3);
+        binscreen();
+// CTW - MODIFICATION
+/*      if(playonten == 0)
+        {
+            if(*t == ' ' && *(t+1) == 0) *t = 0;
+            printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+            printf("%s%s","\n",t);
+        }*/
+        if(true)
+        {
+            if(*t == ' ' && *(t+1) == 0) *t = 0;
+            #if PLATFORM_DOS   // Is there a good reason for this? --ryan.
+            printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+            #else
+            printf("\n%s",t);
+            #endif
+        }
+// CTW END - MODIFICATION        
+    }
+
+    uninitgroupfile();
+
+    unlink("duke3d.tmp");
+	
+    Error(EXIT_SUCCESS, "");
+
+}
+
+
+
+
+short inputloc = 0;
+short strget(short x,short y,char *t,short dalen,short c)
+{
+    short ch,sc;
+
+    while(KB_KeyWaiting())
+    {
+        sc = 0;
+        ch = KB_Getch();
+
+        if (ch == 0)
+        {
+
+            sc = KB_Getch();
+            if( sc == 104) return(1);
+
+            continue;
+        }
+        else
+        {
+            if(ch == 8) // asc_BackSpace
+            {
+                if( inputloc > 0 )
+                {
+                    inputloc--;
+                    *(t+inputloc) = 0;
+                }
+            }
+            else
+            {
+                if(ch == asc_Enter || sc == 104)
+                {
+                    KB_ClearKeyDown(sc_Enter);
+                    KB_ClearKeyDown(sc_kpad_Enter);
+                    return (1);
+                }
+                else if(ch == asc_Escape)
+                {
+                    KB_ClearKeyDown(sc_Escape);
+                    return (-1);
+                }
+                else if ( ch >= 32 && inputloc < dalen && ch < 127)
+                {
+                    ch = toupper(ch);
+                    *(t+inputloc) = ch;
+                    *(t+inputloc+1) = 0;
+                    inputloc++;
+                }
+            }
+        }
+    }
+
+    if( c == 999 ) return(0);
+    if( c == 998 )
+    {
+        char b[41],ii;
+        for(ii=0;ii<inputloc;ii++)
+            b[ii] = '*';
+        b[ii] = 0;
+        x = gametext(x,y,b,c,2+8+16);
+    }
+    else x = gametext(x,y,t,c,2+8+16);
+    c = 4-(sintable[(totalclock<<4)&2047]>>11);
+    rotatesprite((x+8)<<16,(y+4)<<16,32768L,0,SPINNINGNUKEICON+((totalclock>>3)%7),c,0,2+8,0,0,xdim-1,ydim-1);
+
+    return (0);
+}
+
+void typemode(void)
+{
+     short ch, hitstate, i, j;
+
+     if( ps[myconnectindex].gm&MODE_SENDTOWHOM )
+     {
+          if(sendmessagecommand != -1 || ud.multimode < 3 || movesperpacket == 4)
+          {
+                tempbuf[0] = 4; // message command
+                tempbuf[1] = 0;
+                recbuf[0]  = 0;
+
+                if(ud.multimode < 3)
+                     sendmessagecommand = 2;
+
+                strcat(recbuf,ud.user_name[myconnectindex]);
+                strcat(recbuf,": ");
+                strcat(recbuf,typebuf);
+                j = strlen(recbuf);
+                recbuf[j] = 0;
+                strcat(tempbuf+1,recbuf);
+
+                if(sendmessagecommand >= ud.multimode || movesperpacket == 4)
+                {
+                     for(ch=connecthead;ch >= 0;ch=connectpoint2[ch])
+                          if (ch != myconnectindex)
+                                sendpacket(ch,tempbuf,j+1);
+
+                     adduserquote(recbuf);
+                     quotebot += 8;
+                     quotebotgoal = quotebot;
+                }
+                else if(sendmessagecommand >= 0)
+                     sendpacket(sendmessagecommand,tempbuf,j+1);
+
+                sendmessagecommand = -1;
+                ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
+          }
+          else if(sendmessagecommand == -1)
+          {
+                j = 50;
+                gametext(320>>1,j,"SEND MESSAGE TO...",0,2+8+16); j += 8;
+                for(i=connecthead;i>=0;i=connectpoint2[i])
+//                for(i=0;i<ud.multimode;i++)
+                {
+                     if (i == myconnectindex)
+                     {
+                         minitextshade((320>>1)-40+1,j+1,"A/ENTER - ALL",26,0,2+8+16);
+                         minitext((320>>1)-40,j,"A/ENTER - ALL",0,2+8+16); j += 7;
+                     }
+                     else
+                     {
+                         sprintf(buf,"      %ld - %s",i+1,ud.user_name[i]);
+                         minitextshade((320>>1)-40-6+1,j+1,buf,26,0,2+8+16);
+                         minitext((320>>1)-40-6,j,buf,0,2+8+16); j += 7;
+                     }
+                }
+                minitextshade((320>>1)-40-4+1,j+1,"    ESC - Abort",26,0,2+8+16);
+                minitext((320>>1)-40-4,j,"    ESC - Abort",0,2+8+16); j += 7;
+
+                //sprintf(buf,"PRESS 1-%ld FOR INDIVIDUAL PLAYER.",ud.multimode);
+                //gametext(320>>1,j,buf,0,2+8+16); j += 8;
+                //gametext(320>>1,j,"'A' OR 'ENTER' FOR ALL PLAYERS",0,2+8+16); j += 8;
+                //gametext(320>>1,j,"ESC ABORTS",0,2+8+16); j += 8;
+
+                if (ud.screen_size > 0) j = 200-45; else j = 200-8;
+                gametext(320>>1,j,typebuf,0,2+8+16);
+
+                if( KB_KeyWaiting() )
+                {
+                     i = KB_Getch();
+
+                     if(i == 'A' || i == 'a' || i == 13)
+                          sendmessagecommand = ud.multimode;
+                     else if(i >= '1' || i <= (ud.multimode + '1') )
+                          sendmessagecommand = i - '1';
+                     else
+                     {
+                        sendmessagecommand = ud.multimode;
+                          if(i == 27)
+                          {
+                              ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
+                              sendmessagecommand = -1;
+                          }
+                          else
+                          typebuf[0] = 0;
+                     }
+
+                     KB_ClearKeyDown(sc_1);
+                     KB_ClearKeyDown(sc_2);
+                     KB_ClearKeyDown(sc_3);
+                     KB_ClearKeyDown(sc_4);
+                     KB_ClearKeyDown(sc_5);
+                     KB_ClearKeyDown(sc_6);
+                     KB_ClearKeyDown(sc_7);
+                     KB_ClearKeyDown(sc_8);
+                     KB_ClearKeyDown(sc_A);
+                     KB_ClearKeyDown(sc_Escape);
+                     KB_ClearKeyDown(sc_Enter);
+                }
+          }
+     }
+     else
+     {
+          if(ud.screen_size > 0) j = 200-45; else j = 200-8;
+          hitstate = strget(320>>1,j,typebuf,30,1);
+
+          if(hitstate == 1)
+          {
+                KB_ClearKeyDown(sc_Enter);
+                ps[myconnectindex].gm |= MODE_SENDTOWHOM;
+          }
+          else if(hitstate == -1)
+                ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
+          else pub = NUMPAGES;
+     }
+}
+
+void moveclouds(void)
+{
+    if( totalclock > cloudtotalclock || totalclock < (cloudtotalclock-7))
+    {
+        short i;
+
+        cloudtotalclock = totalclock+6;
+
+        for(i=0;i<numclouds;i++)
+        {
+            cloudx[i] += (sintable[(ps[screenpeek].ang+512)&2047]>>9);
+            cloudy[i] += (sintable[ps[screenpeek].ang&2047]>>9);
+
+            sector[clouds[i]].ceilingxpanning = cloudx[i]>>6;
+            sector[clouds[i]].ceilingypanning = cloudy[i]>>6;
+        }
+    }
+}
+
+
+void displayrest(long smoothratio)
+{
+    long a, i, j;
+
+    struct player_struct *pp;
+    walltype *wal;
+    long cposx,cposy,cang;
+
+    pp = &ps[screenpeek];
+
+
+    if(ud.show_help)
+    {
+        switch(ud.show_help)
+        {
+            case 1:
+                rotatesprite(0,0,65536L,0,TEXTSTORY,0,0,10+16+64, 0,0,xdim-1,ydim-1);
+                break;
+            case 2:
+                rotatesprite(0,0,65536L,0,F1HELP,0,0,10+16+64, 0,0,xdim-1,ydim-1);
+                break;
+        }
+
+        if ( KB_KeyPressed(sc_Escape ) )
+        {
+            KB_ClearKeyDown(sc_Escape);
+            ud.show_help = 0;
+            if(ud.multimode < 2 && ud.recstat != 2)
+            {
+                ready2send = 1;
+                totalclock = ototalclock;
+            }
+            vscrn();
+        }
+        return;
+    }
+
+    i = pp->cursectnum;
+
+    show2dsector[i>>3] |= (1<<(i&7));
+    wal = &wall[sector[i].wallptr];
+    for(j=sector[i].wallnum;j>0;j--,wal++)
+    {
+        i = wal->nextsector;
+        if (i < 0) continue;
+        if (wal->cstat&0x0071) continue;
+        if (wall[wal->nextwall].cstat&0x0071) continue;
+        if (sector[i].lotag == 32767) continue;
+        if (sector[i].ceilingz >= sector[i].floorz) continue;
+        show2dsector[i>>3] |= (1<<(i&7));
+    }
+
+    if(ud.camerasprite == -1)
+    {
+        if( ud.overhead_on != 2 )
+        {
+            if(pp->newowner >= 0)
+                cameratext(pp->newowner);
+            else
+            {
+                displayweapon(screenpeek);
+                if(pp->over_shoulder_on == 0 )
+                    displaymasks(screenpeek);
+            }
+            moveclouds();
+        }
+
+        if( ud.overhead_on > 0 )
+        {
+                smoothratio = min(max(smoothratio,0),65536);
+                dointerpolations(smoothratio);
+                if( ud.scrollmode == 0 )
+                {
+                     if(pp->newowner == -1)
+                     {
+                         if (screenpeek == myconnectindex && numplayers > 1)
+                         {
+                             cposx = omyx+mulscale16((long)(myx-omyx),smoothratio);
+                             cposy = omyy+mulscale16((long)(myy-omyy),smoothratio);
+                             cang = omyang+mulscale16((long)(((myang+1024-omyang)&2047)-1024),smoothratio);
+                         }
+                         else
+                         {
+                              cposx = pp->oposx+mulscale16((long)(pp->posx-pp->oposx),smoothratio);
+                              cposy = pp->oposy+mulscale16((long)(pp->posy-pp->oposy),smoothratio);
+                              cang = pp->oang+mulscale16((long)(((pp->ang+1024-pp->oang)&2047)-1024),smoothratio);
+                         }
+                    }
+                    else
+                    {
+                        cposx = pp->oposx;
+                        cposy = pp->oposy;
+                        cang = pp->oang;
+                    }
+                }
+                else
+                {
+
+                     ud.fola += ud.folavel>>3;
+                     ud.folx += (ud.folfvel*sintable[(512+2048-ud.fola)&2047])>>14;
+                     ud.foly += (ud.folfvel*sintable[(512+1024-512-ud.fola)&2047])>>14;
+
+                     cposx = ud.folx;
+                     cposy = ud.foly;
+                     cang = ud.fola;
+                }
+
+                if(ud.overhead_on == 2)
+                {
+                    clearview(0L);
+                    drawmapview(cposx,cposy,pp->zoom,cang);
+                }
+                drawoverheadmap( cposx,cposy,pp->zoom,cang);
+
+                restoreinterpolations();
+
+                if(ud.overhead_on == 2)
+                {
+                    if(ud.screen_size > 0) a = 147;
+                    else a = 182;
+
+                    minitext(1,a+6,volume_names[ud.volume_number],0,2+8+16);
+                    minitext(1,a+12,level_names[ud.volume_number*11 + ud.level_number],0,2+8+16);
+                }
+        }
+    }
+
+    coolgaugetext(screenpeek);
+    operatefta();
+
+    if( KB_KeyPressed(sc_Escape) && ud.overhead_on == 0
+        && ud.show_help == 0
+        && ps[myconnectindex].newowner == -1)
+    {
+			if( (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU &&
+            ps[myconnectindex].newowner == -1 &&
+            (ps[myconnectindex].gm&MODE_TYPE) != MODE_TYPE)
+        {
+            KB_ClearKeyDown(sc_Escape);
+            FX_StopAllSounds();
+            clearsoundlocks();
+
+            intomenusounds();
+
+            ps[myconnectindex].gm |= MODE_MENU;
+
+            if(ud.multimode < 2 && ud.recstat != 2) ready2send = 0;
+
+            if(ps[myconnectindex].gm&MODE_GAME) cmenu(50);
+            else cmenu(0);
+            screenpeek = myconnectindex;
+        }
+    }
+
+    if(ps[myconnectindex].newowner == -1 && ud.overhead_on == 0 && ud.crosshair && ud.camerasprite == -1)
+        rotatesprite((160L-(ps[myconnectindex].look_ang>>1))<<16,100L<<16,65536L,0,CROSSHAIR,0,0,2+1,windowx1,windowy1,windowx2,windowy2);
+
+    if(ps[myconnectindex].gm&MODE_TYPE)
+        typemode();
+    else
+    {
+        CONSOLE_HandleInput();
+        if( !CONSOLE_IsActive())
+        {
+            menus();
+        }
+        CONSOLE_Render();
+    }
+    
+    if( ud.pause_on==1 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
+	{
+		if (!CONSOLE_IsActive()) //Addfaz Console Pause Game line addition 
+		{
+			menutext(160,100,0,0,"GAME PAUSED");
+		}
+		else
+		{
+			menutext(160,120,0,0,"GAME PAUSED");
+		}
+	}
+
+    if(ud.coords)
+        coords(screenpeek);
+
+	// FIX_00085: Optimized Video driver. FPS increases by +20%.
+    if( pp->pals_time > 0 && pp->loogcnt == 0)
+    {
+        palto( pp->pals[0],
+               pp->pals[1],
+               pp->pals[2],
+               pp->pals_time|128);
+
+        restorepalette = 1;
+    }
+    else if( restorepalette )
+    {
+        setbrightness(ud.brightness>>2,&pp->palette[0]);
+        restorepalette = 0;
+    }
+    else if(pp->loogcnt > 0) palto(0,64,0,(pp->loogcnt>>1)+128);
+
+}
+
+
+void updatesectorz(long x, long y, long z, short *sectnum)
+{
+    walltype *wal;
+    long i, j, cz, fz;
+
+    getzsofslope(*sectnum,x,y,&cz,&fz);
+    if ((z >= cz) && (z <= fz))
+        if (inside(x,y,*sectnum) != 0) return;
+
+    if ((*sectnum >= 0) && (*sectnum < numsectors))
+    {
+        wal = &wall[sector[*sectnum].wallptr];
+        j = sector[*sectnum].wallnum;
+        do
+        {
+            i = wal->nextsector;
+            if (i >= 0)
+            {
+                getzsofslope(i,x,y,&cz,&fz);
+                if ((z >= cz) && (z <= fz))
+                    if (inside(x,y,(short)i) == 1)
+                        { *sectnum = i; return; }
+            }
+            wal++; j--;
+        } while (j != 0);
+    }
+
+    for(i=numsectors-1;i>=0;i--)
+    {
+        getzsofslope(i,x,y,&cz,&fz);
+        if ((z >= cz) && (z <= fz))
+            if (inside(x,y,(short)i) == 1)
+                { *sectnum = i; return; }
+    }
+
+    *sectnum = -1;
+}
+
+void view(struct player_struct *pp, long *vx, long *vy,long *vz,short *vsectnum, short ang, short horiz)
+{
+     spritetype *sp;
+     long i, nx, ny, nz, hx, hy, hz, hitx, hity, hitz;
+     short bakcstat, hitsect, hitwall, hitsprite, daang;
+
+     nx = (sintable[(ang+1536)&2047]>>4);
+     ny = (sintable[(ang+1024)&2047]>>4);
+     nz = (horiz-100)*128;
+
+     sp = &sprite[pp->i];
+
+     bakcstat = sp->cstat;
+     sp->cstat &= (short)~0x101;
+
+     updatesectorz(*vx,*vy,*vz,vsectnum);
+     hitscan(*vx,*vy,*vz,*vsectnum,nx,ny,nz,&hitsect,&hitwall,&hitsprite,&hitx,&hity,&hitz,CLIPMASK1);
+
+     if(*vsectnum < 0)
+     {
+        sp->cstat = bakcstat;
+        return;
+     }
+
+     hx = hitx-(*vx); hy = hity-(*vy);
+     if (klabs(nx)+klabs(ny) > klabs(hx)+klabs(hy))
+     {
+         *vsectnum = hitsect;
+         if (hitwall >= 0)
+         {
+             daang = getangle(wall[wall[hitwall].point2].x-wall[hitwall].x,
+                                    wall[wall[hitwall].point2].y-wall[hitwall].y);
+
+             i = nx*sintable[daang]+ny*sintable[(daang+1536)&2047];
+             if (klabs(nx) > klabs(ny)) hx -= mulscale28(nx,i);
+                                          else hy -= mulscale28(ny,i);
+         }
+         else if (hitsprite < 0)
+         {
+             if (klabs(nx) > klabs(ny)) hx -= (nx>>5);
+                                          else hy -= (ny>>5);
+         }
+         if (klabs(nx) > klabs(ny)) i = divscale16(hx,nx);
+                                      else i = divscale16(hy,ny);
+         if (i < cameradist) cameradist = i;
+     }
+     *vx = (*vx)+mulscale16(nx,cameradist);
+     *vy = (*vy)+mulscale16(ny,cameradist);
+     *vz = (*vz)+mulscale16(nz,cameradist);
+
+     cameradist = min(cameradist+((totalclock-cameraclock)<<10),65536);
+     cameraclock = totalclock;
+
+     updatesectorz(*vx,*vy,*vz,vsectnum);
+
+     sp->cstat = bakcstat;
+}
+     
+    //REPLACE FULLY
+void drawbackground(void)
+{
+     short dapicnum;
+     long x,y,x1,y1,x2,y2,topy;
+
+     flushperms();
+
+     switch(ud.m_volume_number)
+     {
+          default:dapicnum = BIGHOLE;break;
+          case 1:dapicnum = BIGHOLE;break;
+          case 2:dapicnum = BIGHOLE;break;
+     }
+
+     y1 = 0; y2 = ydim;
+     if( ready2send || ud.recstat == 2 )
+     {
+        if(ud.coop != 1)
+        {
+            if (ud.multimode > 1) y1 += scale(ydim,8,200);
+            if (ud.multimode > 4) y1 += scale(ydim,8,200);
+        }
+        if (ud.screen_size >= 8) y2 = scale(ydim,200-34,200);
+     }
+
+     for(y=y1;y<y2;y+=128)
+          for(x=0;x<xdim;x+=128)
+                rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,y1,xdim-1,y2-1);
+
+	 // FIX_00081: Screen border in menu
+     if(ud.screen_size > 8 && (ps[myconnectindex].gm & MODE_GAME || ud.recstat == 2 )) // ud.recstat == 2 => playing demo
+     {
+          y = 0;
+          if(ud.coop != 1)
+          {
+             if (ud.multimode > 1) y += 8;
+             if (ud.multimode > 4) y += 8;
+          }
+
+          x1 = max(windowx1-4,0);
+          y1 = max(windowy1-4,y);
+          x2 = min(windowx2+4,xdim-1);
+          y2 = min(windowy2+4,scale(ydim,200-34,200)-1);
+
+          for(y=y1+4;y<y2-4;y+=64)
+          {
+                rotatesprite(x1<<16,y<<16,65536L,0,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
+                rotatesprite((x2+1)<<16,(y+64)<<16,65536L,1024,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
+          }
+
+          for(x=x1+4;x<x2-4;x+=64)
+          {
+                rotatesprite((x+64)<<16,y1<<16,65536L,512,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
+                rotatesprite(x<<16,(y2+1)<<16,65536L,1536,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
+          }
+
+          rotatesprite(x1<<16,y1<<16,65536L,0,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
+          rotatesprite((x2+1)<<16,y1<<16,65536L,512,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
+          rotatesprite((x2+1)<<16,(y2+1)<<16,65536L,1024,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
+          rotatesprite(x1<<16,(y2+1)<<16,65536L,1536,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
+     }
+}
+
+
+// Floor Over Floor
+
+// If standing in sector with SE42
+// then draw viewing to SE41 and raise all =hi SE43 cielings.
+
+// If standing in sector with SE43
+// then draw viewing to SE40 and lower all =hi SE42 floors.
+
+// If standing in sector with SE44
+// then draw viewing to SE40.
+
+// If standing in sector with SE45
+// then draw viewing to SE41.
+
+#define FOFTILE 13
+#define FOFTILEX 32
+#define FOFTILEY 32
+long tempsectorz[MAXSECTORS];
+long tempsectorpicnum[MAXSECTORS];
+//short tempcursectnum;
+
+static void SE40_Draw(int spnum,long x,long y,long z,short a,short h,long smoothratio)
+{
+ int i=0,j=0,k=0;
+ int floor1=0,floor2=0,ok=0,fofmode=0;
+ long offx,offy;
+
+ if(sprite[spnum].ang!=512) return;
+
+ i = FOFTILE;    //Effect TILE
+ if (!(gotpic[i>>3]&(1<<(i&7)))) return;
+ gotpic[i>>3] &= ~(1<<(i&7));
+
+ floor1=spnum;
+
+ if(sprite[spnum].lotag==42) fofmode=40;
+ if(sprite[spnum].lotag==43) fofmode=41;
+ if(sprite[spnum].lotag==44) fofmode=40;
+ if(sprite[spnum].lotag==45) fofmode=41;
+
+// fofmode=sprite[spnum].lotag-2;
+
+// sectnum=sprite[j].sectnum;
+// sectnum=cursectnum;
+ ok++;
+
+/*  recursive?
+ for(j=0;j<MAXSPRITES;j++)
+ {
+  if(
+     sprite[j].sectnum==sectnum &&
+     sprite[j].picnum==1 &&
+     sprite[j].lotag==110
+    ) { DrawFloorOverFloor(j); break;}
+ }
+*/
+
+// if(ok==0) { Message("no fof",RED); return; }
+
+ for(j=0;j<MAXSPRITES;j++)
+ {
+  if(
+     sprite[j].picnum==1 &&
+     sprite[j].lotag==fofmode &&
+     sprite[j].hitag==sprite[floor1].hitag
+    ) { floor1=j; fofmode=sprite[j].lotag; ok++; break;}
+ }
+// if(ok==1) { Message("no floor1",RED); return; }
+
+ if(fofmode==40) k=41; else k=40;
+
+ for(j=0;j<MAXSPRITES;j++)
+ {
+  if(
+     sprite[j].picnum==1 &&
+     sprite[j].lotag==k &&
+     sprite[j].hitag==sprite[floor1].hitag
+    ) {floor2=j; ok++; break;}
+ }
+
+// if(ok==2) { Message("no floor2",RED); return; }
+
+ for(j=0;j<MAXSPRITES;j++)  // raise ceiling or floor
+ {
+  if(sprite[j].picnum==1 &&
+     sprite[j].lotag==k+2 &&
+     sprite[j].hitag==sprite[floor1].hitag
+    )
+    {
+     if(k==40)
+     {tempsectorz[sprite[j].sectnum]=sector[sprite[j].sectnum].floorz;
+      sector[sprite[j].sectnum].floorz+=(((z-sector[sprite[j].sectnum].floorz)/32768)+1)*32768;
+      tempsectorpicnum[sprite[j].sectnum]=sector[sprite[j].sectnum].floorpicnum;
+      sector[sprite[j].sectnum].floorpicnum=13;
+     }
+     if(k==41)
+     {tempsectorz[sprite[j].sectnum]=sector[sprite[j].sectnum].ceilingz;
+      sector[sprite[j].sectnum].ceilingz+=(((z-sector[sprite[j].sectnum].ceilingz)/32768)-1)*32768;
+      tempsectorpicnum[sprite[j].sectnum]=sector[sprite[j].sectnum].ceilingpicnum;
+      sector[sprite[j].sectnum].ceilingpicnum=13;
+     }
+    }
+ }
+
+ i=floor1;
+ offx=x-sprite[i].x;
+ offy=y-sprite[i].y;
+ i=floor2;
+ drawrooms(offx+sprite[i].x,offy+sprite[i].y,z,a,h,sprite[i].sectnum);
+ animatesprites(x,y,a,smoothratio);
+ drawmasks();
+
+ for(j=0;j<MAXSPRITES;j++)  // restore ceiling or floor
+ {
+  if(sprite[j].picnum==1 &&
+     sprite[j].lotag==k+2 &&
+     sprite[j].hitag==sprite[floor1].hitag
+    )
+    {
+     if(k==40)
+     {sector[sprite[j].sectnum].floorz=tempsectorz[sprite[j].sectnum];
+      sector[sprite[j].sectnum].floorpicnum=tempsectorpicnum[sprite[j].sectnum];
+     }
+     if(k==41)
+     {sector[sprite[j].sectnum].ceilingz=tempsectorz[sprite[j].sectnum];
+      sector[sprite[j].sectnum].ceilingpicnum=tempsectorpicnum[sprite[j].sectnum];
+     }
+    }// end if
+ }// end for
+
+} // end SE40
+
+
+
+
+static void se40code(long x,long y,long z,long a,long h, long smoothratio)
+{
+    int i;
+
+    i = headspritestat[15];
+    while(i >= 0)
+    {
+        switch(sprite[i].lotag)
+        {
+//            case 40:
+//            case 41:
+//                SE40_Draw(i,x,y,a,smoothratio);
+//                break;
+            case 42:
+            case 43:
+            case 44:
+            case 45:
+                if(ps[screenpeek].cursectnum == sprite[i].sectnum)
+                    SE40_Draw(i,x,y,z,a,h,smoothratio);
+                break;
+        }
+        i = nextspritestat[i];
+    }
+}
+
+static long oyrepeat=-1;
+
+void displayrooms(short snum,long smoothratio)
+{
+    long cposx,cposy,cposz,dst,j,fz,cz,hz,lz;
+    short sect, cang, k, choriz,tsect;
+    struct player_struct *p;
+    long tposx,tposy,tposz,dx,dy,thoriz,i;
+    short tang;
+
+    p = &ps[snum];
+
+//    if(screencapt == 0 && (p->gm&MODE_MENU) && ( (current_menu/100) == 3 ) || (current_menu >= 1000 && current_menu < 2999 ) )
+  //      return;
+
+    if(pub > 0)
+    {
+        if(ud.screen_size > 8) drawbackground();
+        pub = 0;
+    }
+
+    if( ud.overhead_on == 2 || ud.show_help || p->cursectnum == -1)
+        return;
+
+    smoothratio = min(max(smoothratio,0),65536);
+
+    visibility = p->visibility;
+
+    if(ud.pause_on || ps[snum].on_crane > -1) smoothratio = 65536;
+
+    sect = p->cursectnum;
+    if(sect < 0 || sect >= MAXSECTORS) return;
+
+    dointerpolations(smoothratio);
+
+    animatecamsprite();
+
+    if(ud.camerasprite >= 0)
+    {
+        spritetype *s;
+
+        s = &sprite[ud.camerasprite];
+
+        if(s->yvel < 0) s->yvel = -100;
+        else if(s->yvel > 199) s->yvel = 300;
+
+        cang = hittype[ud.camerasprite].tempang+mulscale16((long)(((s->ang+1024-hittype[ud.camerasprite].tempang)&2047)-1024),smoothratio);
+
+        se40code(s->x,s->y,s->z,cang,s->yvel,smoothratio);
+
+        drawrooms(s->x,s->y,s->z-(4<<8),cang,s->yvel,s->sectnum);
+        animatesprites(s->x,s->y,cang,smoothratio);
+        drawmasks();
+    }
+    else
+    {
+        i = divscale22(1,sprite[p->i].yrepeat+28);
+        if (i != oyrepeat)
+        {
+            oyrepeat = i;
+			//printf("1: %d %d\n", oyrepeat,yxaspect);
+            setaspect(oyrepeat,yxaspect);
+			//printf("2: %d %d\n", oyrepeat,yxaspect);
+        }
+
+        if(screencapt)
+        {
+            walock[MAXTILES-1] = 254;
+            if (waloff[MAXTILES-1] == 0)
+                allocache((long *)&waloff[MAXTILES-1],100*160,&walock[MAXTILES-1]);
+            setviewtotile(MAXTILES-1,100L,160L);
+        }
+        else if( ( ud.screen_tilting && p->rotscrnang ) || ud.detail==0 )
+        {
+                if (ud.screen_tilting) tang = p->rotscrnang; else tang = 0;
+
+                walock[MAXTILES-2] = 255;
+                if (waloff[MAXTILES-2] == 0)
+                    allocache(&waloff[MAXTILES-2],320L*320L,&walock[MAXTILES-2]);
+                if ((tang&1023) == 0)
+                    setviewtotile(MAXTILES-2,200L>>(1-ud.detail),320L>>(1-ud.detail));
+                else
+                    setviewtotile(MAXTILES-2,320L>>(1-ud.detail),320L>>(1-ud.detail));
+                if ((tang&1023) == 512)
+                {     //Block off unscreen section of 90� tilted screen
+                    j = ((320-60)>>(1-ud.detail));
+                    for(i=(60>>(1-ud.detail))-1;i>=0;i--)
+                    {
+                        startumost[i] = 1; startumost[i+j] = 1;
+                        startdmost[i] = 0; startdmost[i+j] = 0;
+                    }
+                }
+
+                i = (tang&511); if (i > 256) i = 512-i;
+                i = sintable[i+512]*8 + sintable[i]*5L;
+                setaspect(i>>1,yxaspect);
+          }
+
+          if ( (snum == myconnectindex) && (numplayers > 1) )
+                  {
+                                cposx = omyx+mulscale16((long)(myx-omyx),smoothratio);
+                                cposy = omyy+mulscale16((long)(myy-omyy),smoothratio);
+                                cposz = omyz+mulscale16((long)(myz-omyz),smoothratio);
+                                cang = omyang+mulscale16((long)(((myang+1024-omyang)&2047)-1024),smoothratio);
+                                choriz = omyhoriz+omyhorizoff+mulscale16((long)(myhoriz+myhorizoff-omyhoriz-omyhorizoff),smoothratio);
+                                sect = mycursectnum;
+                  }
+                  else
+                  {
+                                cposx = p->oposx+mulscale16((long)(p->posx-p->oposx),smoothratio);
+                                cposy = p->oposy+mulscale16((long)(p->posy-p->oposy),smoothratio);
+                                cposz = p->oposz+mulscale16((long)(p->posz-p->oposz),smoothratio);
+                                cang = p->oang+mulscale16((long)(((p->ang+1024-p->oang)&2047)-1024),smoothratio);
+                                choriz = p->ohoriz+p->ohorizoff+mulscale16((long)(p->horiz+p->horizoff-p->ohoriz-p->ohorizoff),smoothratio);
+                  }
+                  cang += p->look_ang;
+
+                  if (p->newowner >= 0)
+                  {
+                                cang = p->ang+p->look_ang;
+                                choriz = p->horiz+p->horizoff;
+                                cposx = p->posx;
+                                cposy = p->posy;
+                                cposz = p->posz;
+                                sect = sprite[p->newowner].sectnum;
+                                smoothratio = 65536L;
+                  }
+
+                  else if( p->over_shoulder_on == 0 )
+                                cposz += p->opyoff+mulscale16((long)(p->pyoff-p->opyoff),smoothratio);
+                  else view(p,&cposx,&cposy,&cposz,&sect,cang,choriz);
+
+        cz = hittype[p->i].ceilingz;
+        fz = hittype[p->i].floorz;
+
+        if(earthquaketime > 0 && p->on_ground == 1)
+        {
+            cposz += 256-(((earthquaketime)&1)<<9);
+            cang += (2-((earthquaketime)&2))<<2;
+        }
+
+        if(sprite[p->i].pal == 1) cposz -= (18<<8);
+
+        if(p->newowner >= 0)
+            choriz = 100+sprite[p->newowner].shade;
+        else if(p->spritebridge == 0)
+        {
+            if( cposz < ( p->truecz + (4<<8) ) ) cposz = cz + (4<<8);
+            else if( cposz > ( p->truefz - (4<<8) ) ) cposz = fz - (4<<8);
+        }
+
+        if (sect >= 0)
+        {
+            getzsofslope(sect,cposx,cposy,&cz,&fz);
+            if (cposz < cz+(4<<8)) cposz = cz+(4<<8);
+            if (cposz > fz-(4<<8)) cposz = fz-(4<<8);
+        }
+
+        if(choriz > 299) choriz = 299;
+        else if(choriz < -99) choriz = -99;
+
+        se40code(cposx,cposy,cposz,cang,choriz,smoothratio);
+
+        if ((gotpic[MIRROR>>3]&(1<<(MIRROR&7))) > 0)
+        {
+            dst = 0x7fffffff; i = 0;
+            for(k=0;k<mirrorcnt;k++)
+            {
+                j = klabs(wall[mirrorwall[k]].x-cposx);
+                j += klabs(wall[mirrorwall[k]].y-cposy);
+                if (j < dst) dst = j, i = k;
+            }
+
+            if( wall[mirrorwall[i]].overpicnum == MIRROR )
+            {
+                preparemirror(cposx,cposy,cposz,cang,choriz,mirrorwall[i],mirrorsector[i],&tposx,&tposy,&tang);
+
+                j = visibility;
+                visibility = (j>>1) + (j>>2);
+
+                drawrooms(tposx,tposy,cposz,tang,choriz,mirrorsector[i]+MAXSECTORS);
+
+                display_mirror = 1;
+                animatesprites(tposx,tposy,tang,smoothratio);
+                display_mirror = 0;
+
+                drawmasks();
+                completemirror();   //Reverse screen x-wise in this function
+                visibility = j;
+            }
+            gotpic[MIRROR>>3] &= ~(1<<(MIRROR&7));
+        }
+
+        drawrooms(cposx,cposy,cposz,cang,choriz,sect);
+        animatesprites(cposx,cposy,cang,smoothratio);
+        drawmasks();
+
+        if(screencapt == 1)
+        {
+            setviewback();
+            walock[MAXTILES-1] = 1;
+            screencapt = 0;
+        }
+        else if( ( ud.screen_tilting && p->rotscrnang) || ud.detail==0 )
+        {
+            if (ud.screen_tilting) tang = p->rotscrnang; else tang = 0;
+            setviewback();
+            picanm[MAXTILES-2] &= 0xff0000ff;
+            i = (tang&511); if (i > 256) i = 512-i;
+            i = sintable[i+512]*8 + sintable[i]*5L;
+            if ((1-ud.detail) == 0) i >>= 1;
+            rotatesprite(160<<16,100<<16,i,tang+512,MAXTILES-2,0,0,4+2+64,windowx1,windowy1,windowx2,windowy2);
+            walock[MAXTILES-2] = 199;
+        }
+    }
+
+    restoreinterpolations();
+
+    if (totalclock < lastvisinc)
+    {
+        if (klabs(p->visibility-ud.const_visibility) > 8)
+            p->visibility += (ud.const_visibility-p->visibility)>>2;
+    }
+    else p->visibility = ud.const_visibility;
+}
+
+
+
+
+
+short LocateTheLocator(short n,short sn)
+{
+    short i;
+
+    i = headspritestat[7];
+    while(i >= 0)
+    {
+        if( (sn == -1 || sn == SECT) && n == SLT )
+            return i;
+        i = nextspritestat[i];
+    }
+    return -1;
+}
+
+short EGS(short whatsect,long s_x,long s_y,long s_z,short s_pn,signed char s_s,signed char s_xr,signed char s_yr,short s_a,short s_ve,long s_zv,short s_ow,signed char s_ss)
+{
+    short i;
+    spritetype *s;
+
+    i = insertsprite(whatsect,s_ss);
+
+    if( i < 0 )
+        gameexit(" Too many sprites spawned. This may happen (for any duke port) if you have hacked the steroids trail in the *.con files. If so, delete your *.con files to use the internal ones and try again.");
+
+    hittype[i].bposx = s_x;
+    hittype[i].bposy = s_y;
+    hittype[i].bposz = s_z;
+
+    s = &sprite[i];
+
+    s->x = s_x;
+    s->y = s_y;
+    s->z = s_z;
+    s->cstat = 0;
+    s->picnum = s_pn;
+    s->shade = s_s;
+    s->xrepeat = s_xr;
+    s->yrepeat = s_yr;
+    s->pal = 0;
+
+    s->ang = s_a;
+    s->xvel = s_ve;
+    s->zvel = s_zv;
+    s->owner = s_ow;
+    s->xoffset = 0;
+    s->yoffset = 0;
+    s->yvel = 0;
+    s->clipdist = 0;
+    s->pal = 0;
+    s->lotag = 0;
+
+    hittype[i].picnum = sprite[s_ow].picnum;
+
+    hittype[i].lastvx = 0;
+    hittype[i].lastvy = 0;
+
+    hittype[i].timetosleep = 0;
+    hittype[i].actorstayput = -1;
+    hittype[i].extra = -1;
+    hittype[i].owner = s_ow;
+    hittype[i].cgg = 0;
+    hittype[i].movflag = 0;
+    hittype[i].tempang = 0;
+    hittype[i].dispicnum = 0;
+    hittype[i].floorz = hittype[s_ow].floorz;
+    hittype[i].ceilingz = hittype[s_ow].ceilingz;
+
+    T1=T3=T4=T6=0;
+    if( actorscrptr[s_pn] )
+    {
+        s->extra = *actorscrptr[s_pn];
+        T5 = *(actorscrptr[s_pn]+1);
+        T2 = *(actorscrptr[s_pn]+2);
+        s->hitag = *(actorscrptr[s_pn]+3);
+    }
+    else
+    {
+        T2=T5=0;
+        s->extra = 0;
+        s->hitag = 0;
+    }
+
+    if (show2dsector[SECT>>3]&(1<<(SECT&7))) show2dsprite[i>>3] |= (1<<(i&7));
+    else show2dsprite[i>>3] &= ~(1<<(i&7));
+/*
+    if(s->sectnum < 0)
+    {
+        s->xrepeat = s->yrepeat = 0;
+        changespritestat(i,5);
+    }
+*/
+    return(i);
+}
+
+char wallswitchcheck(short i)
+{
+    switch(PN)
+    {
+        case HANDPRINTSWITCH:
+        case HANDPRINTSWITCH+1:
+        case ALIENSWITCH:
+        case ALIENSWITCH+1:
+        case MULTISWITCH:
+        case MULTISWITCH+1:
+        case MULTISWITCH+2:
+        case MULTISWITCH+3:
+        case ACCESSSWITCH:
+        case ACCESSSWITCH2:
+        case PULLSWITCH:
+        case PULLSWITCH+1:
+        case HANDSWITCH:
+        case HANDSWITCH+1:
+        case SLOTDOOR:
+        case SLOTDOOR+1:
+        case LIGHTSWITCH:
+        case LIGHTSWITCH+1:
+        case SPACELIGHTSWITCH:
+        case SPACELIGHTSWITCH+1:
+        case SPACEDOORSWITCH:
+        case SPACEDOORSWITCH+1:
+        case FRANKENSTINESWITCH:
+        case FRANKENSTINESWITCH+1:
+        case LIGHTSWITCH2:
+        case LIGHTSWITCH2+1:
+        case POWERSWITCH1:
+        case POWERSWITCH1+1:
+        case LOCKSWITCH1:
+        case LOCKSWITCH1+1:
+        case POWERSWITCH2:
+        case POWERSWITCH2+1:
+        case DIPSWITCH:
+        case DIPSWITCH+1:
+        case DIPSWITCH2:
+        case DIPSWITCH2+1:
+        case TECHSWITCH:
+        case TECHSWITCH+1:
+        case DIPSWITCH3:
+        case DIPSWITCH3+1:
+            return 1;
+    }
+    return 0;
+}
+
+
+long tempwallptr;
+short spawn( short j, short pn )
+{
+    short i, s, startwall, endwall, sect, clostest;
+    long x, y, d;
+    spritetype *sp;
+
+    if(j >= 0)
+    {
+        i = EGS(sprite[j].sectnum,sprite[j].x,sprite[j].y,sprite[j].z
+            ,pn,0,0,0,0,0,0,j,0);
+        hittype[i].picnum = sprite[j].picnum;
+    }
+    else
+    {
+        i = pn;
+
+        hittype[i].picnum = PN;
+        hittype[i].timetosleep = 0;
+        hittype[i].extra = -1;
+
+        hittype[i].bposx = SX;
+        hittype[i].bposy = SY;
+        hittype[i].bposz = SZ;
+
+        OW = hittype[i].owner = i;
+        hittype[i].cgg = 0;
+        hittype[i].movflag = 0;
+        hittype[i].tempang = 0;
+        hittype[i].dispicnum = 0;
+        hittype[i].floorz = sector[SECT].floorz;
+        hittype[i].ceilingz = sector[SECT].ceilingz;
+
+        hittype[i].lastvx = 0;
+        hittype[i].lastvy = 0;
+        hittype[i].actorstayput = -1;
+
+        T1 = T2 = T3 = T4 = T5 = T6 = 0;
+
+        if( PN != SPEAKER && PN != LETTER && PN != DUCK && PN != TARGET && PN != TRIPBOMB && PN != VIEWSCREEN && PN != VIEWSCREEN2 && (CS&48) )
+            if( !(PN >= CRACK1 && PN <= CRACK4) )
+        {
+            if(SS == 127) return i;
+            if( wallswitchcheck(i) == 1 && (CS&16) )
+            {
+                if( PN != ACCESSSWITCH && PN != ACCESSSWITCH2 && sprite[i].pal)
+                {
+                    if( (ud.multimode < 2) || (ud.multimode > 1 && ud.coop==1) )
+                    {
+                        sprite[i].xrepeat = sprite[i].yrepeat = 0;
+                        sprite[i].cstat = SLT = SHT = 0;
+                        return i;
+                    }
+                }
+                CS |= 257;
+                if( sprite[i].pal && PN != ACCESSSWITCH && PN != ACCESSSWITCH2)
+                    sprite[i].pal = 0;
+                return i;
+            }
+
+            if( SHT )
+            {
+                changespritestat(i,12);
+                CS |=  257;
+                SH = impact_damage;
+                return i;
+            }
+        }
+
+        s = PN;
+
+        if( CS&1 ) CS |= 256;
+
+        if( actorscrptr[s] )
+        {
+            SH = *(actorscrptr[s]);
+            T5 = *(actorscrptr[s]+1);
+            T2 = *(actorscrptr[s]+2);
+            if( *(actorscrptr[s]+3) && SHT == 0 )
+                SHT = *(actorscrptr[s]+3);
+        }
+        else T2 = T5 = 0;
+    }
+
+    sp = &sprite[i];
+    sect = sp->sectnum;
+
+    switch(sp->picnum)
+    {
+            default:
+
+                if( actorscrptr[sp->picnum] )
+                {
+                    if( j == -1 && sp->lotag > ud.player_skill )
+                    {
+                        sp->xrepeat=sp->yrepeat=0;
+                        changespritestat(i,5);
+                        break;
+                    }
+
+                        //  Init the size
+                    if(sp->xrepeat == 0 || sp->yrepeat == 0)
+                        sp->xrepeat = sp->yrepeat = 1;
+
+                    if( actortype[sp->picnum] & 3)
+                    {
+                        if( ud.monsters_off == 1 )
+                        {
+                            sp->xrepeat=sp->yrepeat=0;
+                            changespritestat(i,5);
+                            break;
+                        }
+
+                        makeitfall(i);
+
+                        if( actortype[sp->picnum] & 2)
+                            hittype[i].actorstayput = sp->sectnum;
+
+                        ps[myconnectindex].max_actors_killed++;
+                        sp->clipdist = 80;
+                        if(j >= 0)
+                        {
+                            if(sprite[j].picnum == RESPAWN)
+                                hittype[i].tempang = sprite[i].pal = sprite[j].pal;
+                            changespritestat(i,1);
+                        }
+                        else changespritestat(i,2);
+                    }
+                    else
+                    {
+                        sp->clipdist = 40;
+                        sp->owner = i;
+                        changespritestat(i,1);
+                    }
+
+                    hittype[i].timetosleep = 0;
+
+                    if(j >= 0)
+                        sp->ang = sprite[j].ang;
+                }
+                break;
+            case FOF:
+                sp->xrepeat = sp->yrepeat = 0;
+                changespritestat(i,5);
+                break;
+            case WATERSPLASH2:
+                if(j >= 0)
+                {
+                    setsprite(i,sprite[j].x,sprite[j].y,sprite[j].z);
+                    sp->xrepeat = sp->yrepeat = 8+(TRAND&7);
+                }
+                else sp->xrepeat = sp->yrepeat = 16+(TRAND&15);
+
+                sp->shade = -16;
+                sp->cstat |= 128;
+                if(j >= 0)
+                {
+                    if(sector[sprite[j].sectnum].lotag == 2)
+                    {
+                        sp->z = getceilzofslope(SECT,SX,SY)+(16<<8);
+                        sp->cstat |= 8;
+                    }
+                    else if( sector[sprite[j].sectnum].lotag == 1)
+                        sp->z = getflorzofslope(SECT,SX,SY);
+                }
+
+                if(sector[sect].floorpicnum == FLOORSLIME ||
+                    sector[sect].ceilingpicnum == FLOORSLIME)
+                        sp->pal = 7;
+            case NEON1:
+            case NEON2:
+            case NEON3:
+            case NEON4:
+            case NEON5:
+            case NEON6:
+            case DOMELITE:
+                if(sp->picnum != WATERSPLASH2)
+                    sp->cstat |= 257;
+            case NUKEBUTTON:
+                if(sp->picnum == DOMELITE)
+                    sp->cstat |= 257;
+            case JIBS1:
+            case JIBS2:
+            case JIBS3:
+            case JIBS4:
+            case JIBS5:
+            case JIBS6:
+            case HEADJIB1:
+            case ARMJIB1:
+            case LEGJIB1:
+            case LIZMANHEAD1:
+            case LIZMANARM1:
+            case LIZMANLEG1:
+            case DUKETORSO:
+            case DUKEGUN:
+            case DUKELEG:
+                changespritestat(i,5);
+                break;
+            case TONGUE:
+                if(j >= 0)
+                    sp->ang = sprite[j].ang;
+                sp->z -= 38<<8;
+                sp->zvel = 256-(TRAND&511);
+                sp->xvel = 64-(TRAND&127);
+                changespritestat(i,4);
+                break;
+            case NATURALLIGHTNING:
+                sp->cstat &= ~257;
+                sp->cstat |= 32768;
+                break;
+            case TRANSPORTERSTAR:
+            case TRANSPORTERBEAM:
+                if(j == -1) break;
+                if(sp->picnum == TRANSPORTERBEAM)
+                {
+                    sp->xrepeat = 31;
+                    sp->yrepeat = 1;
+                    sp->z = sector[sprite[j].sectnum].floorz-(40<<8);
+                }
+                else
+                {
+                    if(sprite[j].statnum == 4)
+                    {
+                        sp->xrepeat = 8;
+                        sp->yrepeat = 8;
+                    }
+                    else
+                    {
+                        sp->xrepeat = 48;
+                        sp->yrepeat = 64;
+                        if(sprite[j].statnum == 10 || badguy(&sprite[j]) )
+                            sp->z -= (32<<8);
+                    }
+                }
+
+                sp->shade = -127;
+                sp->cstat = 128|2;
+                sp->ang = sprite[j].ang;
+
+                sp->xvel = 128;
+                changespritestat(i,5);
+                ssp(i,CLIPMASK0);
+                setsprite(i,sp->x,sp->y,sp->z);
+                break;
+
+            case FRAMEEFFECT1:
+			case FRAMEEFFECT1_13CON:
+				if(j >= 0)
+                {
+                    sp->xrepeat = sprite[j].xrepeat;
+                    sp->yrepeat = sprite[j].yrepeat;
+                    T2 = sprite[j].picnum;
+                }
+                else sp->xrepeat = sp->yrepeat = 0;
+
+                changespritestat(i,5);
+
+                break;
+
+            case LASERLINE:
+                sp->yrepeat = 6;
+                sp->xrepeat = 32;
+
+                if(lasermode == 1)
+                    sp->cstat = 16 + 2;
+                else if(lasermode == 0 || lasermode == 2)
+                    sp->cstat = 16;
+                else
+                {
+                    sp->xrepeat = 0;
+                    sp->yrepeat = 0;
+                }
+
+                if(j >= 0) sp->ang = hittype[j].temp_data[5]+512;
+                changespritestat(i,5);
+                break;
+
+            case FORCESPHERE:
+                if(j == -1 )
+                {
+                    sp->cstat = (short) 32768;
+                    changespritestat(i,2);
+                }
+                else
+                {
+                    sp->xrepeat = sp->yrepeat = 1;
+                    changespritestat(i,5);
+                }
+                break;
+
+            case BLOOD:
+               sp->xrepeat = sp->yrepeat = 16;
+               sp->z -= (26<<8);
+               if( j >= 0 && sprite[j].pal == 6 )
+                   sp->pal = 6;
+               changespritestat(i,5);
+               break;
+            case BLOODPOOL:
+            case PUKE:
+                {
+                    short s1;
+                    s1 = sp->sectnum;
+
+                    updatesector(sp->x+108,sp->y+108,&s1);
+                    if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                    {
+                        updatesector(sp->x-108,sp->y-108,&s1);
+                        if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                        {
+                            updatesector(sp->x+108,sp->y-108,&s1);
+                            if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                            {
+                                updatesector(sp->x-108,sp->y+108,&s1);
+                                if(s1 >= 0 && sector[s1].floorz != sector[sp->sectnum].floorz)
+                                { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
+                            }
+                            else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
+                        }
+                        else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
+                    }
+                    else { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
+                }
+
+                if( sector[SECT].lotag == 1 )
+                {
+                    changespritestat(i,5);
+                    break;
+                }
+
+                if(j >= 0 && sp->picnum != PUKE)
+                {
+                    if( sprite[j].pal == 1)
+                        sp->pal = 1;
+                    else if( sprite[j].pal != 6 && sprite[j].picnum != NUKEBARREL && sprite[j].picnum != TIRE )
+                    {
+                        if(sprite[j].picnum == FECES)
+                            sp->pal = 7; // Brown
+                        else sp->pal = 2; // Red
+                    }
+                    else sp->pal = 0;  // green
+
+                    if(sprite[j].picnum == TIRE)
+                        sp->shade = 127;
+                }
+                sp->cstat |= 32;
+            case FECES:
+                if( j >= 0)
+                    sp->xrepeat = sp->yrepeat = 1;
+                changespritestat(i,5);
+                break;
+
+            case BLOODSPLAT1:
+            case BLOODSPLAT2:
+            case BLOODSPLAT3:
+            case BLOODSPLAT4:
+                sp->cstat |= 16;
+                sp->xrepeat = 7+(TRAND&7);
+                sp->yrepeat = 7+(TRAND&7);
+                sp->z -= (16<<8);
+                if(j >= 0 && sprite[j].pal == 6)
+                    sp->pal = 6;
+                insertspriteq(i);
+                changespritestat(i,5);
+                break;
+
+            case TRIPBOMB:
+                if( sp->lotag > ud.player_skill )
+                {
+                    sp->xrepeat=sp->yrepeat=0;
+                    changespritestat(i,5);
+                    break;
+                }
+
+                sp->xrepeat=4;
+                sp->yrepeat=5;
+
+                sp->owner = i;
+                sp->hitag = i;
+
+                sp->xvel = 16;
+                ssp(i,CLIPMASK0);
+                hittype[i].temp_data[0] = 17;
+                hittype[i].temp_data[2] = 0;
+                hittype[i].temp_data[5] = sp->ang;
+
+            case SPACEMARINE:
+                if(sp->picnum == SPACEMARINE)
+                {
+                    sp->extra = 20;
+                    sp->cstat |= 257;
+                }
+                changespritestat(i,2);
+                break;
+
+            case HYDRENT:
+            case PANNEL1:
+            case PANNEL2:
+            case SATELITE:
+            case FUELPOD:
+            case SOLARPANNEL:
+            case ANTENNA:
+            case GRATE1:
+            case CHAIR1:
+            case CHAIR2:
+            case CHAIR3:
+            case BOTTLE1:
+            case BOTTLE2:
+            case BOTTLE3:
+            case BOTTLE4:
+            case BOTTLE5:
+            case BOTTLE6:
+            case BOTTLE7:
+            case BOTTLE8:
+            case BOTTLE10:
+            case BOTTLE11:
+            case BOTTLE12:
+            case BOTTLE13:
+            case BOTTLE14:
+            case BOTTLE15:
+            case BOTTLE16:
+            case BOTTLE17:
+            case BOTTLE18:
+            case BOTTLE19:
+            case OCEANSPRITE1:
+            case OCEANSPRITE2:
+            case OCEANSPRITE3:
+            case OCEANSPRITE5:
+            case MONK:
+            case INDY:
+            case LUKE:
+            case JURYGUY:
+            case SCALE:
+            case VACUUM:
+            case FANSPRITE:
+            case CACTUS:
+            case CACTUSBROKE:
+            case HANGLIGHT:
+            case FETUS:
+            case FETUSBROKE:
+            case CAMERALIGHT:
+            case MOVIECAMERA:
+            case IVUNIT:
+            case POT1:
+            case POT2:
+            case POT3:
+            case TRIPODCAMERA:
+            case SUSHIPLATE1:
+            case SUSHIPLATE2:
+            case SUSHIPLATE3:
+            case SUSHIPLATE4:
+            case SUSHIPLATE5:
+            case WAITTOBESEATED:
+            case VASE:
+            case PIPE1:
+            case PIPE2:
+            case PIPE3:
+            case PIPE4:
+            case PIPE5:
+            case PIPE6:
+                sp->clipdist = 32;
+                sp->cstat |= 257;
+            case OCEANSPRITE4:
+                changespritestat(i,0);
+                break;
+            case FEMMAG1:
+            case FEMMAG2:
+                sp->cstat &= ~257;
+                changespritestat(i,0);
+                break;
+            case DUKETAG:
+            case SIGN1:
+            case SIGN2:
+                if(ud.multimode < 2 && sp->pal)
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                }
+                else sp->pal = 0;
+                break;
+            case MASKWALL1:
+            case MASKWALL2:
+            case MASKWALL3:
+            case MASKWALL4:
+            case MASKWALL5:
+            case MASKWALL6:
+            case MASKWALL7:
+            case MASKWALL8:
+            case MASKWALL9:
+            case MASKWALL10:
+            case MASKWALL11:
+            case MASKWALL12:
+            case MASKWALL13:
+            case MASKWALL14:
+            case MASKWALL15:
+                j = sp->cstat&60;
+                sp->cstat = j|1;
+                changespritestat(i,0);
+                break;
+            case FOOTPRINTS:
+            case FOOTPRINTS2:
+            case FOOTPRINTS3:
+            case FOOTPRINTS4:
+                if(j >= 0)
+                {
+                    short s1;
+                    s1 = sp->sectnum;
+
+                    updatesector(sp->x+84,sp->y+84,&s1);
+                    if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                    {
+                        updatesector(sp->x-84,sp->y-84,&s1);
+                        if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                        {
+                            updatesector(sp->x+84,sp->y-84,&s1);
+                            if(s1 >= 0 && sector[s1].floorz == sector[sp->sectnum].floorz)
+                            {
+                                updatesector(sp->x-84,sp->y+84,&s1);
+                                if(s1 >= 0 && sector[s1].floorz != sector[sp->sectnum].floorz)
+                                { sp->xrepeat = sp->yrepeat = 0;changespritestat(i,5);break;}
+                            }
+                            else { sp->xrepeat = sp->yrepeat = 0;break;}
+                        }
+                        else { sp->xrepeat = sp->yrepeat = 0;break;}
+                    }
+                    else { sp->xrepeat = sp->yrepeat = 0;break;}
+
+                    sp->cstat = 32+((ps[sprite[j].yvel].footprintcount&1)<<2);
+                    sp->ang = sprite[j].ang;
+                }
+
+                sp->z = sector[sect].floorz;
+                if(sector[sect].lotag != 1 && sector[sect].lotag != 2)
+                    sp->xrepeat = sp->yrepeat = 32;
+
+                insertspriteq(i);
+                changespritestat(i,5);
+                break;
+
+            case FEM1:
+            case FEM2:
+            case FEM3:
+            case FEM4:
+            case FEM5:
+            case FEM6:
+            case FEM7:
+            case FEM8:
+            case FEM9:
+            case FEM10:
+            case PODFEM1:
+            case NAKED1:
+            case STATUE:
+            case TOUGHGAL:
+                sp->yvel = sp->hitag;
+                sp->hitag = -1;
+                if(sp->picnum == PODFEM1) sp->extra <<= 1;
+            case BLOODYPOLE:
+
+            case QUEBALL:
+            case STRIPEBALL:
+
+                if(sp->picnum == QUEBALL || sp->picnum == STRIPEBALL)
+                {
+                    sp->cstat = 256;
+                    sp->clipdist = 8;
+                }
+                else
+                {
+                    sp->cstat |= 257;
+                    sp->clipdist = 32;
+                }
+
+                changespritestat(i,2);
+                break;
+
+            case DUKELYINGDEAD:
+                if(j >= 0 && sprite[j].picnum == APLAYER)
+                {
+                    sp->xrepeat = sprite[j].xrepeat;
+                    sp->yrepeat = sprite[j].yrepeat;
+                    sp->shade = sprite[j].shade;
+                    sp->pal = ps[sprite[j].yvel].palookup;
+                }
+            case DUKECAR:
+            case HELECOPT:
+//                if(sp->picnum == HELECOPT || sp->picnum == DUKECAR) sp->xvel = 1024;
+                sp->cstat = 0;
+                sp->extra = 1;
+                sp->xvel = 292;
+                sp->zvel = 360;
+            case RESPAWNMARKERRED:
+            case BLIMP:
+
+                if(sp->picnum == RESPAWNMARKERRED)
+                {
+                    sp->xrepeat = sp->yrepeat = 24;
+                    if(j >= 0) sp->z = hittype[j].floorz; // -(1<<4);
+                }
+                else
+                {
+                    sp->cstat |= 257;
+                    sp->clipdist = 128;
+                }
+            case MIKE:
+                if(sp->picnum == MIKE)
+                    sp->yvel = sp->hitag;
+            case WEATHERWARN:
+                changespritestat(i,1);
+                break;
+
+            case SPOTLITE:
+                T1 = sp->x;
+                T2 = sp->y;
+                break;
+            case BULLETHOLE:
+                sp->xrepeat = sp->yrepeat = 3;
+                sp->cstat = 16+(TRAND&12);
+                insertspriteq(i);
+            case MONEY:
+            case MAIL:
+            case PAPER:
+                if( sp->picnum == MONEY || sp->picnum == MAIL || sp->picnum == PAPER )
+                {
+                    hittype[i].temp_data[0] = TRAND&2047;
+                    sp->cstat = TRAND&12;
+                    sp->xrepeat = sp->yrepeat = 8;
+                    sp->ang = TRAND&2047;
+                }
+                changespritestat(i,5);
+                break;
+
+            case VIEWSCREEN:
+            case VIEWSCREEN2:
+                sp->owner = i;
+                sp->lotag = 1;
+                sp->extra = 1;
+                changespritestat(i,6);
+                break;
+
+            case SHELL: //From the player
+            case SHOTGUNSHELL:
+                if( j >= 0 )
+                {
+                    short snum,a;
+
+                    if(sprite[j].picnum == APLAYER)
+                    {
+                        snum = sprite[j].yvel;
+                        a = ps[snum].ang-(TRAND&63)+8;  //Fine tune
+
+                        T1 = TRAND&1;
+                        if(sp->picnum == SHOTGUNSHELL)
+                            sp->z = (6<<8)+ps[snum].pyoff+ps[snum].posz-((ps[snum].horizoff+ps[snum].horiz-100)<<4);
+                        else sp->z = (3<<8)+ps[snum].pyoff+ps[snum].posz-((ps[snum].horizoff+ps[snum].horiz-100)<<4);
+                        sp->zvel = -(TRAND&255);
+                    }
+                    else
+                    {
+                        a = sp->ang;
+                        sp->z = sprite[j].z-PHEIGHT+(3<<8);
+                    }
+
+                    sp->x = sprite[j].x+(sintable[(a+512)&2047]>>7);
+                    sp->y = sprite[j].y+(sintable[a&2047]>>7);
+
+                    sp->shade = -8;
+
+                    sp->ang = a-512;
+                    sp->xvel = 20;
+
+					//  do not try to make it 0 when ud.hideweapon Will make OOS when shooting in water
+					sp->xrepeat=sp->yrepeat=4; 	
+
+                    changespritestat(i,5);
+                }
+                break;
+
+            case RESPAWN:
+                sp->extra = 66-13;
+            case MUSICANDSFX:
+                if( ud.multimode < 2 && sp->pal == 1)
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+                sp->cstat = (short)32768;
+                changespritestat(i,11);
+                break;
+
+            case EXPLOSION2:
+            case EXPLOSION2BOT:
+            case BURNING:
+            case BURNING2:
+            case SMALLSMOKE:
+            case SHRINKEREXPLOSION:
+            case COOLEXPLOSION1:
+
+                if(j >= 0)
+                {
+                    sp->ang = sprite[j].ang;
+                    sp->shade = -64;
+                    sp->cstat = 128|(TRAND&4);
+                }
+
+                if(sp->picnum == EXPLOSION2 || sp->picnum == EXPLOSION2BOT)
+                {
+                    sp->xrepeat = 48;
+                    sp->yrepeat = 48;
+                    sp->shade = -127;
+                    sp->cstat |= 128;
+                }
+                else if(sp->picnum == SHRINKEREXPLOSION )
+                {
+                    sp->xrepeat = 32;
+                    sp->yrepeat = 32;
+                }
+                else if( sp->picnum == SMALLSMOKE )
+                {
+                    // 64 "money"
+                    sp->xrepeat = 24;
+                    sp->yrepeat = 24;
+                }
+                else if(sp->picnum == BURNING || sp->picnum == BURNING2)
+                {
+                    sp->xrepeat = 4;
+                    sp->yrepeat = 4;
+                }
+
+                if(j >= 0)
+                {
+                    x = getflorzofslope(sp->sectnum,sp->x,sp->y);
+                    if(sp->z > x-(12<<8) )
+                        sp->z = x-(12<<8);
+                }
+
+                changespritestat(i,5);
+
+                break;
+
+            case PLAYERONWATER:
+                if(j >= 0)
+                {
+                    sp->xrepeat = sprite[j].xrepeat;
+                    sp->yrepeat = sprite[j].yrepeat;
+                    sp->zvel = 128;
+                    if(sector[sp->sectnum].lotag != 2)
+                        sp->cstat |= 32768;
+                }
+                changespritestat(i,13);
+                break;
+
+            case APLAYER:
+                sp->xrepeat = sp->yrepeat = 0;
+                j = ud.coop;
+                if(j == 2) j = 0;
+
+                if( ud.multimode < 2 || (ud.multimode > 1 && j != sp->lotag) )
+                    changespritestat(i,5);
+                else
+                    changespritestat(i,10);
+                break;
+            case WATERBUBBLE:
+                if(j >= 0 && sprite[j].picnum == APLAYER)
+                    sp->z -= (16<<8);
+                if( sp->picnum == WATERBUBBLE)
+                {
+                    if( j >= 0 )
+                        sp->ang = sprite[j].ang;
+                    sp->xrepeat = sp->yrepeat = 4;
+                }
+                else sp->xrepeat = sp->yrepeat = 32;
+
+                changespritestat(i,5);
+                break;
+
+            case CRANE:
+
+                sp->cstat |= 64|257;
+
+                sp->picnum += 2;
+                sp->z = sector[sect].ceilingz+(48<<8);
+                T5 = tempwallptr;
+
+                msx[tempwallptr] = sp->x;
+                msy[tempwallptr] = sp->y;
+                msx[tempwallptr+2] = sp->z;
+
+                s = headspritestat[0];
+                while(s >= 0)
+                {
+                    if( sprite[s].picnum == CRANEPOLE && SHT == (sprite[s].hitag) )
+                    {
+                        msy[tempwallptr+2] = s;
+
+                        T2 = sprite[s].sectnum;
+
+                        sprite[s].xrepeat = 48;
+                        sprite[s].yrepeat = 128;
+
+                        msx[tempwallptr+1] = sprite[s].x;
+                        msy[tempwallptr+1] = sprite[s].y;
+
+                        sprite[s].x = sp->x;
+                        sprite[s].y = sp->y;
+                        sprite[s].z = sp->z;
+                        sprite[s].shade = sp->shade;
+
+                        setsprite(s,sprite[s].x,sprite[s].y,sprite[s].z);
+                        break;
+                    }
+                    s = nextspritestat[s];
+                }
+
+                tempwallptr += 3;
+                sp->owner = -1;
+                sp->extra = 8;
+                changespritestat(i,6);
+                break;
+
+            case WATERDRIP:
+                if(j >= 0 && sprite[j].statnum == 10 || sprite[j].statnum == 1)
+                {
+                    sp->shade = 32;
+                    if(sprite[j].pal != 1)
+                    {
+                        sp->pal = 2;
+                        sp->z -= (18<<8);
+                    }
+                    else sp->z -= (13<<8);
+                    sp->ang = getangle(ps[connecthead].posx-sp->x,ps[connecthead].posy-sp->y);
+                    sp->xvel = 48-(TRAND&31);
+                    ssp(i,CLIPMASK0);
+                }
+                else if(j == -1)
+                {
+                    sp->z += (4<<8);
+                    T1 = sp->z;
+                    T2 = TRAND&127;
+                }
+            case TRASH:
+
+                if(sp->picnum != WATERDRIP)
+                    sp->ang = TRAND&2047;
+
+            case WATERDRIPSPLASH:
+
+                sp->xrepeat = 24;
+                sp->yrepeat = 24;
+
+
+                changespritestat(i,6);
+                break;
+
+            case PLUG:
+                sp->lotag = 9999;
+                changespritestat(i,6);
+                break;
+            case TOUCHPLATE:
+                T3 = sector[sect].floorz;
+                if(sector[sect].lotag != 1 && sector[sect].lotag != 2)
+                    sector[sect].floorz = sp->z;
+                if(sp->pal && ud.multimode > 1)
+                {
+                    sp->xrepeat=sp->yrepeat=0;
+                    changespritestat(i,5);
+                    break;
+                }
+            case WATERBUBBLEMAKER:
+                sp->cstat |= 32768;
+                changespritestat(i,6);
+                break;
+            case BOLT1:
+            case BOLT1+1:
+            case BOLT1+2:
+            case BOLT1+3:
+            case SIDEBOLT1:
+            case SIDEBOLT1+1:
+            case SIDEBOLT1+2:
+            case SIDEBOLT1+3:
+                T1 = sp->xrepeat;
+                T2 = sp->yrepeat;
+            case MASTERSWITCH:
+                if(sp->picnum == MASTERSWITCH)
+                    sp->cstat |= 32768;
+                sp->yvel = 0;
+                changespritestat(i,6);
+                break;
+            case TARGET:
+            case DUCK:
+            case LETTER:
+                sp->extra = 1;
+                sp->cstat |= 257;
+                changespritestat(i,1);
+                break;
+            case OCTABRAINSTAYPUT:
+            case LIZTROOPSTAYPUT:
+            case PIGCOPSTAYPUT:
+            case LIZMANSTAYPUT:
+            case BOSS1STAYPUT:
+            case PIGCOPDIVE:
+            case COMMANDERSTAYPUT:
+            case BOSS4STAYPUT:
+                hittype[i].actorstayput = sp->sectnum;
+            case BOSS1:
+            case BOSS2:
+            case BOSS3:
+            case BOSS4:
+            case ROTATEGUN:
+            case GREENSLIME:
+                if(sp->picnum == GREENSLIME)
+                    sp->extra = 1;
+            case DRONE:
+            case LIZTROOPONTOILET:
+            case LIZTROOPJUSTSIT:
+            case LIZTROOPSHOOT:
+            case LIZTROOPJETPACK:
+            case LIZTROOPDUCKING:
+            case LIZTROOPRUNNING:
+            case LIZTROOP:
+            case OCTABRAIN:
+            case COMMANDER:
+            case PIGCOP:
+            case LIZMAN:
+            case LIZMANSPITTING:
+            case LIZMANFEEDING:
+            case LIZMANJUMP:
+            case ORGANTIC:
+            case RAT:
+            case SHARK:
+
+                if(sp->pal == 0)
+                {
+                    switch(sp->picnum)
+                    {
+                        case LIZTROOPONTOILET:
+                        case LIZTROOPSHOOT:
+                        case LIZTROOPJETPACK:
+                        case LIZTROOPDUCKING:
+                        case LIZTROOPRUNNING:
+                        case LIZTROOPSTAYPUT:
+                        case LIZTROOPJUSTSIT:
+                        case LIZTROOP:
+                            sp->pal = 22;
+                            break;
+                    }
+                }
+
+                if( sp->picnum == BOSS4STAYPUT || sp->picnum == BOSS1 || sp->picnum == BOSS2 || sp->picnum == BOSS1STAYPUT || sp->picnum == BOSS3 || sp->picnum == BOSS4 )
+                {
+                    if(j >= 0 && sprite[j].picnum == RESPAWN)
+                        sp->pal = sprite[j].pal;
+                    if(sp->pal)
+                    {
+                        sp->clipdist = 80;
+                        sp->xrepeat = 40;
+                        sp->yrepeat = 40;
+                    }
+                    else
+                    {
+                        sp->xrepeat = 80;
+                        sp->yrepeat = 80;
+                        sp->clipdist = 164;
+                    }
+                }
+                else
+                {
+                    if(sp->picnum != SHARK)
+                    {
+                        sp->xrepeat = 40;
+                        sp->yrepeat = 40;
+                        sp->clipdist = 80;
+                    }
+                    else
+                    {
+                        sp->xrepeat = 60;
+                        sp->yrepeat = 60;
+                        sp->clipdist = 40;
+                    }
+                }
+
+                if(j >= 0) sp->lotag = 0;
+
+                if( ( sp->lotag > ud.player_skill ) || ud.monsters_off == 1 )
+                {
+                    sp->xrepeat=sp->yrepeat=0;
+                    changespritestat(i,5);
+                    break;
+                }
+                else
+                {
+                    makeitfall(i);
+
+                    if(sp->picnum == RAT)
+                    {
+                        sp->ang = TRAND&2047;
+                        sp->xrepeat = sp->yrepeat = 48;
+                        sp->cstat = 0;
+                    }
+                    else
+                    {
+                        sp->cstat |= 257;
+
+                        if(sp->picnum != SHARK)
+                            ps[myconnectindex].max_actors_killed++;
+                    }
+
+                    if(sp->picnum == ORGANTIC) sp->cstat |= 128;
+
+                    if(j >= 0)
+                    {
+                        hittype[i].timetosleep = 0;
+                        check_fta_sounds(i);
+                        changespritestat(i,1);
+                    }
+                    else changespritestat(i,2);
+                }
+
+                if(sp->picnum == ROTATEGUN)
+                    sp->zvel = 0;
+
+                break;
+
+            case LOCATORS:
+                sp->cstat |= 32768;
+                changespritestat(i,7);
+                break;
+
+            case ACTIVATORLOCKED:
+            case ACTIVATOR:
+                sp->cstat = (short) 32768;
+                if(sp->picnum == ACTIVATORLOCKED)
+                    sector[sp->sectnum].lotag |= 16384;
+                changespritestat(i,8);
+                break;
+
+            case DOORSHOCK:
+                sp->cstat |= 1+256;
+                sp->shade = -12;
+                changespritestat(i,6);
+                break;
+
+            case OOZ:
+            case OOZ2:
+                sp->shade = -12;
+
+                if(j >= 0)
+                {
+                    if( sprite[j].picnum == NUKEBARREL )
+                        sp->pal = 8;
+                    insertspriteq(i);
+                }
+
+                changespritestat(i,1);
+
+                getglobalz(i);
+
+                j = (hittype[i].floorz-hittype[i].ceilingz)>>9;
+
+                sp->yrepeat = j;
+                sp->xrepeat = 25-(j>>1);
+                sp->cstat |= (TRAND&4);
+
+                break;
+
+            case HEAVYHBOMB:
+                if(j >= 0)
+                    sp->owner = j;
+                else sp->owner = i;
+                sp->xrepeat = sp->yrepeat = 9;
+                sp->yvel = 4;
+            case REACTOR2:
+            case REACTOR:
+            case RECON:
+
+                if(sp->picnum == RECON)
+                {
+                    if( sp->lotag > ud.player_skill )
+                    {
+                        sp->xrepeat = sp->yrepeat = 0;
+                        changespritestat(i,5);
+                        return i;
+                    }
+                    ps[myconnectindex].max_actors_killed++;
+                    hittype[i].temp_data[5] = 0;
+                    if(ud.monsters_off == 1)
+                    {
+                        sp->xrepeat = sp->yrepeat = 0;
+                        changespritestat(i,5);
+                        break;
+                    }
+                    sp->extra = 130;
+                }
+
+                if(sp->picnum == REACTOR || sp->picnum == REACTOR2)
+                    sp->extra = impact_damage;
+
+                CS |= 257; // Make it hitable
+
+                if( ud.multimode < 2 && sp->pal != 0)
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+                sp->pal = 0;
+                SS = -17;
+
+                changespritestat(i,2);
+                break;
+
+            case ATOMICHEALTH:
+            case STEROIDS:
+            case HEATSENSOR:
+            case SHIELD:
+            case AIRTANK:
+            case TRIPBOMBSPRITE:
+            case JETPACK:
+            case HOLODUKE:
+
+            case FIRSTGUNSPRITE:
+            case CHAINGUNSPRITE:
+            case SHOTGUNSPRITE:
+            case RPGSPRITE:
+            case SHRINKERSPRITE:
+            case FREEZESPRITE:
+            case DEVISTATORSPRITE:
+
+            case SHOTGUNAMMO:
+            case FREEZEAMMO:
+            case HBOMBAMMO:
+            case CRYSTALAMMO:
+            case GROWAMMO:
+            case BATTERYAMMO:
+            case DEVISTATORAMMO:
+            case RPGAMMO:
+            case BOOTS:
+            case AMMO:
+            case AMMOLOTS:
+            case COLA:
+            case FIRSTAID:
+            case SIXPAK:
+                if(j >= 0)
+                {
+                    sp->lotag = 0;
+                    sp->z -= (32<<8);
+                    sp->zvel = -1024;
+                    ssp(i,CLIPMASK0);
+                    sp->cstat = TRAND&4;
+                }
+                else
+                {
+                    sp->owner = i;
+                    sp->cstat = 0;
+                }
+
+                if( ( ud.multimode < 2 && sp->pal != 0) || (sp->lotag > ud.player_skill) )
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+
+                sp->pal = 0;
+
+            case ACCESSCARD:
+
+                if(sp->picnum == ATOMICHEALTH)
+                    sp->cstat |= 128;
+
+                if(ud.multimode > 1 && ud.coop != 1 && sp->picnum == ACCESSCARD)
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+                else
+                {
+                    if(sp->picnum == AMMO)
+                        sp->xrepeat = sp->yrepeat = 16;
+                    else sp->xrepeat = sp->yrepeat = 32;
+                }
+
+                sp->shade = -17;
+
+                if(j >= 0) changespritestat(i,1);
+                else
+                {
+                    changespritestat(i,2);
+                    makeitfall(i);
+                }
+                break;
+
+            case WATERFOUNTAIN:
+                SLT = 1;
+
+            case TREE1:
+            case TREE2:
+            case TIRE:
+            case CONE:
+            case BOX:
+                CS = 257; // Make it hitable
+                sprite[i].extra = 1;
+                changespritestat(i,6);
+                break;
+
+            case FLOORFLAME:
+                sp->shade = -127;
+                changespritestat(i,6);
+                break;
+
+            case BOUNCEMINE:
+                sp->owner = i;
+                sp->cstat |= 1+256; //Make it hitable
+                sp->xrepeat = sp->yrepeat = 24;
+                sp->shade = -127;
+                sp->extra = impact_damage<<2;
+                changespritestat(i,2);
+                break;
+
+            case CAMERA1:
+            case CAMERA1+1:
+            case CAMERA1+2:
+            case CAMERA1+3:
+            case CAMERA1+4:
+            case CAMERAPOLE:
+                sp->extra = 1;
+
+                if(camerashitable) sp->cstat = 257;
+                else sp->cstat = 0;
+
+            case GENERICPOLE:
+
+                if( ud.multimode < 2 && sp->pal != 0 )
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+                else sp->pal = 0;
+                if(sp->picnum == CAMERAPOLE || sp->picnum == GENERICPOLE) break;
+                sp->picnum = CAMERA1;
+                changespritestat(i,1);
+                break;
+            case STEAM:
+                if(j >= 0)
+                {
+                    sp->ang = sprite[j].ang;
+                    sp->cstat = 16+128+2;
+                    sp->xrepeat=sp->yrepeat=1;
+                    sp->xvel = -8;
+                    ssp(i,CLIPMASK0);
+                }
+            case CEILINGSTEAM:
+                changespritestat(i,6);
+                break;
+
+            case SECTOREFFECTOR:
+                sp->yvel = sector[sect].extra;
+                sp->cstat |= 32768;
+                sp->xrepeat = sp->yrepeat = 0;
+
+                switch(sp->lotag)
+                {
+                    case 28:
+                        T6 = 65;// Delay for lightning
+                        break;
+                    case 7: // Transporters!!!!
+                    case 23:// XPTR END
+                        if(sp->lotag != 23)
+                        {
+                            for(j=0;j<MAXSPRITES;j++)
+                                if(sprite[j].statnum < MAXSTATUS && sprite[j].picnum == SECTOREFFECTOR && ( sprite[j].lotag == 7 || sprite[j].lotag == 23 ) && i != j && sprite[j].hitag == SHT)
+                                {
+                                    OW = j;
+                                    break;
+                                }
+                        }
+                        else OW = i;
+
+                        T5 = sector[sect].floorz == SZ;
+                        sp->cstat = 0;
+                        changespritestat(i,9);
+                        return i;
+                    case 1:
+                        sp->owner = -1;
+                        T1 = 1;
+                        break;
+                    case 18:
+
+                        if(sp->ang == 512)
+                        {
+                            T2 = sector[sect].ceilingz;
+                            if(sp->pal)
+                                sector[sect].ceilingz = sp->z;
+                        }
+                        else
+                        {
+                            T2 = sector[sect].floorz;
+                            if(sp->pal)
+                                sector[sect].floorz = sp->z;
+                        }
+
+                        sp->hitag <<= 2;
+                        break;
+
+                    case 19:
+                        sp->owner = -1;
+                        break;
+                    case 25: // Pistons
+                        T4 = sector[sect].ceilingz;
+                        T5 = 1;
+                        sector[sect].ceilingz = sp->z;
+                        setinterpolation(&sector[sect].ceilingz);
+                        break;
+                    case 35:
+                        sector[sect].ceilingz = sp->z;
+                        break;
+                    case 27:
+                        if(ud.recstat == 1)
+                        {
+                            sp->xrepeat=sp->yrepeat=64;
+                            sp->cstat &= 32767;
+                        }
+                        break;
+                    case 12:
+
+                        T2 = sector[sect].floorshade;
+                        T3 = sector[sect].ceilingshade;
+                        break;
+
+                    case 13:
+
+                        T1 = sector[sect].ceilingz;
+                        T2 = sector[sect].floorz;
+
+                        if( klabs(T1-sp->z) < klabs(T2-sp->z) )
+                            sp->owner = 1;
+                        else sp->owner = 0;
+
+                        if(sp->ang == 512)
+                        {
+                            if(sp->owner)
+                                sector[sect].ceilingz = sp->z;
+                            else
+                                sector[sect].floorz = sp->z;
+                        }
+                        else
+                            sector[sect].ceilingz = sector[sect].floorz = sp->z;
+
+                        if( sector[sect].ceilingstat&1 )
+                        {
+                            sector[sect].ceilingstat ^= 1;
+                            T4 = 1;
+
+                            if(!sp->owner && sp->ang==512)
+                            {
+                                sector[sect].ceilingstat ^= 1;
+                                T4 = 0;
+                            }
+
+                            sector[sect].ceilingshade =
+                                sector[sect].floorshade;
+
+                            if(sp->ang==512)
+                            {
+                                startwall = sector[sect].wallptr;
+                                endwall = startwall+sector[sect].wallnum;
+                                for(j=startwall;j<endwall;j++)
+                                {
+                                    x = wall[j].nextsector;
+                                    if(x >= 0)
+                                        if( !(sector[x].ceilingstat&1) )
+                                    {
+                                        sector[sect].ceilingpicnum =
+                                            sector[x].ceilingpicnum;
+                                        sector[sect].ceilingshade =
+                                            sector[x].ceilingshade;
+                                        break; //Leave earily
+                                    }
+                                }
+                            }
+                        }
+
+                        break;
+
+                    case 17:
+
+                        T3 = sector[sect].floorz; //Stopping loc
+
+                        j = nextsectorneighborz(sect,sector[sect].floorz,-1,-1);
+                        T4 = sector[j].ceilingz;
+
+                        j = nextsectorneighborz(sect,sector[sect].ceilingz,1,1);
+                        T5 = sector[j].floorz;
+
+                        if(numplayers < 2)
+                        {
+                            setinterpolation(&sector[sect].floorz);
+                            setinterpolation(&sector[sect].ceilingz);
+                        }
+
+                        break;
+
+                    case 24:
+                        sp->yvel <<= 1;
+                    case 36:
+                        break;
+
+                    case 20:
+                    {
+                        long q;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        //find the two most clostest wall x's and y's
+                        q = 0x7fffffff;
+
+                        for(s=startwall;s<endwall;s++)
+                        {
+                            x = wall[s].x;
+                            y = wall[s].y;
+
+                            d = FindDistance2D(sp->x-x,sp->y-y);
+                            if( d < q )
+                            {
+                                q = d;
+                                clostest = s;
+                            }
+                        }
+
+                        T2 = clostest;
+
+                        q = 0x7fffffff;
+
+                        for(s=startwall;s<endwall;s++)
+                        {
+                            x = wall[s].x;
+                            y = wall[s].y;
+
+                            d = FindDistance2D(sp->x-x,sp->y-y);
+                            if(d < q && s != T2)
+                            {
+                                q = d;
+                                clostest = s;
+                            }
+                        }
+
+                        T3 = clostest;
+                    }
+
+                    break;
+
+                    case 3:
+
+                        T4=sector[sect].floorshade;
+
+                        sector[sect].floorshade = sp->shade;
+                        sector[sect].ceilingshade = sp->shade;
+
+                        sp->owner = sector[sect].ceilingpal<<8;
+                        sp->owner |= sector[sect].floorpal;
+
+                        //fix all the walls;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        for(s=startwall;s<endwall;s++)
+                        {
+                            if(!(wall[s].hitag&1))
+                                wall[s].shade=sp->shade;
+                            if( (wall[s].cstat&2) && wall[s].nextwall >= 0)
+                                wall[wall[s].nextwall].shade = sp->shade;
+                        }
+                        break;
+
+                    case 31:
+                        T2 = sector[sect].floorz;
+                    //    T3 = sp->hitag;
+                        if(sp->ang != 1536) sector[sect].floorz = sp->z;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        for(s=startwall;s<endwall;s++)
+                            if(wall[s].hitag == 0) wall[s].hitag = 9999;
+
+                        setinterpolation(&sector[sect].floorz);
+
+                        break;
+                    case 32:
+                        T2 = sector[sect].ceilingz;
+                        T3 = sp->hitag;
+                        if(sp->ang != 1536) sector[sect].ceilingz = sp->z;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        for(s=startwall;s<endwall;s++)
+                            if(wall[s].hitag == 0) wall[s].hitag = 9999;
+
+                        setinterpolation(&sector[sect].ceilingz);
+
+                        break;
+
+                    case 4: //Flashing lights
+
+                        T3 = sector[sect].floorshade;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        sp->owner = sector[sect].ceilingpal<<8;
+                        sp->owner |= sector[sect].floorpal;
+
+                        for(s=startwall;s<endwall;s++)
+                            if(wall[s].shade > T4)
+                                T4 = wall[s].shade;
+
+                        break;
+
+                    case 9:
+                        if( sector[sect].lotag &&
+                            labs(sector[sect].ceilingz-sp->z) > 1024)
+                                sector[sect].lotag |= 32768; //If its open
+                    case 8:
+                        //First, get the ceiling-floor shade
+
+                        T1 = sector[sect].floorshade;
+                        T2 = sector[sect].ceilingshade;
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        for(s=startwall;s<endwall;s++)
+                            if(wall[s].shade > T3)
+                                T3 = wall[s].shade;
+
+                        T4 = 1; //Take Out;
+
+                        break;
+
+                    case 11://Pivitor rotater
+                        if(sp->ang>1024) T4 = 2;
+                        else T4 = -2;
+                    case 0:
+                    case 2://Earthquakemakers
+                    case 5://Boss Creature
+                    case 6://Subway
+                    case 14://Caboos
+                    case 15://Subwaytype sliding door
+                    case 16://That rotating blocker reactor thing
+                    case 26://ESCELATOR
+                    case 30://No rotational subways
+
+                        if(sp->lotag == 0)
+                        {
+                            if( sector[sect].lotag == 30 )
+                            {
+                                if(sp->pal) sprite[i].clipdist = 1;
+                                else sprite[i].clipdist = 0;
+                                T4 = sector[sect].floorz;
+                                sector[sect].hitag = i;
+                            }
+
+                            for(j = 0;j < MAXSPRITES;j++)
+                            {
+                                if( sprite[j].statnum < MAXSTATUS )
+                                if( sprite[j].picnum == SECTOREFFECTOR &&
+                                    sprite[j].lotag == 1 &&
+                                    sprite[j].hitag == sp->hitag)
+                                {
+                                    if( sp->ang == 512 )
+                                    {
+                                        sp->x = sprite[j].x;
+                                        sp->y = sprite[j].y;
+                                    }
+                                    break;
+                                }
+                            }
+                            if(j == MAXSPRITES)
+                            {
+                                sprintf(tempbuf,"Found lonely Sector Effector (lotag 0) at (%ld,%ld)\n",sp->x,sp->y);
+                                gameexit(tempbuf);
+                            }
+                            sp->owner = j;
+                        }
+
+                        startwall = sector[sect].wallptr;
+                        endwall = startwall+sector[sect].wallnum;
+
+                        T2 = tempwallptr;
+                        for(s=startwall;s<endwall;s++)
+                        {
+                            msx[tempwallptr] = wall[s].x-sp->x;
+                            msy[tempwallptr] = wall[s].y-sp->y;
+                            tempwallptr++;
+                            if(tempwallptr > 2047)
+                            {
+                                sprintf(tempbuf,"Too many moving sectors at (%ld,%ld).\n",wall[s].x,wall[s].y);
+                                gameexit(tempbuf);
+                            }
+                        }
+                        if( sp->lotag == 30 || sp->lotag == 6 || sp->lotag == 14 || sp->lotag == 5 )
+                        {
+
+                            startwall = sector[sect].wallptr;
+                            endwall = startwall+sector[sect].wallnum;
+
+                            if(sector[sect].hitag == -1)
+                                sp->extra = 0;
+                            else sp->extra = 1;
+
+                            sector[sect].hitag = i;
+
+                            j = 0;
+
+                            for(s=startwall;s<endwall;s++)
+                            {
+                                if( wall[ s ].nextsector >= 0 &&
+                                    sector[ wall[ s ].nextsector].hitag == 0 &&
+                                        sector[ wall[ s ].nextsector].lotag < 3 )
+                                    {
+                                        s = wall[s].nextsector;
+                                        j = 1;
+                                        break;
+                                    }
+                            }
+
+                            if(j == 0)
+                            {
+                                sprintf(tempbuf,"Subway found no zero'd sectors with locators\nat (%ld,%ld).\n",sp->x,sp->y);
+                                gameexit(tempbuf);
+                            }
+
+                            sp->owner = -1;
+                            T1 = s;
+
+                            if(sp->lotag != 30)
+                                T4 = sp->hitag;
+                        }
+
+                        else if(sp->lotag == 16)
+                            T4 = sector[sect].ceilingz;
+
+                        else if( sp->lotag == 26 )
+                        {
+                            T4 = sp->x;
+                            T5 = sp->y;
+                            if(sp->shade==sector[sect].floorshade) //UP
+                                sp->zvel = -256;
+                            else
+                                sp->zvel = 256;
+
+                            sp->shade = 0;
+                        }
+                        else if( sp->lotag == 2)
+                        {
+                            T6 = sector[sp->sectnum].floorheinum;
+                            sector[sp->sectnum].floorheinum = 0;
+                        }
+                }
+
+                switch(sp->lotag)
+                {
+                    case 6:
+                    case 14:
+                        j = callsound(sect,i);
+                        if(j == -1) j = SUBWAY;
+                        hittype[i].lastvx = j;
+                    case 30:
+                        if(numplayers > 1) break;
+                    case 0:
+                    case 1:
+                    case 5:
+                    case 11:
+                    case 15:
+                    case 16:
+                    case 26:
+                        setsectinterpolate(i);
+                        break;
+                }
+
+                switch(sprite[i].lotag)
+                {
+                    case 40:
+                    case 41:
+                    case 43:
+                    case 44:
+                    case 45:
+                        changespritestat(i,15);
+                        break;
+                    default:
+                        changespritestat(i,3);
+                        break;
+                }
+
+                break;
+
+
+            case SEENINE:
+            case OOZFILTER:
+
+                sp->shade = -16;
+                if(sp->xrepeat <= 8)
+                {
+                    sp->cstat = (short)32768;
+                    sp->xrepeat=sp->yrepeat=0;
+                }
+                else sp->cstat = 1+256;
+                sp->extra = impact_damage<<2;
+                sp->owner = i;
+
+                changespritestat(i,6);
+                break;
+
+            case CRACK1:
+            case CRACK2:
+            case CRACK3:
+            case CRACK4:
+            case FIREEXT:
+                if(sp->picnum == FIREEXT)
+                {
+                    sp->cstat = 257;
+                    sp->extra = impact_damage<<2;
+                }
+                else
+                {
+                    sp->cstat |= 17;
+                    sp->extra = 1;
+                }
+
+                if( ud.multimode < 2 && sp->pal != 0)
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                    break;
+                }
+
+                sp->pal = 0;
+                sp->owner = i;
+                changespritestat(i,6);
+                sp->xvel = 8;
+                ssp(i,CLIPMASK0);
+                break;
+
+            case TOILET:
+            case STALL:
+                sp->lotag = 1;
+                sp->cstat |= 257;
+                sp->clipdist = 8;
+                sp->owner = i;
+                break;
+            case CANWITHSOMETHING:
+            case CANWITHSOMETHING2:
+            case CANWITHSOMETHING3:
+            case CANWITHSOMETHING4:
+            case RUBBERCAN:
+                sp->extra = 0;
+            case EXPLODINGBARREL:
+            case HORSEONSIDE:
+            case FIREBARREL:
+            case NUKEBARREL:
+            case FIREVASE:
+            case NUKEBARRELDENTED:
+            case NUKEBARRELLEAKED:
+            case WOODENHORSE:
+
+                if(j >= 0)
+                    sp->xrepeat = sp->yrepeat = 32;
+                sp->clipdist = 72;
+                makeitfall(i);
+                if(j >= 0)
+                    sp->owner = j;
+                else sp->owner = i;
+            case EGG:
+                if( ud.monsters_off == 1 && sp->picnum == EGG )
+                {
+                    sp->xrepeat = sp->yrepeat = 0;
+                    changespritestat(i,5);
+                }
+                else
+                {
+                    if(sp->picnum == EGG)
+                        sp->clipdist = 24;
+                    sp->cstat = 257|(TRAND&4);
+                    changespritestat(i,2);
+                }
+                break;
+            case TOILETWATER:
+                sp->shade = -16;
+                changespritestat(i,6);
+                break;
+    }
+    return i;
+}
+
+
+void animatesprites(long x,long y,short a,long smoothratio)
+{
+    short i, j, k, p, sect;
+    long l, t1,t3,t4;
+    spritetype *s,*t;
+
+    for(j=0;j < spritesortcnt; j++)
+    {
+        t = &tsprite[j];
+        i = t->owner;
+        s = &sprite[t->owner];
+
+        switch(t->picnum)
+        {
+            case BLOODPOOL:
+            case PUKE:
+            case FOOTPRINTS:
+            case FOOTPRINTS2:
+            case FOOTPRINTS3:
+            case FOOTPRINTS4:
+                if(t->shade == 127) continue;
+                break;
+            case RESPAWNMARKERRED:
+            case RESPAWNMARKERYELLOW:
+            case RESPAWNMARKERGREEN:
+                if(ud.marker == 0)
+                    t->xrepeat = t->yrepeat = 0;
+                continue;
+            case CHAIR3:
+
+                k = (((t->ang+3072+128-a)&2047)>>8)&7;
+                if(k>4)
+                {
+                    k = 8-k;
+                    t->cstat |= 4;
+                }
+                else t->cstat &= ~4;
+                t->picnum = s->picnum+k;
+                break;
+            case BLOODSPLAT1:
+            case BLOODSPLAT2:
+            case BLOODSPLAT3:
+            case BLOODSPLAT4:
+                if(ud.lockout) t->xrepeat = t->yrepeat = 0;
+                else if(t->pal == 6)
+                {
+                    t->shade = -127;
+                    continue;
+                }
+            case BULLETHOLE:
+            case CRACK1:
+            case CRACK2:
+            case CRACK3:
+            case CRACK4:
+                t->shade = 16;
+                continue;
+            case NEON1:
+            case NEON2:
+            case NEON3:
+            case NEON4:
+            case NEON5:
+            case NEON6:
+                continue;
+            case GREENSLIME:
+            case GREENSLIME+1:
+            case GREENSLIME+2:
+            case GREENSLIME+3:
+            case GREENSLIME+4:
+            case GREENSLIME+5:
+            case GREENSLIME+6:
+            case GREENSLIME+7:
+                break;
+            default:
+                if( ( (t->cstat&16) ) || ( badguy(t) && t->extra > 0) || t->statnum == 10)
+                    continue;
+        }
+
+        if (sector[t->sectnum].ceilingstat&1)
+            l = sector[t->sectnum].ceilingshade;
+        else
+            l = sector[t->sectnum].floorshade;
+
+        if(l < -127) l = -127;
+        if(l > 128) l =  127;
+        t->shade = l;
+    }
+
+
+    for(j=0;j < spritesortcnt; j++ )  //Between drawrooms() and drawmasks()
+    {                             //is the perfect time to animate sprites
+        t = &tsprite[j];
+        i = t->owner;
+        s = &sprite[i];
+
+        switch(s->picnum)
+        {
+            case SECTOREFFECTOR:
+                if(t->lotag == 27 && ud.recstat == 1)
+                {
+                    t->picnum = 11+((totalclock>>3)&1);
+                    t->cstat |= 128;
+                }
+                else
+                    t->xrepeat = t->yrepeat = 0;
+                break;
+            case NATURALLIGHTNING:
+               t->shade = -127;
+               break;
+            case FEM1:
+            case FEM2:
+            case FEM3:
+            case FEM4:
+            case FEM5:
+            case FEM6:
+            case FEM7:
+            case FEM8:
+            case FEM9:
+            case FEM10:
+            case MAN:
+            case MAN2:
+            case WOMAN:
+            case NAKED1:
+            case PODFEM1:
+            case FEMMAG1:
+            case FEMMAG2:
+            case FEMPIC1:
+            case FEMPIC2:
+            case FEMPIC3:
+            case FEMPIC4:
+            case FEMPIC5:
+            case FEMPIC6:
+            case FEMPIC7:
+            case BLOODYPOLE:
+            case FEM6PAD:
+            case STATUE:
+            case STATUEFLASH:
+            case OOZ:
+            case OOZ2:
+            case WALLBLOOD1:
+            case WALLBLOOD2:
+            case WALLBLOOD3:
+            case WALLBLOOD4:
+            case WALLBLOOD5:
+            case WALLBLOOD7:
+            case WALLBLOOD8:
+            case SUSHIPLATE1:
+            case SUSHIPLATE2:
+            case SUSHIPLATE3:
+            case SUSHIPLATE4:
+            case FETUS:
+            case FETUSJIB:
+            case FETUSBROKE:
+            case HOTMEAT:
+            case FOODOBJECT16:
+            case DOLPHIN1:
+            case DOLPHIN2:
+            case TOUGHGAL:
+            case TAMPON:
+            case XXXSTACY:
+            case 4946:
+            case 4947:
+            case 693:
+            case 2254:
+            case 4560:
+            case 4561:
+            case 4562:
+            case 4498:
+            case 4957:
+                if(ud.lockout)
+                {
+                    t->xrepeat = t->yrepeat = 0;
+                    continue;
+                }
+        }
+
+        if( t->statnum == 99 ) continue;
+        if( s->statnum != 1 && s->picnum == APLAYER && ps[s->yvel].newowner == -1 && s->owner >= 0 )
+        {
+            t->x -= mulscale16(65536-smoothratio,ps[s->yvel].posx-ps[s->yvel].oposx);
+            t->y -= mulscale16(65536-smoothratio,ps[s->yvel].posy-ps[s->yvel].oposy);
+            t->z = ps[s->yvel].oposz + mulscale16(smoothratio,ps[s->yvel].posz-ps[s->yvel].oposz);
+            t->z += (40<<8);
+        }
+        else if( ( s->statnum == 0 && s->picnum != CRANEPOLE) || s->statnum == 10 || s->statnum == 6 || s->statnum == 4 || s->statnum == 5 || s->statnum == 1 )
+        {
+            t->x -= mulscale16(65536-smoothratio,s->x-hittype[i].bposx);
+            t->y -= mulscale16(65536-smoothratio,s->y-hittype[i].bposy);
+            t->z -= mulscale16(65536-smoothratio,s->z-hittype[i].bposz);
+        }
+
+        sect = s->sectnum;
+        t1 = T2;t3 = T4;t4 = T5;
+
+        switch(s->picnum)
+        {
+            case DUKELYINGDEAD:
+                t->z += (24<<8);
+                break;
+            case BLOODPOOL:
+            case FOOTPRINTS:
+            case FOOTPRINTS2:
+            case FOOTPRINTS3:
+            case FOOTPRINTS4:
+                if(t->pal == 6)
+                    t->shade = -127;
+            case PUKE:
+            case MONEY:
+            case MONEY+1:
+            case MAIL:
+            case MAIL+1:
+            case PAPER:
+            case PAPER+1:
+                if(ud.lockout && s->pal == 2)
+                {
+                    t->xrepeat = t->yrepeat = 0;
+                    continue;
+                }
+                break;
+            case TRIPBOMB:
+                continue;
+            case FORCESPHERE:
+                if(t->statnum == 5)
+                {
+                    short sqa,sqb;
+
+                    sqa =
+                        getangle(
+                            sprite[s->owner].x-ps[screenpeek].posx,
+                            sprite[s->owner].y-ps[screenpeek].posy);
+                    sqb =
+                        getangle(
+                            sprite[s->owner].x-t->x,
+                            sprite[s->owner].y-t->y);
+
+                    if( klabs(getincangle(sqa,sqb)) > 512 )
+                        if( ldist(&sprite[s->owner],t) < ldist(&sprite[ps[screenpeek].i],&sprite[s->owner]) )
+                            t->xrepeat = t->yrepeat = 0;
+                }
+                continue;
+            case BURNING:
+            case BURNING2:
+                if( sprite[s->owner].statnum == 10 )
+                {
+                    if( display_mirror == 0 && sprite[s->owner].yvel == screenpeek && ps[sprite[s->owner].yvel].over_shoulder_on == 0 )
+                        t->xrepeat = 0;
+                    else
+                    {
+                        t->ang = getangle(x-t->x,y-t->y);
+                        t->x = sprite[s->owner].x;
+                        t->y = sprite[s->owner].y;
+                        t->x += sintable[(t->ang+512)&2047]>>10;
+                        t->y += sintable[t->ang&2047]>>10;
+                    }
+                }
+                break;
+
+            case ATOMICHEALTH:
+                t->z -= (4<<8);
+                break;
+            case CRYSTALAMMO:
+                t->shade = (sintable[(totalclock<<4)&2047]>>10);
+                continue;
+            case VIEWSCREEN:
+            case VIEWSCREEN2:
+                if(camsprite >= 0 && hittype[OW].temp_data[0] == 1)
+                {
+                    t->picnum = STATIC;
+                    t->cstat |= (rand()&12);
+                    t->xrepeat += 8;
+                    t->yrepeat += 8;
+                }
+                break;
+
+            case SHRINKSPARK:
+                t->picnum = SHRINKSPARK+( (totalclock>>4)&3 );
+                break;
+            case GROWSPARK:
+                t->picnum = GROWSPARK+( (totalclock>>4)&3 );
+                break;
+            case RPG:
+                 k = getangle(s->x-x,s->y-y);
+                 k = (((s->ang+3072+128-k)&2047)/170);
+                 if(k > 6)
+                 {
+                    k = 12-k;
+                    t->cstat |= 4;
+                 }
+                 else t->cstat &= ~4;
+                 t->picnum = RPG+k;
+                 break;
+
+            case RECON:
+
+                k = getangle(s->x-x,s->y-y);
+                if( T1 < 4 )
+                    k = (((s->ang+3072+128-k)&2047)/170);
+                else k = (((s->ang+3072+128-k)&2047)/170);
+
+                if(k>6)
+                {
+                    k = 12-k;
+                    t->cstat |= 4;
+                }
+                else t->cstat &= ~4;
+
+                if( klabs(t3) > 64 ) k += 7;
+                t->picnum = RECON+k;
+
+                break;
+
+            case APLAYER:
+
+                p = s->yvel;
+
+                if(t->pal == 1) t->z -= (18<<8);
+
+                if(ps[p].over_shoulder_on > 0 && ps[p].newowner < 0 )
+                {
+                    t->cstat |= 2;
+                    if ( screenpeek == myconnectindex && numplayers >= 2 )
+                    {
+                        t->x = omyx+mulscale16((long)(myx-omyx),smoothratio);
+                        t->y = omyy+mulscale16((long)(myy-omyy),smoothratio);
+                        t->z = omyz+mulscale16((long)(myz-omyz),smoothratio)+(40<<8);
+                        t->ang = omyang+mulscale16((long)(((myang+1024-omyang)&2047)-1024),smoothratio);
+                        t->sectnum = mycursectnum;
+                    }
+                }
+
+                if( ( display_mirror == 1 || screenpeek != p || s->owner == -1 ) && ud.multimode > 1 && ud.showweapons && sprite[ps[p].i].extra > 0 && ps[p].curr_weapon > 0 )
+                {
+                    memcpy((spritetype *)&tsprite[spritesortcnt],(spritetype *)t,sizeof(spritetype));
+
+                    tsprite[spritesortcnt].statnum = 99;
+
+                    tsprite[spritesortcnt].yrepeat = ( t->yrepeat>>3 );
+                    if(t->yrepeat < 4) t->yrepeat = 4;
+
+                    tsprite[spritesortcnt].shade = t->shade;
+                    tsprite[spritesortcnt].cstat = 0;
+
+                    switch(ps[p].curr_weapon)
+                    {
+                        case PISTOL_WEAPON:      tsprite[spritesortcnt].picnum = FIRSTGUNSPRITE;       break;
+                        case SHOTGUN_WEAPON:     tsprite[spritesortcnt].picnum = SHOTGUNSPRITE;        break;
+                        case CHAINGUN_WEAPON:    tsprite[spritesortcnt].picnum = CHAINGUNSPRITE;       break;
+                        case RPG_WEAPON:         tsprite[spritesortcnt].picnum = RPGSPRITE;            break;
+                        case HANDREMOTE_WEAPON:
+                        case HANDBOMB_WEAPON:    tsprite[spritesortcnt].picnum = HEAVYHBOMB;           break;
+                        case TRIPBOMB_WEAPON:    tsprite[spritesortcnt].picnum = TRIPBOMBSPRITE;       break;
+                        case GROW_WEAPON:        tsprite[spritesortcnt].picnum = GROWSPRITEICON;       break;
+                        case SHRINKER_WEAPON:    tsprite[spritesortcnt].picnum = SHRINKERSPRITE;       break;
+                        case FREEZE_WEAPON:      tsprite[spritesortcnt].picnum = FREEZESPRITE;         break;
+                        case DEVISTATOR_WEAPON:  tsprite[spritesortcnt].picnum = DEVISTATORSPRITE;     break;
+                    }
+
+                    if(s->owner >= 0)
+                        tsprite[spritesortcnt].z = ps[p].posz-(12<<8);
+                    else tsprite[spritesortcnt].z = s->z-(51<<8);
+                    if(ps[p].curr_weapon == HANDBOMB_WEAPON)
+                    {
+                        tsprite[spritesortcnt].xrepeat = 10;
+                        tsprite[spritesortcnt].yrepeat = 10;
+                    }
+                    else
+                    {
+                        tsprite[spritesortcnt].xrepeat = 16;
+                        tsprite[spritesortcnt].yrepeat = 16;
+                    }
+                    tsprite[spritesortcnt].pal = 0;
+                    spritesortcnt++;
+                }
+
+                if(s->owner == -1)
+                {
+                    k = (((s->ang+3072+128-a)&2047)>>8)&7;
+                    if(k>4)
+                    {
+                        k = 8-k;
+                        t->cstat |= 4;
+                    }
+                    else t->cstat &= ~4;
+
+                    if(sector[t->sectnum].lotag == 2) k += 1795-1405;
+                    else if( (hittype[i].floorz-s->z) > (64<<8) ) k += 60;
+
+                    t->picnum += k;
+                    t->pal = ps[p].palookup;
+
+                    goto PALONLY;
+                }
+
+                if( ps[p].on_crane == -1 && (sector[s->sectnum].lotag&0x7ff) != 1 )
+                {
+                    l = s->z-hittype[ps[p].i].floorz+(3<<8);
+                    if( l > 1024 && s->yrepeat > 32 && s->extra > 0 )
+                        s->yoffset = (signed char)(l/(s->yrepeat<<2));
+                    else s->yoffset=0;
+                }
+
+                if(ps[p].newowner > -1)
+                {
+                    t4 = *(actorscrptr[APLAYER]+1);
+                    t3 = 0;
+                    t1 = *(actorscrptr[APLAYER]+2);
+                }
+
+                if(ud.camerasprite == -1 && ps[p].newowner == -1)
+                    if(s->owner >= 0 && display_mirror == 0 && ps[p].over_shoulder_on == 0 )
+                        if( ud.multimode < 2 || ( ud.multimode > 1 && p == screenpeek ) )
+                {
+                    t->owner = -1;
+                    t->xrepeat = t->yrepeat = 0;
+                    continue;
+                }
+
+                PALONLY:
+
+                if( sector[sect].floorpal )
+                    t->pal = sector[sect].floorpal;
+
+                if(s->owner == -1) continue;
+
+                if( t->z > hittype[i].floorz && t->xrepeat < 32 )
+                    t->z = hittype[i].floorz;
+
+                break;
+
+            case JIBS1:
+            case JIBS2:
+            case JIBS3:
+            case JIBS4:
+            case JIBS5:
+            case JIBS6:
+            case HEADJIB1:
+            case LEGJIB1:
+            case ARMJIB1:
+            case LIZMANHEAD1:
+            case LIZMANARM1:
+            case LIZMANLEG1:
+            case DUKELEG:
+            case DUKEGUN:
+            case DUKETORSO:
+                if(ud.lockout)
+                {
+                    t->xrepeat = t->yrepeat = 0;
+                    continue;
+                }
+                if(t->pal == 6) t->shade = -120;
+
+            case SCRAP1:
+            case SCRAP2:
+            case SCRAP3:
+            case SCRAP4:
+            case SCRAP5:
+            case SCRAP6:
+            case SCRAP6+1:
+            case SCRAP6+2:
+            case SCRAP6+3:
+            case SCRAP6+4:
+            case SCRAP6+5:
+            case SCRAP6+6:
+            case SCRAP6+7:
+
+                if(hittype[i].picnum == BLIMP && t->picnum == SCRAP1 && s->yvel >= 0)
+                    t->picnum = s->yvel;
+                else t->picnum += T1;
+                t->shade -= 6;
+
+                if( sector[sect].floorpal )
+                    t->pal = sector[sect].floorpal;
+                break;
+
+            case WATERBUBBLE:
+                if(sector[t->sectnum].floorpicnum == FLOORSLIME)
+                {
+                    t->pal = 7;
+                    break;
+                }
+            default:
+
+                if( sector[sect].floorpal )
+                    t->pal = sector[sect].floorpal;
+                break;
+        }
+
+        if( actorscrptr[s->picnum] )
+        {
+            if(t4>10000)
+				// FIX_00093: fixed crashbugs in multiplayer (mine/blimp)
+				// This is the mine issue (confusion bug in hittype[i].temp_data[4] usage)
+				// close to blimp bug (search for BLIMP)
+				// -> t4 aka macro T5 is incremented at DETONATEB: in actor.c
+				// for a time counter. Instead we want an address.
+				// Issue happens in confessn.map (do a dnclip + dnkroz + dncoords,
+				// start with duke3d_w32 /m /q2 -map confessn.map)
+				// go through the Guilty logo till x = -2932, y = 42174, z = 18416.
+				// blow up the bomb. Wait in the water. Look at the respawn sign
+				// at the bottom of the chain. Crashes when it's about to respawn.
+				// Lame fix. ok for w32. Doesn't work for other plateform.
+				// How to make a differene between a timer and an address??
+            {
+                l = *(long *)(t4+8);
+
+                switch( l )
+                {
+                    case 2:
+                        k = (((s->ang+3072+128-a)&2047)>>8)&1;
+                        break;
+
+                    case 3:
+                    case 4:
+                        k = (((s->ang+3072+128-a)&2047)>>7)&7;
+                        if(k > 3)
+                        {
+                            t->cstat |= 4;
+                            k = 7-k;
+                        }
+                        else t->cstat &= ~4;
+                        break;
+
+                    case 5:
+                        k = getangle(s->x-x,s->y-y);
+                        k = (((s->ang+3072+128-k)&2047)>>8)&7;
+                        if(k>4)
+                        {
+                            k = 8-k;
+                            t->cstat |= 4;
+                        }
+                        else t->cstat &= ~4;
+                        break;
+                    case 7:
+                        k = getangle(s->x-x,s->y-y);
+                        k = (((s->ang+3072+128-k)&2047)/170);
+                        if(k>6)
+                        {
+                            k = 12-k;
+                            t->cstat |= 4;
+                        }
+                        else t->cstat &= ~4;
+                        break;
+                    case 8:
+                        k = (((s->ang+3072+128-a)&2047)>>8)&7;
+                        t->cstat &= ~4;
+                        break;
+                    default:
+                        k = 0;
+                        break;
+                }
+
+                t->picnum += k + ( *(long *)t4 ) + l * t3;
+
+                if(l > 0) while(tilesizx[t->picnum] == 0 && t->picnum > 0 )
+                    t->picnum -= l;       //Hack, for actors
+
+                if( hittype[i].dispicnum >= 0)
+                    hittype[i].dispicnum = t->picnum;
+            }
+            else if(display_mirror == 1)
+                t->cstat |= 4;
+        }
+
+        if( s->statnum == 13 || badguy(s) || (s->picnum == APLAYER && s->owner >= 0) )
+            if(t->statnum != 99 && s->picnum != EXPLOSION2 && s->picnum != HANGLIGHT && s->picnum != DOMELITE)
+                if(s->picnum != HOTMEAT)
+        {
+            if( hittype[i].dispicnum < 0 )
+            {
+                hittype[i].dispicnum++;
+                continue;
+            }
+            else if( ud.shadows && spritesortcnt < (MAXSPRITESONSCREEN-2))
+            {
+                long daz,xrep,yrep;
+
+                if( (sector[sect].lotag&0xff) > 2 || s->statnum == 4 || s->statnum == 5 || s->picnum == DRONE || s->picnum == COMMANDER )
+                    daz = sector[sect].floorz;
+                else
+                    daz = hittype[i].floorz;
+
+                if( (s->z-daz) < (8<<8) )
+                    if( ps[screenpeek].posz < daz )
+                {
+                    memcpy((spritetype *)&tsprite[spritesortcnt],(spritetype *)t,sizeof(spritetype));
+
+                    tsprite[spritesortcnt].statnum = 99;
+
+                    tsprite[spritesortcnt].yrepeat = ( t->yrepeat>>3 );
+                    if(t->yrepeat < 4) t->yrepeat = 4;
+
+                    tsprite[spritesortcnt].shade = 127;
+                    tsprite[spritesortcnt].cstat |= 2;
+
+                    tsprite[spritesortcnt].z = daz;
+                    xrep = tsprite[spritesortcnt].xrepeat;// - (klabs(daz-t->z)>>11);
+                    tsprite[spritesortcnt].xrepeat = xrep;
+                    tsprite[spritesortcnt].pal = 4;
+
+                    yrep = tsprite[spritesortcnt].yrepeat;// - (klabs(daz-t->z)>>11);
+                    tsprite[spritesortcnt].yrepeat = yrep;
+                    spritesortcnt++;
+                }
+            }
+
+            if( ps[screenpeek].heat_amount > 0 && ps[screenpeek].heat_on )
+            {
+                t->pal = 6;
+                t->shade = 0;
+            }
+        }
+
+
+        switch(s->picnum)
+        {
+            case LASERLINE:
+                if(sector[t->sectnum].lotag == 2) t->pal = 8;
+                t->z = sprite[s->owner].z-(3<<8);
+                if(lasermode == 2 && ps[screenpeek].heat_on == 0 )
+                    t->yrepeat = 0;
+            case EXPLOSION2:
+            case EXPLOSION2BOT:
+            case FREEZEBLAST:
+            case ATOMICHEALTH:
+            case FIRELASER:
+            case SHRINKSPARK:
+            case GROWSPARK:
+            case CHAINGUN:
+            case SHRINKEREXPLOSION:
+            case RPG:
+            case FLOORFLAME:
+                if(t->picnum == EXPLOSION2)
+                {
+                    ps[screenpeek].visibility = -127;
+                    lastvisinc = totalclock+32;
+                    restorepalette = 1;
+                }
+                t->shade = -127;
+                break;
+            case FIRE:
+            case FIRE2:
+            case BURNING:
+            case BURNING2:
+                if( sprite[s->owner].picnum != TREE1 && sprite[s->owner].picnum != TREE2 )
+                    t->z = sector[t->sectnum].floorz;
+                t->shade = -127;
+                break;
+            case COOLEXPLOSION1:
+                t->shade = -127;
+                t->picnum += (s->shade>>1);
+                break;
+            case PLAYERONWATER:
+
+                k = (((t->ang+3072+128-a)&2047)>>8)&7;
+                if(k>4)
+                {
+                    k = 8-k;
+                    t->cstat |= 4;
+                }
+                else t->cstat &= ~4;
+
+                t->picnum = s->picnum+k+((T1<4)*5);
+                t->shade = sprite[s->owner].shade;
+
+                break;
+
+            case WATERSPLASH2:
+                t->picnum = WATERSPLASH2+t1;
+                break;
+            case REACTOR2:
+                t->picnum = s->picnum + T3;
+                break;
+            case SHELL:
+                t->picnum = s->picnum+(T1&1);
+            case SHOTGUNSHELL:
+                t->cstat |= 12;
+                if(T1 > 1) t->cstat &= ~4;
+                if(T1 > 2) t->cstat &= ~12;
+                break;
+            case FRAMEEFFECT1:
+			case FRAMEEFFECT1_13CON:
+			if(s->owner >= 0 && sprite[s->owner].statnum < MAXSTATUS)
+                {
+                    if(sprite[s->owner].picnum == APLAYER)
+                        if(ud.camerasprite == -1)
+                            if(screenpeek == sprite[s->owner].yvel && display_mirror == 0)
+                    {
+                        t->owner = -1;
+                        break;
+                    }
+                    if( (sprite[s->owner].cstat&32768) == 0 )
+                    {
+                        t->picnum = hittype[s->owner].dispicnum;
+                        t->pal = sprite[s->owner].pal;
+                        t->shade = sprite[s->owner].shade;
+                        t->ang = sprite[s->owner].ang;
+                        t->cstat = 2|sprite[s->owner].cstat;
+                    }
+                }
+                break;
+            
+            case CAMERA1:
+            case RAT:
+                k = (((t->ang+3072+128-a)&2047)>>8)&7;
+                if(k>4)
+                {
+                    k = 8-k;
+                    t->cstat |= 4;
+                }
+                else t->cstat &= ~4;
+                t->picnum = s->picnum+k;
+                break;
+        }
+
+        hittype[i].dispicnum = t->picnum;
+        if(sector[t->sectnum].floorpicnum == MIRROR)
+            t->xrepeat = t->yrepeat = 0;
+    }
+}
+
+
+
+#define NUMCHEATCODES 26
+char cheatquotes[NUMCHEATCODES][14] = {
+    {"cornholio"},	// 0
+    {"stuff"},		// 1
+    {"scotty###"},	// 2
+    {"coords"},		// 3
+    {"view"},		// 4
+    {"time"},		// 5
+    {"unlock"},		// 6
+    {"cashman"},	// 7 
+    {"items"},		// 8
+    {"rate"},		// 9
+    {"skill#"},		// 10
+    {"beta"},		// 11
+    {"hyper"},		// 12
+    {"monsters"},	// 13
+    {"<RESERVED>"},	// 14
+    {"<RESERVED>"},	// 15
+    {"todd"},		// 16
+    {"showmap"},	// 17
+    {"kroz"},		// 18
+    {"allen"},		// 19
+    {"clip"},		// 20
+    {"weapons"},	// 21
+    {"inventory"},	// 22
+    {"keys"},		// 23
+    {"debug"}		// 24
+//    {"ending"}
+
+};
+
+
+char cheatbuf[10],cheatbuflen;
+void cheats(void)
+{
+    short ch, i, j, k, keystate, weapon;
+
+    if( (ps[myconnectindex].gm&MODE_TYPE) || (ps[myconnectindex].gm&MODE_MENU))
+        return;
+
+#ifdef BETA
+    return;
+#endif
+
+    if ( ps[myconnectindex].cheat_phase == 1)
+    {
+       while (KB_KeyWaiting())
+       {
+          ch = KB_Getch();
+          ch = tolower(ch);
+
+          if( !( (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ) )
+          {
+             ps[myconnectindex].cheat_phase = 0;
+//             FTA(46,&ps[myconnectindex]);
+             return;
+          }
+
+          cheatbuf[cheatbuflen++] = ch;
+          cheatbuf[cheatbuflen] = 0;
+
+          if(cheatbuflen > 11)
+          {
+              ps[myconnectindex].cheat_phase = 0;
+              return;
+          }
+
+          for(k = 0;k < NUMCHEATCODES;k++)
+          {
+              for(j = 0;j<cheatbuflen;j++)
+              {
+                  if( cheatbuf[j] == cheatquotes[k][j] || (cheatquotes[k][j] == '#' && ch >= '0' && ch <= '9') )
+                  {
+                      if( cheatquotes[k][j+1] == 0 ) goto FOUNDCHEAT;
+                      if(j == cheatbuflen-1) return;
+                  }
+                  else break;
+              }
+          }
+
+          ps[myconnectindex].cheat_phase = 0;
+          return;
+
+          FOUNDCHEAT:
+          {
+                switch(k)
+                {
+                    case 0: // cornholio
+                    case 18: // kroz
+
+                        ud.god = 1-ud.god;
+
+                        if(ud.god)
+                        { // set on
+                            pus = 1;
+                            pub = 1;
+                            sprite[ps[myconnectindex].i].cstat = 257;
+
+                            hittype[ps[myconnectindex].i].temp_data[0] = 0;
+                            hittype[ps[myconnectindex].i].temp_data[1] = 0;
+                            hittype[ps[myconnectindex].i].temp_data[2] = 0;
+                            hittype[ps[myconnectindex].i].temp_data[3] = 0;
+                            hittype[ps[myconnectindex].i].temp_data[4] = 0;
+                            hittype[ps[myconnectindex].i].temp_data[5] = 0;
+
+                            sprite[ps[myconnectindex].i].hitag = 0;
+                            sprite[ps[myconnectindex].i].lotag = 0;
+                            sprite[ps[myconnectindex].i].pal =
+                                ps[myconnectindex].palookup;
+
+                            FTA(17,&ps[myconnectindex],1);
+                        }
+                        else // set off
+                        {
+                            ud.god = 0;
+                            sprite[ps[myconnectindex].i].extra = max_player_health;
+                            hittype[ps[myconnectindex].i].extra = -1;
+                            ps[myconnectindex].last_extra = max_player_health;
+                            FTA(18,&ps[myconnectindex],1);
+                        }
+
+                        sprite[ps[myconnectindex].i].extra = max_player_health;
+                        hittype[ps[myconnectindex].i].extra = 0;
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+
+                        return;
+
+                    case 1: // stuff
+
+						if(VOLUMEONE)
+                        	j = 6;
+						else
+                        	j = 0;
+
+                        for ( weapon = PISTOL_WEAPON;weapon < MAX_WEAPONS-j;weapon++ )
+                           ps[myconnectindex].gotweapon[weapon]  = 1;
+
+                        for ( weapon = PISTOL_WEAPON;
+                              weapon < (MAX_WEAPONS-j);
+                              weapon++ )
+                            addammo( weapon, &ps[myconnectindex], max_ammo_amount[weapon] );
+
+                        ps[myconnectindex].ammo_amount[GROW_WEAPON] = 50;
+
+                        ps[myconnectindex].steroids_amount =         400;
+                        ps[myconnectindex].heat_amount     =        1200;
+                        ps[myconnectindex].boot_amount          =    200;
+                        ps[myconnectindex].shield_amount =           100;
+                        ps[myconnectindex].scuba_amount =            6400;
+                        ps[myconnectindex].holoduke_amount =         2400;
+                        ps[myconnectindex].jetpack_amount =          1600;
+                        ps[myconnectindex].firstaid_amount =         max_player_health;
+
+                        ps[myconnectindex].got_access =              7;
+                        FTA(5,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].inven_icon = 1;
+                        return;
+
+                    case 2:  // dnscotty###
+                    case 10: // skill#
+#ifdef ONELEVELDEMO
+    ps[myconnectindex].cheat_phase = 0;
+    break;
+#endif
+
+                        if(k == 2)
+                        {
+                            short volnume,levnume;
+                            volnume = cheatbuf[6] - '0';
+                            levnume = (cheatbuf[7] - '0')*10+(cheatbuf[8]-'0');
+
+                            volnume--;
+                            levnume--;
+							if (VOLUMEONE)
+							{
+								if( volnume > 0 )
+	                            {
+	                                ps[myconnectindex].cheat_phase = 0;
+	                                KB_FlushKeyboardQueue();
+	                                return;
+	                            }
+							}
+
+                            if((volnume > 4)&&PLUTOPAK)
+                            {
+                                ps[myconnectindex].cheat_phase = 0;
+                                KB_FlushKeyboardQueue();
+                                return;
+                            }
+                            else
+
+							if((volnume > 3)&&!PLUTOPAK)
+                            {
+                                ps[myconnectindex].cheat_phase = 0;
+                                KB_FlushKeyboardQueue();
+                                return;
+                            }
+                            else
+
+                            if(volnume == 0)
+                            {
+                                if(levnume > 5)
+                                {
+                                    ps[myconnectindex].cheat_phase = 0;
+                                    KB_FlushKeyboardQueue();
+                                    return;
+                                }
+                            }
+                            else
+                            {
+                                if(levnume >= 11)
+                                {
+                                    ps[myconnectindex].cheat_phase = 0;
+                                    KB_FlushKeyboardQueue();
+                                    return;
+                                }
+                            }
+
+                            ud.m_volume_number = ud.volume_number = volnume;
+                            ud.m_level_number = ud.level_number = levnume;
+
+                        }
+                        else ud.m_player_skill = ud.player_skill =
+                            cheatbuf[5] - '1';
+
+                        if(numplayers > 1 && myconnectindex == connecthead)
+                        {
+                            tempbuf[0] = 5;
+                            tempbuf[1] = ud.m_level_number;
+                            tempbuf[2] = ud.m_volume_number;
+                            tempbuf[3] = ud.m_player_skill;
+                            tempbuf[4] = ud.m_monsters_off;
+                            tempbuf[5] = ud.m_respawn_monsters;
+                            tempbuf[6] = ud.m_respawn_items;
+                            tempbuf[7] = ud.m_respawn_inventory;
+                            tempbuf[8] = ud.m_coop;
+                            tempbuf[9] = ud.m_marker;
+                            tempbuf[10] = ud.m_ffire;
+
+                            for(i=connecthead;i>=0;i=connectpoint2[i])
+                                sendpacket(i,tempbuf,11);
+                        }
+                        else ps[myconnectindex].gm |= MODE_RESTART;
+
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 3: // coords
+                        ps[myconnectindex].cheat_phase = 0;
+                        ud.coords = 1-ud.coords;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 4: // view
+                        if( ps[myconnectindex].over_shoulder_on )
+                            ps[myconnectindex].over_shoulder_on = 0;
+                        else
+                        {
+                            ps[myconnectindex].over_shoulder_on = 1;
+                            cameradist = 0;
+                            cameraclock = totalclock;
+                        }
+                        // FTA(22,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 5: // time
+                        // FTA(21,&ps[myconnectindex]);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+					case 6: // unlock
+                        for(i=numsectors-1;i>=0;i--) //Unlock
+                        {
+                            j = sector[i].lotag;
+                            if(j == -1 || j == 32767) continue;
+                            if( (j & 0x7fff) > 2 )
+                            {
+                                if( j&(0xffff-16384) )
+                                    sector[i].lotag &= (0xffff-16384);
+                                operatesectors(i,ps[myconnectindex].i);
+                            }
+                        }
+                        operateforcefields(ps[myconnectindex].i,-1);
+
+                        FTA(100,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 7: // cashman
+                        ud.cashman = 1-ud.cashman;
+                        KB_ClearKeyDown(sc_N);
+                        ps[myconnectindex].cheat_phase = 0;
+                        return;
+
+                    case 8: // items
+                        ps[myconnectindex].steroids_amount =         400;
+                        ps[myconnectindex].heat_amount     =        1200;
+                        ps[myconnectindex].boot_amount          =    200;
+                        ps[myconnectindex].shield_amount =           100;
+                        ps[myconnectindex].scuba_amount =            6400;
+                        ps[myconnectindex].holoduke_amount =         2400;
+                        ps[myconnectindex].jetpack_amount =          1600;
+
+                        ps[myconnectindex].firstaid_amount =         max_player_health;
+                        ps[myconnectindex].got_access =              7;
+                        FTA(5,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 9: // rate
+                        ud.tickrate ^= 1;
+						vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 11: // beta
+                        FTA(105,&ps[myconnectindex],1);
+                        KB_ClearKeyDown(sc_H);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 12: // hyper
+                        ps[myconnectindex].steroids_amount = 399;
+                        ps[myconnectindex].heat_amount = 1200;
+                        ps[myconnectindex].cheat_phase = 0;
+                        FTA(37,&ps[myconnectindex],1);
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 13: // monsters
+                        if(actor_tog == 3) actor_tog = 0;
+                        actor_tog++;
+                        ps[screenpeek].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 14: // <RESERVED>
+                    case 25: // ??
+                        ud.eog = 1;
+                        ps[myconnectindex].gm |= MODE_EOL;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 15: // <RESERVED>
+                        ps[myconnectindex].gm = MODE_EOL;
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 16: // todd
+                        FTA(99,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                   case 17: // showmap
+                        ud.showallmap = 1-ud.showallmap;
+                        if(ud.showallmap)
+                        {
+                            for(i=0;i<(MAXSECTORS>>3);i++)
+                                show2dsector[i] = 255;
+                            for(i=0;i<(MAXWALLS>>3);i++)
+                                show2dwall[i] = 255;
+                            FTA(111,&ps[myconnectindex],1);
+                        }
+                        else
+                        {
+                            for(i=0;i<(MAXSECTORS>>3);i++)
+                                show2dsector[i] = 0;
+                            for(i=0;i<(MAXWALLS>>3);i++)
+                                show2dwall[i] = 0;
+                            FTA(1,&ps[myconnectindex],1);
+                        }
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_FlushKeyboardQueue();
+                        return;
+
+                    case 19: // allen
+                        FTA(79,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        KB_ClearKeyDown(sc_N);
+                        return;
+					
+					case 20: // clip
+                        ud.clipping = 1-ud.clipping;
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].cheat_phase = 0;
+                        FTA(112+ud.clipping,&ps[myconnectindex],1);
+                        return;
+
+					case 21: // weapons
+						if(VOLUMEONE)
+                        	j = 6;
+						else
+                        	j = 0;
+
+                        for ( weapon = PISTOL_WEAPON;weapon < MAX_WEAPONS-j;weapon++ )
+                        {
+                            addammo( weapon, &ps[myconnectindex], max_ammo_amount[weapon] );
+                            ps[myconnectindex].gotweapon[weapon]  = 1;
+                        }
+
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].cheat_phase = 0;
+                        FTA(119,&ps[myconnectindex],1);
+                        return;
+
+                    case 22: // inventory
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].cheat_phase = 0;
+                        ps[myconnectindex].steroids_amount =         400;
+                        ps[myconnectindex].heat_amount     =        1200;
+                        ps[myconnectindex].boot_amount          =    200;
+                        ps[myconnectindex].shield_amount =           100;
+                        ps[myconnectindex].scuba_amount =            6400;
+                        ps[myconnectindex].holoduke_amount =         2400;
+                        ps[myconnectindex].jetpack_amount =          1600;
+                        ps[myconnectindex].firstaid_amount =         max_player_health;
+                        FTA(120,&ps[myconnectindex],1);
+                        ps[myconnectindex].cheat_phase = 0;
+                        return;
+
+                    case 23: // keys
+                        ps[myconnectindex].got_access =              7;
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].cheat_phase = 0;
+                        FTA(121,&ps[myconnectindex],1);
+                        return;
+
+                    case 24: // debug
+                        debug_on = 1-debug_on;
+                        KB_FlushKeyboardQueue();
+                        ps[myconnectindex].cheat_phase = 0;
+                        break;
+                }
+             }
+          }
+       }
+
+    else
+    {
+        if( KB_KeyPressed(sc_D) )
+        {
+            if( ps[myconnectindex].cheat_phase >= 0 && numplayers < 2 && ud.recstat == 0)
+                ps[myconnectindex].cheat_phase = -1;
+        }
+
+        if( KB_KeyPressed(sc_N) )
+        {
+            if( ps[myconnectindex].cheat_phase == -1 )
+            {
+                if(ud.player_skill == 4)
+                {
+                    FTA(22,&ps[myconnectindex],1);
+                    ps[myconnectindex].cheat_phase = 0;
+                }
+                else
+                {
+                    ps[myconnectindex].cheat_phase = 1;
+//                    FTA(25,&ps[myconnectindex]);
+                    cheatbuflen = 0;
+                }
+                KB_FlushKeyboardQueue();
+            }
+            else if(ps[myconnectindex].cheat_phase != 0)
+            {
+                ps[myconnectindex].cheat_phase = 0;
+                KB_ClearKeyDown(sc_D);
+                KB_ClearKeyDown(sc_N);
+            }
+        }
+    }
+}
+
+
+long nonsharedtimer;
+void nonsharedkeys(void)
+{
+    short i,ch, weapon;
+    long j;
+        
+    if(ud.recstat == 2)
+    {
+        ControlInfo noshareinfo;
+        CONTROL_GetInput( &noshareinfo );
+    }
+
+    if( KB_KeyPressed( sc_F12 ) )
+    {
+        KB_ClearKeyDown( sc_F12 );
+		takescreenshot();
+        // FTA(103,&ps[myconnectindex]); done better in takescreenshot()
+    }
+
+    if( !ALT_IS_PRESSED && ud.overhead_on == 0)
+        {
+            if( ACTION( gamefunc_Enlarge_Screen ) )
+            {
+                CONTROL_ClearAction( gamefunc_Enlarge_Screen );
+                if(ud.screen_size > 0)
+                    sound(THUD);
+
+				// FIX_00027: Added an extra small statusbar (HUD)
+				if (ud.screen_size==4)
+				{
+					ud.extended_screen_size++;
+					if(ud.extended_screen_size==2)
+					{
+						ud.extended_screen_size = 1;
+						ud.screen_size -= 4;
+					}
+				}
+				else
+	                ud.screen_size -= 4;	
+                vscrn();
+            }
+            if( ACTION( gamefunc_Shrink_Screen ) )
+            {
+                CONTROL_ClearAction( gamefunc_Shrink_Screen );
+                if(ud.screen_size < 64) sound(THUD);
+
+				// FIX_00027: Added an extra small statusbar (HUD)
+				if (ud.screen_size==4)
+				{
+					ud.extended_screen_size--;
+					if(ud.extended_screen_size<0)
+					{
+						ud.extended_screen_size=0;
+						ud.screen_size += 4;
+					}
+				}
+				else
+	                ud.screen_size += 4;
+                vscrn();
+            }
+
+			if(ud.screen_size < 4)
+				ud.extended_screen_size = 1;
+			else if(ud.screen_size > 4)
+				ud.extended_screen_size = 0;
+
+        }
+
+    if( ps[myconnectindex].cheat_phase == 1 || ps[myconnectindex].gm&(MODE_MENU|MODE_TYPE)) return;
+
+    if( ACTION(gamefunc_See_Coop_View) && ( ud.coop == 1 || ud.recstat == 2) )
+    {
+        CONTROL_ClearAction( gamefunc_See_Coop_View );
+        screenpeek = connectpoint2[screenpeek];
+        if(screenpeek == -1) screenpeek = connecthead;
+        restorepalette = 1;
+    }
+
+    if( ud.multimode > 1 && ACTION(gamefunc_Show_Opponents_Weapon) )
+    {
+        CONTROL_ClearAction(gamefunc_Show_Opponents_Weapon);
+        ud.showweapons = 1-ud.showweapons;
+        FTA(82-ud.showweapons,&ps[screenpeek],1);
+    }
+
+    if( ACTION(gamefunc_Toggle_Crosshair) )
+    {
+        CONTROL_ClearAction(gamefunc_Toggle_Crosshair);
+        ud.crosshair = 1-ud.crosshair;
+        FTA(21-ud.crosshair,&ps[screenpeek],1);
+    }
+
+    if(ud.overhead_on && ACTION(gamefunc_Map_Follow_Mode) )
+    {
+        CONTROL_ClearAction(gamefunc_Map_Follow_Mode);
+        ud.scrollmode = 1-ud.scrollmode;
+        if(ud.scrollmode)
+        {
+            ud.folx = ps[screenpeek].oposx;
+            ud.foly = ps[screenpeek].oposy;
+            ud.fola = ps[screenpeek].oang;
+        }
+        FTA(83+ud.scrollmode,&ps[myconnectindex],1);
+    }
+
+    if( SHIFTS_IS_PRESSED || ALT_IS_PRESSED )
+    {
+        i = 0;
+        if( KB_KeyPressed( sc_F1) ) { KB_ClearKeyDown(sc_F1);i = 1; }
+        if( KB_KeyPressed( sc_F2) ) { KB_ClearKeyDown(sc_F2);i = 2; }
+        if( KB_KeyPressed( sc_F3) ) { KB_ClearKeyDown(sc_F3);i = 3; }
+        if( KB_KeyPressed( sc_F4) ) { KB_ClearKeyDown(sc_F4);i = 4; }
+        if( KB_KeyPressed( sc_F5) ) { KB_ClearKeyDown(sc_F5);i = 5; }
+        if( KB_KeyPressed( sc_F6) ) { KB_ClearKeyDown(sc_F6);i = 6; }
+        if( KB_KeyPressed( sc_F7) ) { KB_ClearKeyDown(sc_F7);i = 7; }
+        if( KB_KeyPressed( sc_F8) ) { KB_ClearKeyDown(sc_F8);i = 8; }
+        if( KB_KeyPressed( sc_F9) ) { KB_ClearKeyDown(sc_F9);i = 9; }
+        if( KB_KeyPressed( sc_F10) ) {KB_ClearKeyDown(sc_F10);i = 10; }
+
+        if(i)
+        {
+            if(SHIFTS_IS_PRESSED)
+            {
+                if(i == 5 && ps[myconnectindex].fta > 0 && ps[myconnectindex].ftq == 26)
+                {
+                    music_select++;
+
+					// FIX_00065: Music cycling with F5 and SHIFT-F5 messed up
+					if(VOLUMEALL) // Then its 1.3d reg
+					{
+						if(music_select == 33) music_select = 0;
+					}
+					else if (VOLUMEONE)
+					{
+						if(music_select == 6) music_select = 0;
+					}
+					else // assume 1.5 or plutopak
+					{
+						if(music_select == 44) music_select = 0;
+					}
+
+                    strcpy(&tempbuf[0],"PLAYING ");
+                    strcat(&tempbuf[0],&music_fn[0][music_select][0]);
+					MUSIC_StopSong(); // FIX_00074: Shift f5 doesn't change hi-res tunes, but only midi tunes.
+                    playmusic(&music_fn[0][music_select][0]);
+                    strcpy(&fta_quotes[26][0],&tempbuf[0]);
+                    FTA(26,&ps[myconnectindex],1);
+                    return;
+                }
+
+                adduserquote(ud.ridecule[i-1]);
+
+                ch = 0;
+
+                tempbuf[ch] = 4;
+                tempbuf[ch+1] = 0;
+                strcat(tempbuf+1,ud.ridecule[i-1]);
+
+                i = 1+strlen(ud.ridecule[i-1]);
+
+                if(ud.multimode > 1)
+                    for(ch=connecthead;ch>=0;ch=connectpoint2[ch])
+                        if (ch != myconnectindex)
+                            sendpacket(ch,tempbuf,i);
+
+                pus = NUMPAGES;
+                pub = NUMPAGES;
+
+                return;
+
+            }
+
+            if(ud.lockout == 0)
+                if(SoundToggle && ALT_IS_PRESSED && ( RTS_NumSounds() > 0 ) && rtsplaying == 0 && VoiceToggle )
+            {
+                rtsptr = (char *)RTS_GetSound (i-1);
+                if(*rtsptr == 'C')
+                    FX_PlayVOC3D( rtsptr,0,0,0,255,-i);
+                else FX_PlayWAV3D( rtsptr,0,0,0,255,-i);
+
+                rtsplaying = 7;
+
+                if(ud.multimode > 1)
+                {
+                    tempbuf[0] = 7;
+                    tempbuf[1] = i;
+
+                    for(ch=connecthead;ch>=0;ch=connectpoint2[ch])
+                        if(ch != myconnectindex)
+                            sendpacket(ch,tempbuf,2);
+                }
+
+                pus = NUMPAGES;
+                pub = NUMPAGES;
+
+                return;
+            }
+        }
+    }
+
+    if(!ALT_IS_PRESSED && !SHIFTS_IS_PRESSED)
+    {
+
+        if( ud.multimode > 1 && ACTION(gamefunc_SendMessage) )
+        {
+            KB_FlushKeyboardQueue();
+            CONTROL_ClearAction( gamefunc_SendMessage );
+            ps[myconnectindex].gm |= MODE_TYPE;
+            typebuf[0] = 0;
+            inputloc = 0;
+        }
+
+        if( KB_KeyPressed(sc_F1) || ( ud.show_help && ( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) ) ) )
+        {
+            KB_ClearKeyDown(sc_F1);
+            KB_ClearKeyDown(sc_Space);
+            KB_ClearKeyDown(sc_kpad_Enter);
+            KB_ClearKeyDown(sc_Enter);
+            ud.show_help ++;
+
+            if( ud.show_help > 2 )
+            {
+                ud.show_help = 0;
+                if(ud.multimode < 2 && ud.recstat != 2) ready2send = 1;
+                vscrn();
+            }
+            else
+            {
+                setview(0,0,xdim-1,ydim-1);
+                if(ud.multimode < 2 && ud.recstat != 2)
+                {
+                    ready2send = 0;
+                    totalclock = ototalclock;
+                }
+            }
+        }
+
+//        if(ud.multimode < 2)
+        {
+            if(ud.recstat != 2 && KB_KeyPressed( sc_F2 ) )
+            {
+                KB_ClearKeyDown( sc_F2 );
+
+                if(movesperpacket == 4 && connecthead != myconnectindex)
+                    return;
+
+                FAKE_F2:
+                if(sprite[ps[myconnectindex].i].extra <= 0)
+                {
+                    FTA(118,&ps[myconnectindex],1);
+                    return;
+                }
+                cmenu(350);
+                screencapt = 1;
+                displayrooms(myconnectindex,65536);
+                savetemp("duke3d.tmp",waloff[MAXTILES-1],160*100);
+                screencapt = 0;
+                FX_StopAllSounds();
+                clearsoundlocks();
+
+//                setview(0,0,xdim-1,ydim-1);
+                ps[myconnectindex].gm |= MODE_MENU;
+
+                if(ud.multimode < 2)
+                {
+                    ready2send = 0;
+                    totalclock = ototalclock;
+                    screenpeek = myconnectindex;
+                }
+            }
+
+            if(KB_KeyPressed( sc_F3 ))
+            {
+                KB_ClearKeyDown( sc_F3 );
+
+                if(movesperpacket == 4 && connecthead != myconnectindex)
+                    return;
+
+                cmenu(300);
+                FX_StopAllSounds();
+                clearsoundlocks();
+
+//                setview(0,0,xdim-1,ydim-1);
+                ps[myconnectindex].gm |= MODE_MENU;
+                if(ud.multimode < 2 && ud.recstat != 2)
+                {
+                    ready2send = 0;
+                    totalclock = ototalclock;
+                }
+                screenpeek = myconnectindex;
+            }
+        }
+
+        if(KB_KeyPressed( sc_F4 ) && FXDevice != NumSoundCards )
+        {
+            KB_ClearKeyDown( sc_F4 );
+            FX_StopAllSounds();
+            clearsoundlocks();
+
+            ps[myconnectindex].gm |= MODE_MENU;
+            if(ud.multimode < 2 && ud.recstat != 2)
+            {
+                ready2send = 0;
+                totalclock = ototalclock;
+            }
+            cmenu(700);
+
+        }
+
+        if( KB_KeyPressed( sc_F6 ) && (ps[myconnectindex].gm&MODE_GAME))
+        {
+            KB_ClearKeyDown( sc_F6 );
+
+            if(movesperpacket == 4 && connecthead != myconnectindex)
+                return;
+
+            if(lastsavedpos == -1) goto FAKE_F2;
+
+            KB_FlushKeyboardQueue();
+
+            if(sprite[ps[myconnectindex].i].extra <= 0)
+            {
+                FTA(118,&ps[myconnectindex],1);
+                return;
+            }
+            screencapt = 1;
+            displayrooms(myconnectindex,65536);
+            savetemp("duke3d.tmp",waloff[MAXTILES-1],160*100);
+            screencapt = 0;
+            if( lastsavedpos >= 0 )
+            {
+                inputloc = strlen(&ud.savegame[lastsavedpos][0]);
+                current_menu = 360+lastsavedpos;
+                probey = lastsavedpos;
+            }
+            FX_StopAllSounds();
+            clearsoundlocks();
+
+            setview(0,0,xdim-1,ydim-1);
+            ps[myconnectindex].gm |= MODE_MENU;
+            if(ud.multimode < 2 && ud.recstat != 2)
+            {
+                ready2send = 0;
+                totalclock = ototalclock;
+            }
+        }
+
+        if(KB_KeyPressed( sc_F7 ) )
+        {
+            KB_ClearKeyDown(sc_F7);
+            if( ps[myconnectindex].over_shoulder_on )
+                ps[myconnectindex].over_shoulder_on = 0;
+            else
+            {
+                ps[myconnectindex].over_shoulder_on = 1;
+                cameradist = 0;
+                cameraclock = totalclock;
+            }
+            FTA(109+ps[myconnectindex].over_shoulder_on,&ps[myconnectindex],1);
+        }
+
+        if( KB_KeyPressed( sc_F5 ) && MusicDevice != NumSoundCards )
+        {
+            KB_ClearKeyDown( sc_F5 );
+            strcpy(&tempbuf[0],&music_fn[0][music_select][0]);
+            strcat(&tempbuf[0],".  USE SHIFT-F5 TO CHANGE.");
+            strcpy(&fta_quotes[26][0],&tempbuf[0]);
+            FTA(26,&ps[myconnectindex],1);
+
+        }
+
+        if(KB_KeyPressed( sc_F8 ))
+        {
+            KB_ClearKeyDown( sc_F8 );
+            ud.fta_on = !ud.fta_on;
+            FTA(24-ud.fta_on,&ps[myconnectindex],1);
+        }
+
+        if(KB_KeyPressed( sc_F9 ) && (ps[myconnectindex].gm&MODE_GAME) )
+        {
+            KB_ClearKeyDown( sc_F9 );
+
+            if(movesperpacket == 4 && myconnectindex != connecthead)
+                return;
+
+            if( lastsavedpos >= 0 ) cmenu(15001);
+            else cmenu(25000);
+            FX_StopAllSounds();
+            clearsoundlocks();
+            ps[myconnectindex].gm |= MODE_MENU;
+            if(ud.multimode < 2 && ud.recstat != 2)
+            {
+                ready2send = 0;
+                totalclock = ototalclock;
+            }
+        }
+
+        if(KB_KeyPressed( sc_F10 ))
+        {
+            KB_ClearKeyDown( sc_F10 );
+			cmenu(500);
+            FX_StopAllSounds();
+            clearsoundlocks();
+            ps[myconnectindex].gm |= MODE_MENU;
+            if(ud.multimode < 2 && ud.recstat != 2)
+            {
+                ready2send = 0;
+                totalclock = ototalclock;
+            }
+        }
+
+        
+        if( ud.overhead_on != 0)
+        {
+
+            j = totalclock-nonsharedtimer; nonsharedtimer += j;
+            if ( ACTION( gamefunc_Enlarge_Screen ) )
+                ps[myconnectindex].zoom += mulscale6(j,max(ps[myconnectindex].zoom,256));
+            if ( ACTION( gamefunc_Shrink_Screen ) )
+                ps[myconnectindex].zoom -= mulscale6(j,max(ps[myconnectindex].zoom,256));
+
+            if( (ps[myconnectindex].zoom > 2048) )
+                ps[myconnectindex].zoom = 2048;
+            if( (ps[myconnectindex].zoom < 48) )
+                ps[myconnectindex].zoom = 48;
+
+        }
+    }
+
+    if( KB_KeyPressed(sc_Escape) && ud.overhead_on && ps[myconnectindex].newowner == -1 )
+    {
+        KB_ClearKeyDown( sc_Escape );
+        ud.last_overhead = ud.overhead_on;
+        ud.overhead_on = 0;
+        ud.scrollmode = 0;
+        vscrn();
+    }
+
+    if( ACTION(gamefunc_AutoRun) )
+    {
+        CONTROL_ClearAction(gamefunc_AutoRun);
+        ud.auto_run = 1-ud.auto_run;
+        FTA(85+ud.auto_run,&ps[myconnectindex],1);
+    }
+
+    if( ACTION(gamefunc_Map) )
+    {
+        CONTROL_ClearAction( gamefunc_Map );
+        if( ud.last_overhead != ud.overhead_on && ud.last_overhead)
+        {
+            ud.overhead_on = ud.last_overhead;
+            ud.last_overhead = 0;
+        }
+        else
+        {
+            ud.overhead_on++;
+            if(ud.overhead_on == 3 ) ud.overhead_on = 0;
+            ud.last_overhead = ud.overhead_on;
+        }
+        restorepalette = 1;
+        vscrn();
+    }
+
+    if(KB_KeyPressed( sc_F11 ))
+    {
+        KB_ClearKeyDown( sc_F11 );
+		// FIX_00030: Brightness step was not the same from the keys vs menu 
+        if(SHIFTS_IS_PRESSED) ud.brightness-=8; // Keyboard step must be 8, as the brightness cursor step.
+        else ud.brightness+=8;
+
+        if (ud.brightness > 56 )
+            ud.brightness = 0;
+        else if(ud.brightness < 0)
+            ud.brightness = 56;
+
+        setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);
+        if(ud.brightness < 40) FTA( 29 + (ud.brightness>>3) ,&ps[myconnectindex],1);
+        else if(ud.brightness < 80) FTA( 96 + (ud.brightness>>3) - 5,&ps[myconnectindex],1);
+    }
+}
+
+
+
+void comlinehelp(char **argv)
+{
+    printf("Command line help.  %s [/flags...]\n",argv[0]);
+    puts(" ?, /?         This help message");
+    puts(" /l##          Level (1-11)");
+    puts(" /v#           Volume (1-4)");
+    puts(" /s#           Skill (1-4)");
+    puts(" /r            Record demo");
+    puts(" /dFILE        Start to play demo FILE");
+    puts(" /m            No monsters");
+    puts(" /ns           No sound");
+    puts(" /nm           No music");
+    puts(" /t#           Respawn, 1 = Monsters, 2 = Items, 3 = Inventory, x = All");
+    puts(" /c#           MP mode, 1 = DukeMatch(spawn), 2 = Coop, 3 = Dukematch(no spawn)");
+    puts(" /q#           Fake multiplayer (2-8 players)");
+    puts(" /a            Use player AI (fake multiplayer only)");
+    puts(" /i#           Network mode (1/0) (multiplayer only) (default == 1)");
+    puts(" /f#           Send fewer packets (1, 2, 4) (multiplayer only)");
+    puts(" /gFILE, /g... Use multiple group files (must be last on command line)");
+    puts(" /xFILE        Compile FILE (default GAME.CON)");
+    puts(" /u#########   User's favorite weapon order (default: 3425689071)");
+    puts(" /#            Load and run a game (slot 0-9)");
+    puts(" /z            Skip memory check");
+    puts(" -map FILE     Use a map FILE");
+    puts(" -name NAME    Foward NAME");
+    puts(" -net          Net mode game");
+    printf("\n");
+}
+
+void checkcommandline(int argc,char **argv)
+{
+    short i, j;
+    char *c;
+	char kbdKey;
+
+    ud.fta_on = 1;
+    ud.god = 0;
+    ud.m_respawn_items = 0;
+    ud.m_respawn_monsters = 0;
+    ud.m_respawn_inventory = 0;
+    ud.warp_on = 0;
+    ud.cashman = 0;
+    ud.m_player_skill = ud.player_skill = 2;
+	ud.multimode_bot = 0;
+
+#ifdef BETA
+    return;
+#endif
+
+	printf("Commands: ");
+	i=1;
+	while(i < argc)
+	{
+   		printf("%s ",argv[i]);
+   		i++;
+	}
+	printf("\n");
+
+	i = 1;
+
+    if(argc > 1)
+    {
+        while(i < argc)
+        {
+            c = argv[i];
+
+            if (stricmp(c, "-net") == 0)
+            {
+                i += 2;  // skip filename.
+				// FIX_00044: Markers are now on by default in netgames (as real DOS duke3d)
+				ud.m_marker = ud.marker = 1; 
+                continue;
+            }
+
+            if (stricmp(c, "-game_dir") == 0)
+            {
+				// Get the file name
+				i++;
+				c = argv[i];
+				strncpy(game_dir, c, 512);
+			
+				// skip over the file name now that we have it.
+				i++;
+
+                continue;
+            }
+
+            if (stricmp(c, "-stun") == 0)
+            {
+				g_bStun = 1;
+
+				i++;
+                continue;
+            }
+
+            if (stricmp(c, "/disableautoaim") == 0)
+            {
+
+				printf(	"\nThe Host used the /disableautoaim option to turn your Weapon AutoAim OFF\n"
+						"Playing without AutoAim is usually extremely difficult and can make boring\n" 
+						"games ruining Duke's playability. Duke3D was not designed to play with\n"
+						"AutoAim OFF like in modern FPS.\n\n"
+						"Do you authorize the HOST to turn your AutoAim OFF (Y/N)? ");
+
+				do
+					kbdKey = getch()|' ';
+				while(kbdKey != 'n' && kbdKey != 'y');
+
+				printf("%c \n", kbdKey);
+
+				if(kbdKey == 'y')
+					nHostForceDisableAutoaim = 1;
+				else
+					nHostForceDisableAutoaim = 2;
+
+				i++;
+                continue;
+            }
+
+            if(*c == '-')
+            {
+                if( *(c+1) == '8' ) eightytwofifty = 1;
+                i++;
+                continue;
+            }
+
+            if(*c == '?')
+            {
+                comlinehelp(argv);
+                Error(EXIT_SUCCESS, "");
+            }
+
+            if(*c == '/')
+            {
+                c++;
+                switch(*c)
+                {
+                    case 'x':
+                    case 'X':
+                        c++;
+                        if(*c)
+                        {
+							if(game_dir[0] != '\0')
+							{
+								sprintf(confilename, "%s\\%s", game_dir, c);
+							}
+							else
+							{
+								strcpy(confilename,c);
+							}
+
+                            if(SafeFileExists(confilename) == 0)
+                            {
+                                Error(EXIT_SUCCESS, "Could not find con file '%s'.\n",confilename);
+                            }
+                            else printf("Using con file: '%s'\n",confilename);
+                        }
+                        break;
+                    case 'g':
+                    case 'G':
+                        c++;
+                        if(*c)
+                        {
+							char fullpathgrpfile[16]; // 16 not enough
+							memset(fullpathgrpfile, 0, 16);
+
+                            if( strchr(c,'.') == 0)
+							{
+								strcat(c,".grp"); // crap!
+							}
+
+							if(game_dir[0] != '\0')
+							{
+								sprintf(fullpathgrpfile, "%s\\%s", game_dir, c);
+							}
+							else
+							{
+								strcpy(fullpathgrpfile, c);
+							}
+
+                            j = initgroupfile(fullpathgrpfile);
+                            if( j == -1 )
+                                printf("Could not find group file %s.\n",fullpathgrpfile);
+                        }
+
+                        break;
+                    case 'a':
+                    case 'A':
+                        ud.playerai = 1;
+                        puts("Other player AI.");
+                        break;
+                    case 'n':
+                    case 'N':
+                        c++;
+                        if(*c == 's' || *c == 'S')
+                        {
+                            CommandSoundToggleOff = 2;
+                            puts("Sound off.");
+                        }
+                        else if(*c == 'm' || *c == 'M')
+                        {
+                            CommandMusicToggleOff = 1;
+                            puts("Music off.");
+                        }
+                        else
+                        {
+                            comlinehelp(argv);
+                            Error(EXIT_SUCCESS, "");
+                        }
+                        break;
+                    case 'i':
+                    case 'I':
+                        c++;
+                        if(*c == '0') networkmode = 0;
+                        if(*c == '1') networkmode = 1;
+                        printf("Network Mode %d\n",networkmode);
+                        break;
+                    case 'c':
+                    case 'C':
+                        c++;
+                        if(*c == '1' || *c == '2' || *c == '3' )  // C1 = DM; C2 =COOP
+                            ud.m_coop = *c - '0' - 1; // 0 = DM   1 = COOP
+                        else ud.m_coop = 0;
+
+                        switch(ud.m_coop)
+                        {
+                            case 0:
+                                puts("Dukematch (spawn).");
+                                break;
+                            case 1:
+                                puts("Cooperative play.");
+                                break;
+                            case 2:
+                                puts("Dukematch (no spawn).");
+                                break;
+                        }
+
+                        break;
+                    case 'z':
+                    case 'Z':
+                        memorycheckoveride = 1;
+                        break;
+                    case 'f':
+                    case 'F':
+                        c++;
+                        if(*c == '1')
+                            movesperpacket = 1;
+                        if(*c == '2')
+                            movesperpacket = 2;
+                        if(*c == '4')
+                        {
+                            movesperpacket = 4;
+                            setpackettimeout(0x3fffffff,0x3fffffff);
+                        }
+                        break;
+                    case 't':
+                    case 'T':
+                        c++;
+                        if(*c == '1') ud.m_respawn_monsters = 1;
+                        else if(*c == '2') ud.m_respawn_items = 1;
+                        else if(*c == '3') ud.m_respawn_inventory = 1;
+                        else
+                        {
+                            ud.m_respawn_monsters = 1;
+                            ud.m_respawn_items = 1;
+                            ud.m_respawn_inventory = 1;
+                        }
+                        puts("Respawn on.");
+                        break;
+                    case 'm':
+                    case 'M':
+                        if( *(c+1) != 'a' && *(c+1) != 'A' )
+                        {
+                            ud.m_monsters_off = 1;
+                            ud.m_player_skill = ud.player_skill = 0;
+                            puts("Monsters off.");
+                        }
+                        break;
+                    case 'w':
+                    case 'W':
+                        ud.coords = 1;
+                        break;
+                    case 'q':
+                    case 'Q':
+                        puts("Fake multiplayer mode.");
+                        if( *(++c) == 0) ud.multimode_bot = 1;
+                        else ud.multimode_bot = atol(c)%17;
+						ud.multimode = ud.multimode_bot;
+                        ud.m_coop = ud.coop = 0;
+                        ud.m_marker = ud.marker = 1;
+                        ud.m_respawn_monsters = ud.respawn_monsters = 1;
+                        ud.m_respawn_items = ud.respawn_items = 1;
+                        ud.m_respawn_inventory = ud.respawn_inventory = 1;
+
+                        break;
+                    case 'r':
+                    case 'R':
+                        ud.m_recstat = 1;
+                        puts("Demo record mode on.");
+                        break;
+                    case 'd':
+                    case 'D':
+                        c++;
+                        if( strchr(c,'.') == 0)
+                            strcat(c,".dmo");
+                        printf("Play demo %s.\n",c);
+                        strcpy(firstdemofile,c);
+                        break;
+                    case 'l':
+                    case 'L':
+                        ud.warp_on = 1;
+                        c++;
+                        ud.m_level_number = ud.level_number = (atol(c)-1)%11;
+                        break;
+                    case 'j':
+                    case 'J':
+                        Error(EXIT_SUCCESS, "This port has a auto adaptive version system. All versions supported, simply change your duke3d.grp\n");
+						break;
+
+                    case 'v':
+                    case 'V':
+                        c++;
+                        ud.warp_on = 1;
+                        ud.m_volume_number = ud.volume_number = atol(c)-1;
+                        break;
+                    case 's':
+                    case 'S':
+                        c++;
+                        ud.m_player_skill = ud.player_skill = (atol(c)%5);
+                        if(ud.m_player_skill == 4)
+                            ud.m_respawn_monsters = ud.respawn_monsters = 1;
+                        break;
+                    case '0':
+                    case '1':
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
+                    case '8':
+                    case '9':
+                        ud.warp_on = 2 + (*c) - '0';
+                        break;
+                    case 'u':
+                    case 'U':
+                        c++;
+                        j = 0;
+                        if(*c)
+                        {
+                            puts("Using favorite weapon order(s).");
+                            while(*c)
+                            {
+                                ud.mywchoice[j] = *c-'0';
+                                c++;
+                                j++;
+                            }
+                            while(j < 10)
+                            {
+                                if(j == 9)
+                                    ud.mywchoice[9] = 1;
+                                else
+                                    ud.mywchoice[j] = 2;
+
+                                j++;
+                            }
+                        }
+                        else
+                        {
+                            puts("Using default weapon orders.");                         
+                        }
+
+                        break;
+
+                    case '?': // Show help
+					default: 
+
+						comlinehelp(argv);
+                        Error(EXIT_SUCCESS, "");
+						break;
+                }
+            }
+            i++;
+        }
+    }
+}
+
+
+
+void printstr(short x, short y, char string[81], char attribute)
+{
+        char character;
+        short i, pos;
+
+        pos = (y*80+x)<<1;
+        i = 0;
+        while (string[i] != 0)
+        {
+                character = string[i];
+                printchrasm(0xb8000+(long)pos,1L,((long)attribute<<8)+(long)character);
+                i++;
+                pos+=2;
+        }
+}
+
+/*
+void cacheicon(void)
+{
+    if(cachecount > 0)
+    {
+        if( (ps[myconnectindex].gm&MODE_MENU) == 0 )
+            rotatesprite((320-7)<<16,(200-23)<<16,32768L,0,SPINNINGNUKEICON,0,0,2,windowx1,windowy1,windowx2,windowy2);
+        cachecount = 0;
+    }
+}
+       */
+
+void Logo(void)
+{
+    short i,j,soundanm;
+
+    soundanm = 0;
+
+    ready2send = 0;
+
+    KB_FlushKeyboardQueue();
+
+    setview(0,0,xdim-1,ydim-1);
+    clearview(0L);
+    palto(0,0,0,63);
+
+    flushperms();
+    nextpage();
+
+    MUSIC_StopSong();
+
+	if(ud.showcinematics && numplayers < 2)
+	{
+		if(!VOLUMEONE)
+		{
+		    if(!KB_KeyWaiting() && nomorelogohack == 0)
+		    {
+		        getpackets();
+		        playanm("logo.anm",5);
+		        palto(0,0,0,63);
+		        KB_FlushKeyboardQueue();
+		    }
+		
+		    clearview(0L);
+		    nextpage();
+		}
+		
+		playmusic(&env_music_fn[0][0]);
+		
+	    for(i=0;i<64;i+=7) palto(0,0,0,i);
+	    ps[myconnectindex].palette = drealms;
+	    palto(0,0,0,63);
+	    rotatesprite(0,0,65536L,0,DREALMS,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+	    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+	    totalclock = 0;
+	    while( totalclock < (120*7) && !KB_KeyWaiting() )
+	        getpackets();
+	
+	    for(i=0;i<64;i+=7) palto(0,0,0,i);
+	    clearview(0L);
+	    nextpage();
+	
+	    ps[myconnectindex].palette = titlepal;
+	    flushperms();
+	    rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
+	    KB_FlushKeyboardQueue();
+	    nextpage();
+	    for(i=63;i>0;i-=7) palto(0,0,0,i);
+	    totalclock = 0;
+	
+	    while(totalclock < (860+120) && !KB_KeyWaiting())
+	    {
+	        rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
+	
+	        if( totalclock > 120 && totalclock < (120+60) )
+	        {
+	            if(soundanm == 0)
+	            {
+	                soundanm = 1;
+	                sound(PIPEBOMB_EXPLODE);
+	            }
+	            rotatesprite(160<<16,104<<16,(totalclock-120)<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
+	        }
+	        else if( totalclock >= (120+60) )
+	            rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
+	
+	        if( totalclock > 220 && totalclock < (220+30) )
+	        {
+	            if( soundanm == 1)
+	            {
+	                soundanm = 2;
+	                sound(PIPEBOMB_EXPLODE);
+	            }
+	
+	            rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
+	            rotatesprite(160<<16,(129)<<16,(totalclock - 220 )<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);
+	        }
+	        else if( totalclock >= (220+30) )
+	            rotatesprite(160<<16,(129)<<16,30<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);
+	
+			if(PLUTOPAK) // FIX_00064: Cinematics explosions were not right for 1.3/1.3d grp.
+			{
+				if( totalclock >= 280 && totalclock < 395 )
+				{
+					rotatesprite(160<<16,(151)<<16,(410-totalclock)<<12,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);
+					if(soundanm == 2)
+					{
+						soundanm = 3;
+						sound(FLY_BY);
+					}
+				}
+				else if( totalclock >= 395 )
+				{
+					if(soundanm == 3)
+					{
+						soundanm = 4;
+						sound(PIPEBOMB_EXPLODE);
+					}
+					rotatesprite(160<<16,(151)<<16,30<<11,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);
+				}
+			}
+
+	        getpackets();
+	        nextpage();
+	    }
+		// FIX_00077: Menu goes directly to the "NEW GAME" sub-menu when starting new game (Turrican)
+		KB_FlushKeyboardQueue();
+	} 
+	else if(numplayers > 1)
+    {
+		// FIX_00079: "waiting player" screen not showing up (black screen)
+		playmusic(&env_music_fn[0][0]);
+
+	    ps[myconnectindex].palette = titlepal;
+	    for(i=63;i>0;i-=7) palto(0,0,0,i);
+
+        rotatesprite(0,0,65536L,0,BETASCREEN,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
+        rotatesprite(160<<16,(104)<<16,60<<10,0,DUKENUKEM,0,0,2+8,0,0,xdim-1,ydim-1);
+        rotatesprite(160<<16,(129)<<16,30<<11,0,THREEDEE,0,0,2+8,0,0,xdim-1,ydim-1);
+        rotatesprite(160<<16,(151)<<16,30<<11,0,PLUTOPAKSPRITE+1,0,0,2+8,0,0,xdim-1,ydim-1);
+
+        gametext(160,190,"WAITING FOR PLAYERS",14,2);
+        nextpage();
+    }
+	else
+	{
+		// FIX_00091: Main theme starting too early (Bryzian/Turrican)
+		playmusic(&env_music_fn[0][0]);
+	}
+
+    waitforeverybody();
+
+    flushperms();
+    clearview(0L);
+    nextpage();
+
+    ps[myconnectindex].palette = palette;
+    sound(NITEVISION_ONOFF);
+
+    palto(0,0,0,0);
+    clearview(0L);
+}
+
+void loadtmb(void)
+{
+    char tmb[8000];
+    long fil, l;
+
+    fil = kopen4load("d3dtimbr.tmb",0);
+    if(fil == -1) return;
+    l = kfilelength(fil);
+    kread(fil,(char *)tmb,l);
+    MUSIC_RegisterTimbreBank(tmb);
+    kclose(fil);
+}
+
+/*
+===================
+=
+= ShutDown
+=
+===================
+*/
+
+void ShutDown( void )
+{
+    SoundShutdown();
+    MusicShutdown();
+    uninittimer();
+    uninitengine();
+    CONTROL_Shutdown();
+    CONFIG_WriteSetup();
+    KB_Shutdown();
+    CONSOLE_Term();
+}
+
+static char todd[] = "Duke Nukem 3D(tm) Copyright 1989, 1996 Todd Replogle and 3D Realms Entertainment";
+static char trees[] = "I want to make a game with trees";
+static char sixteen[] = "16 Possible Dukes";
+
+/*
+===================
+=
+= Startup
+=
+===================
+*/
+
+void compilecons(void)
+{
+	char userconfilename[512];
+
+   mymembuf = (char *)&hittype[0];
+   labelcode = (long *)&sector[0];
+   label = (char *)&sprite[0];
+
+	sprintf(userconfilename, "%s", confilename);
+
+   loadefs(userconfilename,mymembuf, 0);  
+
+}
+
+
+void Startup(void)
+{
+   int i;
+
+   // Init the Console 
+   CONSOLE_Init();
+
+   KB_Startup();
+
+   CONFIG_GetSetupFilename();
+   CONFIG_ReadSetup();
+
+   compilecons();
+
+#ifdef AUSTRALIA
+  ud.lockout = 1;
+#endif
+
+   if(CommandSoundToggleOff) SoundToggle = 0;
+   if(CommandMusicToggleOff) MusicToggle = 0;
+
+//if(VOLUMEONE)
+//{
+//   printf("\n*** You have run Duke Nukem 3D %ld times. ***\n",ud.executions);
+//   if(ud.executions >= 50) puts("IT IS NOW TIME TO UPGRADE TO THE COMPLETE VERSION!!!\n");
+//}
+
+   CONTROL_Startup( ControllerType, &GetTime, TICRATE );
+
+// CTW - MODIFICATION
+// initengine(ScreenMode,ScreenWidth,ScreenHeight);
+   initengine();
+// CTW END - MODIFICATION
+   inittimer(TICRATE);
+
+   puts("Loading art header.");
+
+   if(game_dir[0] != '\0')
+   {
+		loadpics("tiles000.art", game_dir);
+   }
+   else
+   {
+	   loadpics("tiles000.art", "\0");
+   }
+
+   readsavenames();
+
+   tilesizx[MIRROR] = tilesizy[MIRROR] = 0;
+
+   for(i=0;i<MAXPLAYERS;i++) playerreadyflag[i] = 0;
+   initmultiplayers(0,0,0);
+
+   if(numplayers > 1)
+    puts("Multiplayer initialized.");
+
+   ps[myconnectindex].palette = (char *) &palette[0];
+   SetupGameButtons();
+
+   if(networkmode == 255)
+       networkmode = 1;
+
+#ifdef PLATFORM_DOS
+   puts("Checking music inits.");
+   MusicStartup();
+   puts("Checking sound inits.");
+   SoundStartup();
+#else
+   /* SBF - wasn't sure if swapping them would harm anything. */
+   puts("Checking sound inits.");
+   SoundStartup();
+   puts("Checking music inits.");
+   MusicStartup();
+#endif
+
+   // AutoAim
+	if(nHostForceDisableAutoaim)
+		ud.auto_aim = 0;
+
+   puts("loadtmb()");
+   loadtmb();
+}
+
+
+void sendscore(char *s)
+{
+    if(numplayers > 1)
+      genericmultifunction(-1,s,strlen(s)+1,5);
+}
+
+
+void getnames(void)
+{
+    short i,j,l;
+
+	// FIX_00031: Names now limited to 10 chars max that is the fragbar field limit.
+    for(l=0; l<=9 && myname[l];l++)
+    {
+        ud.user_name[myconnectindex][l] = toupper(myname[l]);
+        buf[l+2] = toupper(myname[l]);
+    }
+
+#ifdef CHECK_XDUKE_REV // must not be under "if(numplayers > 1)" so it runs in any case
+	ud.rev[myconnectindex][0] = true; // always true. Used to check who we validated
+	ud.rev[myconnectindex][1] = DUKE_ID;
+	ud.rev[myconnectindex][2] = XDUKE_REV_X;
+	ud.rev[myconnectindex][3] = XDUKE_REV_DOT_Y;
+#endif
+
+	memcpy(ud.groupefil_crc32[myconnectindex],groupefil_crc32, sizeof(groupefil_crc32));
+	ud.conSize[myconnectindex] = ud.conSize[0]; // [0] still containing the original value
+	ud.conCRC[myconnectindex] = ud.conCRC[0];
+	ud.exeCRC[myconnectindex] = ud.exeCRC[0];
+
+    if(numplayers > 1)
+    {
+
+        buf[0] = 6;
+        buf[1] = grpVersion;
+
+        buf[l+2] = 0;
+        l += 3;
+
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if( i != myconnectindex )
+                sendpacket(i,&buf[0],l);
+
+		if(nHostForceDisableAutoaim==2) // user doesn't want AA off.
+			for(i=connecthead;i>=0;i=connectpoint2[i])
+			{
+				buf[0] = 133; // request to stop the game.
+				sendpacket(i,&buf[0],l);
+			}
+
+
+#ifdef CHECK_XDUKE_REV
+        buf[0] = 131; // xDuke TAG ID
+        buf[1] = ud.rev[myconnectindex][1];
+		buf[2] = ud.rev[myconnectindex][2]; // version x
+		buf[3] = ud.rev[myconnectindex][3]; // version .y
+
+		buf[4] = 0;		// reserved
+		buf[5] = 0;		// reserved...
+        buf[9] = 0;		// reserved.
+		// See below for single player mode.
+
+		for(i=connecthead;i>=0;i=connectpoint2[i]) 
+			if( i != myconnectindex )
+                sendpacket(i,&buf[0],10);
+#endif
+
+  //      getpackets();
+
+
+        l = 1;
+        buf[0] = 9; // send weapon order
+
+        for(i=0;i<10;i++)
+        {
+            ud.wchoice[myconnectindex][i] = ud.mywchoice[i];
+            buf[l] = (char) ud.mywchoice[i];
+            l++;
+        }
+
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if(i != myconnectindex)
+                sendpacket(i,&buf[0],11);
+
+		buf[0] = 134;	// GRP CRC + CON SIZE + conCRC + exeCRC
+		memcpy(buf+1, groupefil_crc32, sizeof(groupefil_crc32));
+		memcpy(buf+1+sizeof(groupefil_crc32), ud.conSize, sizeof(ud.conSize[0]));
+		memcpy(buf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0]), ud.conCRC, sizeof(ud.conCRC[0]));
+		memcpy(buf+1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0]), ud.exeCRC, sizeof(ud.exeCRC[0]));
+
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if( i != myconnectindex )
+				sendpacket(i,buf,1+sizeof(groupefil_crc32)+sizeof(ud.conSize[0])+sizeof(ud.conCRC[0])+
+				sizeof(ud.exeCRC[0]));
+
+//        getpackets();
+
+        buf[0] = 10;
+        buf[1] = ps[0].aim_mode;
+        ps[myconnectindex].aim_mode = ps[0].aim_mode;
+
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if(i != myconnectindex)
+                sendpacket(i,buf,2);
+
+//        getpackets();
+
+        if(cp == 0)
+        {
+            buf[0] = 125;
+
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                if(i != myconnectindex)
+                    sendpacket(i,buf,1);
+        }
+
+//        getpackets();
+
+        waitforeverybody();
+
+#ifdef CHECK_XDUKE_REV
+		// from command "case 131:"
+		for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
+			if(((ud.rev[i][2]<<8)+ud.rev[i][3]) != ((XDUKE_REV_X<<8)+XDUKE_REV_DOT_Y))
+				l=1;
+			else
+				ud.rev[i][0] = true; // means we validated this guy
+
+		if(l)
+		{
+			printf("\n*** One or more players do not have the same xDuke version:\n\n");
+			for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
+				printf("Player [%-10s] is using xDuke v%d.%d\n", ud.user_name[i], 
+				ud.rev[i][2],ud.rev[i][3]);
+			Error(EXIT_SUCCESS, "");
+		}		
+#endif
+
+		// checking GRP/CON size from "case 134"
+		for(l=0,i=connecthead;i>=0;i=connectpoint2[i])
+			for(j=0; j<MAXGROUPFILES; j++)
+			{
+				if(ud.groupefil_crc32[i][j]!=ud.groupefil_crc32[myconnectindex][j] || ud.conSize[i] != ud.conSize[myconnectindex])
+					l=1;
+			}
+
+		if(l)
+		{
+			printf("\n*** One or more players do not have the same GRP/CON version:\n\n");
+			for(i=connecthead;i>=0;i=connectpoint2[i])
+			{
+				for(j=0; j<MAXGROUPFILES && ud.groupefil_crc32[i][j]; j++)
+				{
+					if(j)
+						printf("                    GRP (Add-on) : %s CRC=%X\n",
+						grpVersion2char_from_crc(ud.groupefil_crc32[i][j]),
+						ud.groupefil_crc32[i][j]);
+					else
+						printf("Player [%-10s] GRP (base)   : %s CRC=%X\n", ud.user_name[i],
+						grpVersion2char_from_crc(ud.groupefil_crc32[i][j]),
+						ud.groupefil_crc32[i][j]);
+				}
+				printf("                    CON code size: %d\n\n",ud.conSize[i]);
+			}
+			Error(EXIT_SUCCESS,	"");
+		}
+    } 
+	else if(nHostForceDisableAutoaim==2)
+	{
+		nHostForceDisableAutoaim=0;
+		ud.auto_aim = 2;
+	}
+
+    if(cp == 1)
+        gameexit("Please put Duke Nukem 3D Atomic Edition CD in drive.");
+}
+
+void writestring(long a1,long a2,long a3,short a4,long vx,long vy,long vz)
+{
+
+    FILE *fp;
+
+    fp = (FILE *)fopen("debug.txt","rt+");
+
+    fprintf(fp,"%ld %ld %ld %ld %ld %ld %ld\n",a1,a2,a3,a4,vx,vy,vz);
+
+    fclose(fp);
+
+}
+
+
+char testcd( char *fn )
+{
+#if PLATFORM_DOS
+ short drive_count, drive;
+ long dalen = 0;
+ struct find_t dafilet;
+ int fil;
+
+ union _REGS ir;
+ union _REGS or;
+ struct _SREGS sr;
+
+ if( IDFSIZE != 9961476 )
+ {
+     drive = toupper(*fn)-'A';
+
+     ir.w.ax = 0x1500;
+     ir.w.bx = 0;                             /* check that MSCDEX is installed */
+     int386(0x2f, &ir, &or);
+     drive_count = or.w.bx;
+
+     if( drive_count == 0 )
+         return 1;
+
+     ir.w.ax = 0x150b;
+     ir.w.bx = 0;
+     ir.w.cx = drive;
+     int386(0x2f, &ir, &or);
+
+     if (or.w.ax == 0 || or.w.bx != 0xadad)
+         return 1;
+
+     ir.w.ax = 0x1502;
+     ir.w.bx = FP_OFF(buf);
+     sr.es = FP_SEG(buf);
+     ir.w.cx = drive;
+     int386x(0x2f, &ir, &or, &sr);
+
+     if( or.h.al == 0 || or.h.al == 30)
+         return 1;
+
+  }
+
+  fil = open(fn,O_RDONLY,S_IREAD);
+
+  if ( fil < 0 ) return 1;
+
+  // ( DO A SEE/Byte check here.) (Not coded in this version)
+
+
+  dalen = filelength(fil);
+
+  close(fil);
+
+  return( dalen != IDFSIZE );
+
+#else
+    STUBBED("CD detection.");
+    return 0;
+#endif
+}
+
+
+void copyprotect(void)
+{
+    FILE *fp;
+    char idfile[256];
+
+    return;
+
+    cp = 0;
+
+    fp = (FILE *)fopen("cdrom.ini","rt");
+    if(fp == (FILE *) NULL)
+    {
+        cp = 1;
+        return;
+    }
+
+    fscanf(fp,"%s",idfile);
+    fclose(fp);
+
+    strcat(idfile,IDFILENAME);
+
+    if( testcd(idfile) )
+    {
+        cp = 1;
+        return;
+    }
+}
+
+
+static int load_duke3d_groupfile(void)
+{
+	// FIX_00032: Added multi base GRP manager. Use duke3d*.grp to handle multiple grp.
+    char groupfile[9][512];
+	char groupfilefullpath[512];
+	int kbdKey, i = 0;
+
+	char *baseDir="duke3d*.grp";
+	WIN32_FIND_DATA FindFileData;
+	HANDLE hFind =  INVALID_HANDLE_VALUE;
+
+	if(game_dir[0] != '\0')
+	{
+		sprintf(groupfilefullpath, "%s\\%s", game_dir, baseDir);
+		hFind = FindFirstFile(groupfilefullpath, &FindFileData);
+		if (hFind == INVALID_HANDLE_VALUE) 
+		{
+			sprintf(groupfilefullpath, "%s", baseDir);
+		}
+		else
+			FindClose(hFind);
+	}
+	else
+		sprintf(groupfilefullpath, "%s", baseDir);		
+
+	printf("Searching duke3d*.grp:\n\n");
+	hFind = FindFirstFile(groupfilefullpath,&FindFileData);
+
+	if ( hFind==INVALID_HANDLE_VALUE ) 
+		Error(EXIT_SUCCESS, "Can't find %s\n", groupfilefullpath);
+ 
+	do 
+	{
+		i++;
+		sprintf(groupfile[i-1], "%s", FindFileData.cFileName);
+		printf("Found GRP #%d:\t%d Bytes\t %s \n", i, FindFileData.nFileSizeLow, groupfile[i-1]);
+	} while ( FindNextFile(hFind, &FindFileData) && i < 9 );
+
+	if(i==1)
+		sprintf(groupfilefullpath, "%s", groupfile[0]);
+	else
+	{
+		printf("\n-> Choose a base GRP file from 1 to %c: ",'0' + i);
+		do
+			kbdKey = getch();
+		while(kbdKey < '1' || kbdKey > ('0' + i));
+		printf("%c\n", kbdKey);
+		sprintf(groupfilefullpath, "%s", groupfile[kbdKey-'1']);
+	}
+	
+	FindClose(hFind);
+
+	FixFilePath(groupfilefullpath);
+
+	return(initgroupfile(groupfilefullpath) != -1);
+}
+
+int main(int argc,char **argv)
+{
+    long i, j, k, l;
+	int32 iScriptHandle;
+	long filehandle;
+
+	char HEAD[2048], HEAD2[2048], HEADA[2048], HEAD2A[2048];
+	char kbdKey;
+	char *exe;
+
+
+	//printf(	"This is a debug version 19.7.1 only Based on 19.7\n"
+	//		"Fully compliant with v19.7. Added the following:\n\n"
+	//		"FIX_00086: grp loaded by smaller sucessive chunks to avoid\n"
+	//		"           overloading low ram computers (Spanator)\n"
+	//		"FIX_00087: intro in 1024x768 mode being slow. Undone FIX_00070\n"
+	//		"           and fixed font issue again (Bryzian)\n"
+	//		"FIX_00088: crash on maps using a bad palette index like the end\n"
+	//		"           of roch3.map (NY00123)\n"
+	//		"FIX_00089: scoreboard not shown for last player who quits a DM.\n"
+	//		"           Only 19.7 affected. (Sarah)\n"
+	//		"FIX_00090: Removed showinfo key. FPS were shown after CRC msg. \n"
+	//		"           CRC not always removed. (Turrican)\n"
+	//		"FIX_00091: Main theme starting too early (Bryzian/Turrican)\n"
+	//		"FIX_00092: corrupted saved files making the following saved\n"
+	//		"           files invisible (Bryzian)\n\n"
+	//		"This version should not be distributed. It's not secret but it\n"
+	//		"would create a bad mess in the duke community if people start\n"
+	//		"using it as it may contain new unsuspected bugs. Only a select\n"
+	//		"group of known dukers who know what they are doing should be using\n"
+	//		"it. Please report new bugs at xd@m-klein.com or on DX forums. Thx!\n\n");
+	if(XDUKE_SUBVERSION)
+		printf("*** xDuke v%d.%d.%d ***\n\n", XDUKE_REV_X, XDUKE_REV_DOT_Y,XDUKE_SUBVERSION);
+	else
+		printf("*** xDuke v%d.%d ***\n\n", XDUKE_REV_X, XDUKE_REV_DOT_Y);
+
+	// FIX_00033: Fake multi and AI are now fully working
+	ud.multimode = 1;  // xduke: must be done before checkcommandline or that will prevent Fakeplayer and AI
+	
+    if (!load_duke3d_groupfile())
+    {
+        Error(EXIT_SUCCESS, "Could not initialize any original BASE duke3d*.grp file\n"
+							"Even if you are playing a custom GRP you still need\n"
+							"an original base GRP file as Shareware/Full 1.3D GRP or\n"
+							"the v1.5 ATOMIC GRP file. Such a file seems to be missing\n"
+							"or is corrupted\n");
+    }
+
+	// FIX_00022: Automatically recognize the shareware grp (v1.3) + full version (1.3d) +
+	//            atomic (1.4/1.5 grp) and the con files version (either 1.3 or 1.4) (JonoF's idea)
+
+	// Detecting grp version
+	// We keep the old GRP scheme detection for 19.6 compliance. Will be obsolete.
+	filehandle = kopen4load("DUKEDC9.MAP",1);
+	kclose(filehandle);
+
+	if (filehandle == -1) // not DC pack
+	{
+		filehandle = kopen4load("DUKESW.BIN",1);
+		kclose(filehandle);
+
+		if (filehandle == -1) // not Shareware version 1.3
+		{
+			filehandle = kopen4load("E4L11.MAP",1);
+			kclose(filehandle);
+
+			if (filehandle == -1) // not Atomic Edition 1.4/1.5
+			{
+				filehandle = kopen4load("E3L11.MAP",1);
+				kclose(filehandle);
+
+				if (filehandle == -1) // not Regular version 1.3d
+				{
+					grpVersion = UNKNOWN_GRP;
+				}
+				else
+				{
+					grpVersion = REGULAR_GRP13D;
+				}
+			}
+			else
+			{
+				grpVersion = ATOMIC_GRP14_15;
+			}
+		}
+		else
+		{
+			grpVersion = SHAREWARE_GRP13;
+		}
+	}
+	else
+	{
+		grpVersion = DUKEITOUTINDC_GRP;
+	}
+
+	// FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
+	if (	groupefil_crc32[0]==CRC_BASE_GRP_SHAREWARE_13 ||
+				groupefil_crc32[0]==CRC_BASE_GRP_FULL_13 ||
+				groupefil_crc32[0]==CRC_BASE_GRP_PLUTONIUM_14 ||
+				groupefil_crc32[0]==CRC_BASE_GRP_ATOMIC_15 )
+	{
+		printf("GRP identified as: %s\n", grpVersion2char_from_crc(groupefil_crc32[0]));
+	}
+	else
+	{
+		printf(	"The content of your original BASE *.GRP is corrupted. CRC=%X\n"
+			"You may run in troubles. Official GRP are:\n\n", groupefil_crc32[0]);
+
+		for(i=0; i<MAX_KNOWN_GRP; i++)
+			printf("%s -> CRC32=%X  Size=%d bytes\n", crc32lookup[i].name, crc32lookup[i].crc32, crc32lookup[i].size);
+
+		printf(	"\nYou should try to get one of these GRP only as a base GRP\n"
+				"Do you want to continue anyway? (Y/N): ");
+		do
+			kbdKey = getch() | ' ';
+		while(kbdKey != 'y' && kbdKey != 'n');
+		printf("%c\n", kbdKey);
+
+		if(kbdKey == 'n')
+			Error(EXIT_SUCCESS,"");
+	}
+
+	// computing exe crc
+	ud.exeCRC[0] = 0;
+	exe = NULL;
+	filehandle = open(argv[0],O_BINARY|O_RDONLY);
+	if(filehandle!=-1)
+	{
+		exe = malloc(filelength(filehandle));
+		if(exe)
+		{
+			read(filehandle, exe, filelength(filehandle));
+			ud.exeCRC[0] = crc32_update(exe, filelength(filehandle), ud.exeCRC[0]);
+			free(exe);
+		}
+		close(filehandle);
+	}
+
+
+	checkcommandline(argc,argv);
+
+    copyprotect();
+
+    setvmode(0x03);
+
+// This is needed for the icculus.org ported Build Engine.
+#if !PLATFORM_DOS
+    //get the config entry for fullscreen
+
+
+	// Are we trying to load a mod?
+	if(game_dir[0] != '\0')
+	{
+		//FILE *fp = NULL;
+		char setupfilename[128];
+
+	   //Yes
+		sprintf(setupfilename, "%s\\%s", game_dir, SETUPFILENAME);	
+		iScriptHandle = SCRIPT_Load(setupfilename);
+	}else
+	{
+		iScriptHandle = SCRIPT_Load(SETUPFILENAME);
+	}
+
+    if(iScriptHandle != -1)
+    {
+		SCRIPT_GetNumber(iScriptHandle, "Screen Setup", "Fullscreen",&BFullScreen);
+        SCRIPT_Free(iScriptHandle);
+        iScriptHandle = -1;
+    }
+
+    _platform_init(argc, argv, "Duke Nukem 3D", "Duke3D");
+#endif
+
+	setmmxoverlay(getenv("BUILD_NOPENTIUM") == NULL);
+
+    todd[0] = 'T';
+    sixteen[0] = 'D';
+    trees[0] = 'I';
+
+	sprintf(HEAD, "   Duke Nukem 3D Shareware v1.3\n");
+
+	if(PLUTOPAK)
+	sprintf(HEAD2, "   Duke Nukem 3D v1.4/1.5 Atomic Edition\n");
+	else
+	sprintf(HEAD2, "   Duke Nukem 3D Full Version 1.3d\n");
+
+	sprintf(HEADA, "   Duke Nukem 3D AUSSIE Shareware v1.3\n");
+	sprintf(HEADA, "   Duke Nukem 3D AUSSIE Full Version\n");
+
+    totalmemory = Z_AvailHeap();
+
+    if(memorycheckoveride == 0)
+    {
+        if(totalmemory < (3162000-350000))
+        {
+            puts("You don't have enough free memory to run Duke Nukem 3D.");
+            puts("The DOS \"mem\" command should report 6,800K (or 6.8 megs)");
+            puts("of \"total memory free\".\n");
+            printf("Duke Nukem 3D requires %ld more bytes to run.\n",3162000-350000-totalmemory);
+            Error(EXIT_SUCCESS, "");
+        }
+    }
+    else
+        printf("Using %ld bytes for heap.\n",totalmemory);
+
+#ifndef ONELEVELDEMO
+// CTW - REMOVED
+/*  if(movesperpacket == 4)
+        TENtext();*/
+// CTW END - REMOVED
+#endif
+
+    RegisterShutdownFunction( ShutDown );
+
+#ifdef ONELEVELDEMO
+    puts("DUKE NUKEM 3D SINGLE-LEVEL PROMOTIONAL EDITION\n");
+    puts("This single-level promotional edition of Duke Nukem 3D (tm) may not be");
+    puts("distributed domestically (North America) by any publication other than");
+    puts("Computer Gaming World, a Ziff-Davis publication.  It is a promotional");
+    puts("version, licensed for a single month's run, and may not be redistributed");
+    puts("by any online service, BBS, commercial publisher, magazine or distributor.");
+    puts("International distribution rights are reserved.\n");
+    puts("Please read LICENSE.DOC for further information about this special version.");
+    puts("NOTE: DUKE NUKEM 3D CONTAINS MATURE CONTENT.\n");
+    puts("Press any key to continue.");
+    getch();
+#endif
+
+    Startup();
+
+    if( eightytwofifty && numplayers > 1 && (MusicDevice != NumSoundCards) )
+    {
+        puts("\n=========================================================================");
+        puts("WARNING: 8250 UART detected.");
+        puts("Music is being disabled and lower quality sound is being set.  We apologize");
+        puts("for this, but it is necessary to maintain high frame rates while trying to");
+        puts("play the game on an 8250.  We suggest upgrading to a 16550 or better UART");
+        puts("for maximum performance.  Press any key to continue.");
+        puts("=========================================================================\n");
+
+        while( !KB_KeyWaiting() ) getpackets();
+    }
+
+	if(g_bStun)
+	{
+		waitforeverybody();
+	}
+
+    if(numplayers > 1) // if multimode > 1 and numplayer == 1 => fake player mode on
+    {
+        ud.multimode = numplayers;
+        sendlogon();
+    }
+    else if(boardfilename[0] != 0)
+    {
+        ud.m_level_number = 7;
+        ud.m_volume_number = 0;
+        ud.warp_on = 1;
+    }
+
+    getnames();
+
+    if(ud.multimode > 1)
+    {
+        playerswhenstarted = ud.multimode;
+
+		// AddFaz fix.
+		// This would cause monsters not to spawn when loading a usermap
+		/*
+        if(ud.warp_on == 0)
+        {
+            ud.m_monsters_off = 1;
+            ud.m_player_skill = 0;
+        }
+		*/
+    }
+
+    ud.last_level = -1;
+
+   RTS_Init(ud.rtsname);
+   if(numlumps) printf("Using .RTS file:%s\n",ud.rtsname);
+
+   if (CONTROL_JoystickEnabled)
+       CONTROL_CenterJoystick
+          (
+          CenterCenter,
+          UpperLeft,
+          LowerRight,
+          CenterThrottle,
+          CenterRudder
+          );
+
+        puts("Loading palette/lookups.");
+
+// CTW - MODIFICATION
+/*  if( setgamemode(ScreenMode,ScreenWidth,ScreenHeight) < 0 )
+    {
+        printf("\nVESA driver for ( %i * %i ) not found/supported!\n",xdim,ydim);
+        vidoption = 2;
+        setgamemode(vidoption,320,200);
+    }*/
+    if( setgamemode(ScreenMode,ScreenWidth,ScreenHeight) < 0 )
+    {
+        printf("\nVESA driver for ( %i * %i ) not found/supported!\n",xdim,ydim);
+        ScreenMode = 2;
+        ScreenWidth = 320;
+        ScreenHeight = 200;
+        setgamemode(ScreenMode,ScreenWidth,ScreenHeight);
+    }
+// CTW END - MODIFICATION
+        printf("genspriteremaps()\n");
+
+    genspriteremaps();
+
+//if (VOLUMEONE)
+// {        if(numplayers > 4 || ud.multimode > 4)
+//            gameexit(" The full version of Duke Nukem 3D supports 5 or more players.");
+// }
+
+    setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);
+
+    ESCESCAPE;
+
+    FX_StopAllSounds();
+    clearsoundlocks();
+
+    if(ud.warp_on > 1 && ud.multimode < 2)
+    {
+        clearview(0L);
+        ps[myconnectindex].palette = palette;
+        palto(0,0,0,0);
+        rotatesprite(320<<15,200<<15,65536L,0,LOADSCREEN,0,0,2+8+64,0,0,xdim-1,ydim-1);
+        menutext(160,105,0,0,"LOADING SAVED GAME...");
+        nextpage();
+
+        j = loadplayer(ud.warp_on-2);
+        if(j)
+            ud.warp_on = 0;
+    }
+
+    //getpackets();
+
+    MAIN_LOOP_RESTART:
+
+    if(ud.warp_on == 0) //if game is loaded without /V or /L cmd arguments.
+	{
+ 
+		if (numplayers > 1 && boardfilename[0] != 0) //check if a user map is loaded and in multiplayer.
+		{
+			int c;
+ 
+			ud.level_number = ud.m_level_number = 7; // 7 = usermap.
+            ud.volume_number = ud.m_volume_number;
+			ud.player_skill = ud.m_player_skill;
+ 
+            switch(ud.m_coop) //set item spawn options, as they would be if
+			{      //game was started via main menu. 
+				case 0:
+					ud.respawn_inventory = ud.m_respawn_inventory = 1;
+					ud.respawn_items = ud.m_respawn_items = 1;
+					break;
+				case 1:
+					ud.respawn_inventory = ud.m_respawn_inventory = 1;
+					ud.respawn_items = ud.m_respawn_items = 0;
+					break;
+				case 2:
+					ud.respawn_inventory = ud.m_respawn_inventory = 0;
+					ud.respawn_items = ud.m_respawn_items = 0;
+				break;
+            }
+ 
+			if( ud.m_player_skill == 4 ) 
+			{
+				ud.m_respawn_monsters = 1; //set skill
+			}
+			else
+			{
+				ud.m_respawn_monsters = 0;
+			}
+ 
+			waitforeverybody();
+ 
+			for(c=connecthead;c>=0;c=connectpoint2[c])
+            {
+				resetweapons(c);
+				resetinventory(c);
+            }
+ 
+			newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);
+
+            enterlevel(MODE_GAME); //start game.
+ 
+		}
+		else
+		{
+			Logo(); //play logo, (game must be started via menus).
+		}
+	}
+ 
+	else if(ud.warp_on == 1) //if cmd arguments /V and /L are given.
+    {
+ 
+		if (numplayers > 1) //if in multiplayer reset everyones weapon status.
+		{
+			int c;
+   
+            switch(ud.m_coop)	//set item spawn options, as they would be if
+			{					//game was started via main menu. 
+				case 0:
+					ud.respawn_inventory = ud.m_respawn_inventory = 1;
+					ud.respawn_items = ud.m_respawn_items = 1;
+					break;
+				case 1:
+					ud.respawn_inventory = ud.m_respawn_inventory = 1;
+					ud.respawn_items = ud.m_respawn_items = 0;
+					break;
+				case 2:
+					ud.respawn_inventory = ud.m_respawn_inventory = 0;
+					ud.respawn_items = ud.m_respawn_items = 0;
+					break;
+			}
+ 
+			if( ud.m_player_skill == 4 ) 
+			{
+				ud.m_respawn_monsters = 1; //set skill
+			}
+			else
+			{
+				ud.m_respawn_monsters = 0;
+			}
+ 
+			waitforeverybody();
+   
+			for(c=connecthead;c>=0;c=connectpoint2[c])
+            {
+			    resetweapons(c); //without this players would spawn with no weapon.
+				resetinventory(c);
+            }
+ 
+		}
+ 
+		newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);
+		enterlevel(MODE_GAME); //start game.
+  
+    }
+    else 
+	{
+		vscrn();
+	}
+
+    if( ud.warp_on == 0 && playback() )
+    {
+        FX_StopAllSounds();
+        clearsoundlocks();
+        nomorelogohack = 1;
+        goto MAIN_LOOP_RESTART;
+    }
+
+    ud.warp_on = 0;
+
+    while ( !(ps[myconnectindex].gm&MODE_END) ) //The whole loop!!!!!!!!!!!!!!!!!!
+    {
+    		sampletimer();
+        if( ud.recstat == 2 || ud.multimode > 1 || ( ud.show_help == 0 && (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU ) )
+            if( ps[myconnectindex].gm&MODE_GAME )
+			{
+#pragma message (" It's stuck here ")
+				//printf("ps[myconnectindex].gm&MODE_GAME\n");
+				if( moveloop() ) 
+				{
+					continue;
+				}
+			}
+
+        if( ps[myconnectindex].gm&MODE_EOL || ps[myconnectindex].gm&MODE_RESTART )
+        {
+
+            if( ps[myconnectindex].gm&MODE_EOL )
+            {
+#ifdef ONELEVELDEMO
+                gameexit(" ");
+#endif
+                closedemowrite();
+
+                ready2send = 0;
+
+                i = ud.screen_size;
+                ud.screen_size = 0;
+                vscrn();
+                ud.screen_size = i;
+                dobonus(0);
+
+                if(ud.eog)
+                {
+                    ud.eog = 0;
+                    if(ud.multimode < 2)
+                    {
+						if(VOLUMEONE)
+                        	doorders();
+
+                        ps[myconnectindex].gm = MODE_MENU;
+                        cmenu(0);
+                        probey = 0;
+                        goto MAIN_LOOP_RESTART;
+                    }
+                    else
+                    {
+                        ud.m_level_number = 0;
+                        ud.level_number = 0;
+                    }
+                }
+            }
+
+            ready2send = 0;
+            if(numplayers > 1) ps[myconnectindex].gm = MODE_GAME;
+
+            enterlevel(ps[myconnectindex].gm);
+            continue;
+        }
+
+        cheats();
+
+        if( !CONSOLE_IsActive() )
+        {
+          nonsharedkeys();
+        }
+
+        if( (ud.show_help == 0 && ud.multimode < 2 && !(ps[myconnectindex].gm&MODE_MENU) ) || ud.multimode > 1 || ud.recstat == 2)
+            i = min(max((totalclock-ototalclock)*(65536L/TICSPERFRAME),0),65536);
+        else
+            i = 65536;
+
+        displayrooms(screenpeek,i);
+        displayrest(i);
+
+//        if( KB_KeyPressed(sc_F) )
+//        {
+//            KB_ClearKeyDown(sc_F);
+//            addplayer();
+//        }
+
+        if(ps[myconnectindex].gm&MODE_DEMO)
+            goto MAIN_LOOP_RESTART;
+
+        if(debug_on) caches();
+
+        checksync();
+
+		if (VOLUMEONE)
+        	if(ud.show_help == 0 && show_shareware > 0 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
+            	rotatesprite((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128,0,0,xdim-1,ydim-1);
+
+        nextpage();
+    }
+
+    gameexit(" ");
+	return(0);
+}
+
+char opendemoread(char which_demo) // 0 = mine
+{
+    char d[] = "demo_.dmo";
+    char *fname = d;
+    char ver;
+    short i,j;
+	char firstdemofile_[512];
+	int32 dummy;
+	long groupefil_crc32_from_demo[MAXGROUPFILES];
+
+    if(which_demo == 10)
+        d[4] = 'x';
+    else
+        d[4] = '0' + which_demo;
+
+    ud.reccnt = 0;
+
+     if(which_demo == 1 && firstdemofile[0] != 0)
+     {
+		fname = firstdemofile;
+		if ((recfilep = TCkopen4load(firstdemofile,0)) == -1)
+		{
+			return(0);
+		}
+     }
+     else
+	 {
+		 if ((recfilep = TCkopen4load(d,0)) == -1)
+		 {
+			return(0);
+		 }
+	 }
+
+     kread(recfilep,&ud.reccnt,sizeof(long));
+     kread(recfilep,&ver,sizeof(char));
+	
+	 printf("%s has version = %d\n", fname, ver);
+
+	// FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116, 117 and 118)
+	if (PLUTOPAK)
+	{
+		if( (ver != BYTEVERSION && ver != BYTEVERSION_116 && ver != BYTEVERSION_117 && ver != BYTEVERSION_118) ) // || (ud.reccnt < 512) )
+		{
+			printf("%s is a demo version %d. We want v. %d, %d, %d, or %d (1.5 Atomic versions)\n",
+					fname, (int) ver, BYTEVERSION_116, BYTEVERSION_117, BYTEVERSION_118, BYTEVERSION);
+			kclose(recfilep);
+			return 0;
+		}
+	}
+	else // 1.3/1.3d style
+	{
+		if( (ver != BYTEVERSION && ver != BYTEVERSION_27 && ver != BYTEVERSION_28 && ver != BYTEVERSION_29) ) // || (ud.reccnt < 512) )
+		{
+			printf("%s is a demo version %d. We want v. %d, %d, %d or %d (1.3/1.3d versions)\n",
+					fname, (int) ver, BYTEVERSION_27, BYTEVERSION_28, BYTEVERSION_29, BYTEVERSION);
+			kclose(recfilep);
+			return 0;
+		}
+	}
+
+	// FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
+	if(ver==BYTEVERSION)
+	{
+		kread(recfilep, (long *)groupefil_crc32_from_demo, sizeof(groupefil_crc32_from_demo));
+	
+		for(i=0; i<MAXGROUPFILES; i++)
+			if(groupefil_crc32_from_demo[i]!=groupefil_crc32[i])
+			{
+				for(j=0; j<=i; j++)
+				{
+					printf("You have GRP #%d:  %s (CRC32=%X)\n"
+						   "this demo expects %s (CRC32=%X)\n",
+						j, grpVersion2char_from_crc(groupefil_crc32[j]),groupefil_crc32[j],
+						grpVersion2char_from_crc(groupefil_crc32_from_demo[j]),groupefil_crc32_from_demo[j]);
+
+				}
+				kclose(recfilep);
+				return 0;
+			}
+
+
+	}
+
+	 ud.playing_demo_rev = ver;
+
+	 kread(recfilep,(char *)&ud.volume_number,sizeof(char));
+     kread(recfilep,(char *)&ud.level_number,sizeof(char));
+     kread(recfilep,(char *)&ud.player_skill,sizeof(char));
+     kread(recfilep,(char *)&ud.m_coop,sizeof(char));
+     kread(recfilep,(char *)&ud.m_ffire,sizeof(char));
+     kread(recfilep,(short *)&ud.multimode,sizeof(short));
+     kread(recfilep,(short *)&ud.m_monsters_off,sizeof(short));
+     kread(recfilep,(int32 *)&ud.m_respawn_monsters,sizeof(int32));
+     kread(recfilep,(int32 *)&ud.m_respawn_items,sizeof(int32));
+     kread(recfilep,(int32 *)&ud.m_respawn_inventory,sizeof(int32));
+     kread(recfilep,(int32 *)&ud.playerai,sizeof(int32));
+     kread(recfilep,(char *)&ud.user_name[0][0],sizeof(ud.user_name));
+	 // FIX_00034: Demos do not turn your run mode off anymore:
+     kread(recfilep,(int32 *)&dummy /*ud.auto_run*/,sizeof(int32)); // not needed and would affect autorun status in duke3d.cfg when quitting duke from a demo
+     kread(recfilep,(char *)boardfilename,sizeof(boardfilename));
+     if( boardfilename[0] != 0 )
+     {
+        ud.m_level_number = 7;
+        ud.m_volume_number = 0;
+     }
+
+     for(i=0;i<ud.multimode;i++)
+	 {
+        kread(recfilep,(int32 *)&ps[i].aim_mode,sizeof(char));
+		
+		// FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v27/28/29/116/117/118. New: v30/v119.
+		if(ver==BYTEVERSION) 
+			kread(recfilep,ud.wchoice[i],sizeof(ud.wchoice[0]));
+	 }
+
+     ud.god = ud.cashman = ud.eog = ud.showallmap = 0;
+     ud.clipping = ud.scrollmode = ud.overhead_on = 0;
+	 // FIX_00034: Demos do not turn your run mode off anymore:
+     /* ud.showweapons =  */ ud.pause_on /*= ud.auto_run */ = 0; // makes no sense to reset those 2 value!
+
+         newgame(ud.volume_number,ud.level_number,ud.player_skill);
+         return(1);
+}
+
+
+void opendemowrite(void)
+{
+    char d[] = "demo1.dmo";
+    long dummylong = 0;
+    char ver;
+    short i;
+	char fullpathdemofilename[16];
+
+    if(ud.recstat == 2) kclose(recfilep);
+
+    ver = BYTEVERSION;
+
+	// Are we loading a TC?
+	if(game_dir[0] != '\0')
+	{
+		// Yes
+		sprintf(fullpathdemofilename, "%s\\%s", game_dir, d);
+	}
+	else
+	{
+		// No 
+		sprintf(fullpathdemofilename, "%s", d);
+	}
+
+// CTW - MODIFICATION
+//  if ((frecfilep = fopen(d,"wb")) == -1) return;
+    if ((frecfilep = fopen(fullpathdemofilename,"wb")) == NULL) return;
+// CTW END - MODIFICATION
+    fwrite(&dummylong,4,1,frecfilep);
+    fwrite(&ver,sizeof(char),1,frecfilep);
+	// FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
+	fwrite((long *)groupefil_crc32,sizeof(groupefil_crc32),1,frecfilep);
+    fwrite((char *)&ud.volume_number,sizeof(char),1,frecfilep);
+    fwrite((char *)&ud.level_number,sizeof(char),1,frecfilep);
+    fwrite((char *)&ud.player_skill,sizeof(char),1,frecfilep);
+    fwrite((char *)&ud.m_coop,sizeof(char),1,frecfilep);
+    fwrite((char *)&ud.m_ffire,sizeof(char),1,frecfilep);
+    fwrite((short *)&ud.multimode,sizeof(short),1,frecfilep);
+    fwrite((short *)&ud.m_monsters_off,sizeof(short),1,frecfilep);
+    fwrite((int32 *)&ud.m_respawn_monsters,sizeof(int32),1,frecfilep);
+    fwrite((int32 *)&ud.m_respawn_items,sizeof(int32),1,frecfilep);
+    fwrite((int32 *)&ud.m_respawn_inventory,sizeof(int32),1,frecfilep);
+    fwrite((int32 *)&ud.playerai,sizeof(int32),1,frecfilep);
+    fwrite((char *)&ud.user_name[0][0],sizeof(ud.user_name),1,frecfilep);
+    fwrite((int32 *)&ud.auto_run,sizeof(int32),1,frecfilep);
+    fwrite((char *)boardfilename,sizeof(boardfilename),1,frecfilep);
+
+    for(i=0;i<ud.multimode;i++)
+	{    
+		fwrite((int32 *)&ps[i].aim_mode,sizeof(char),1,frecfilep); // seems wrong; prolly not needed anyway
+		// FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v27/28/29/116/117/118. New: v30/v119.
+		fwrite(ud.wchoice[i],sizeof(ud.wchoice[0]),1,frecfilep);
+	}
+
+    totalreccnt = 0;
+    ud.reccnt = 0;
+}
+
+void record(void)
+{
+    short i;
+#ifdef DBGRECORD
+	FILE *pFile;
+#endif
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+         {
+         copybufbyte(&sync[i],&recsync[ud.reccnt],sizeof(input));
+#ifdef DBGRECORD
+		 pFile = fopen("c:\\temp\\record.txt","a");
+			   fprintf(pFile,"i=%-5d a=%-2x b=%-8x f=%-4x h=%-2x s=%-4x s=%-8x\n", 
+				   ud.reccnt, recsync[ud.reccnt].avel&0xFF, recsync[ud.reccnt].bits&0xFFFFFFFF, recsync[ud.reccnt].fvel&0xFFFF,
+				   recsync[ud.reccnt].horz&0xFF, recsync[ud.reccnt].svel&0xFFFF, randomseed&0xFFFFFFFF);
+			   fclose(pFile);
+#endif
+			   ud.reccnt++;
+                 totalreccnt++;
+                 if (ud.reccnt >= RECSYNCBUFSIZ)
+                 {
+              dfwrite(recsync,sizeof(input)*ud.multimode,ud.reccnt/ud.multimode,frecfilep);
+                          ud.reccnt = 0;
+                 }
+         }
+}
+
+void closedemowrite(void)
+{
+         if (ud.recstat == 1)
+         {
+        if (ud.reccnt > 0)
+        {
+            dfwrite(recsync,sizeof(input)*ud.multimode,ud.reccnt/ud.multimode,frecfilep);
+
+            fseek(frecfilep,SEEK_SET,0L);
+            fwrite(&totalreccnt,sizeof(long),1,frecfilep);
+            ud.recstat = ud.m_recstat = 0;
+        }
+        fclose(frecfilep);
+        frecfilep = NULL;
+    }
+}
+
+// CTW - MODIFICATION
+// On my XP machine, demo playback causes the game to crash shortly in.
+// Only bug found so far, not sure if it's OS dependent or compiler or what.
+// Seems to happen when player input starts being simulated, but just guessing.
+// This change effectively disables it. The related code is still enabled.
+// (This is working on Linux, so I flipped it back to '1'. --ryan.)
+ char which_demo = 1;
+// CTW END - MODIFICATION
+
+char in_menu = 0;
+
+// extern long syncs[];
+long playback(void)
+{
+    long i,j,k,l,t;
+    short p;
+    char foundemo;
+#ifdef DBGRECORD
+	FILE * pFile;
+#endif
+    if( ready2send ) 
+	{
+		return 0;
+	}
+
+	/*
+	if(numplayers > 1)
+		return 1;
+	*/
+
+    foundemo = 0;
+
+    RECHECK:
+
+    in_menu = ps[myconnectindex].gm&MODE_MENU;
+
+    pub = NUMPAGES;
+    pus = NUMPAGES;
+
+    flushperms();
+
+	if(numplayers < 2 && ud.multimode_bot<2) foundemo = opendemoread(which_demo);
+
+    if(foundemo == 0)
+    {
+
+        if(which_demo > 1)
+        {
+            which_demo = 1;
+            goto RECHECK;
+        }
+        for(t=0;t<63;t+=7) palto(0,0,0,t);
+        drawbackground();
+
+        CONSOLE_HandleInput();
+        if( !CONSOLE_IsActive())
+        {
+            menus();
+        }
+        CONSOLE_Render();
+        ps[myconnectindex].palette = palette;
+        nextpage();
+        for(t=63;t>0;t-=7) 
+		{
+			palto(0,0,0,t);
+		}
+
+        ud.reccnt = 0;
+    }
+    else
+    {
+        ud.recstat = 2;
+        which_demo++;
+        if(which_demo == 10) 
+		{
+			which_demo = 1;
+		}
+
+        enterlevel(MODE_DEMO);
+    }
+
+    if(foundemo == 0 || in_menu || KB_KeyWaiting() || numplayers > 1)
+    {
+        FX_StopAllSounds();
+        clearsoundlocks();
+        ps[myconnectindex].gm |= MODE_MENU;
+    }
+
+    ready2send = 0;
+    i = 0;
+
+    KB_FlushKeyboardQueue();
+
+    k = 0;
+
+    while (ud.reccnt > 0 || foundemo == 0)
+    {
+
+        if(foundemo) while ( totalclock >= (lockclock+TICSPERFRAME) )
+        {
+            if ((i == 0) || (i >= RECSYNCBUFSIZ))
+            {
+                i = 0;
+                l = min(ud.reccnt,RECSYNCBUFSIZ);
+                kdfread(recsync,sizeof(input)*ud.multimode,l/ud.multimode,recfilep);
+            }
+
+            for(j=connecthead;j>=0;j=connectpoint2[j])
+            {
+               copybufbyte(&recsync[i],&inputfifo[movefifoend[j]&(MOVEFIFOSIZ-1)][j],sizeof(input));
+#ifdef DBGRECORD
+			   pFile = fopen("c:\\temp\\playback.txt","a");
+			   fprintf(pFile,"i=%-5d a=%-2x b=%-8x f=%-4x h=%-2x s=%-4x s=%-8x\n", 
+				   i, recsync[i].avel&0xFF, recsync[i].bits&0xFFFFFFFF, recsync[i].fvel&0xFFFF,
+				   recsync[i].horz&0xFF, recsync[i].svel&0xFFFF, randomseed&0xFFFFFFFF);
+			   fclose(pFile);
+#endif
+			   movefifoend[j]++;
+               i++;
+               ud.reccnt--;
+            }
+            domovethings();
+        }
+
+        if(foundemo == 0)
+            drawbackground();
+        else
+        {
+            if( !CONSOLE_IsActive() )
+            {
+                nonsharedkeys();
+            }
+
+            j = min(max((totalclock-lockclock)*(65536/TICSPERFRAME),0),65536);
+            displayrooms(screenpeek,j);
+            displayrest(j);
+
+            if(ud.multimode > 1 && ps[myconnectindex].gm )
+                getpackets();
+        }
+
+        if( (ps[myconnectindex].gm&MODE_MENU) && (ps[myconnectindex].gm&MODE_EOL) )
+		{
+			printf("playback(1) :: goto RECHECK:\n");
+			goto RECHECK;
+		}
+
+        if(ps[myconnectindex].gm&MODE_TYPE)
+        {
+            typemode();
+            if((ps[myconnectindex].gm&MODE_TYPE) != MODE_TYPE)
+                ps[myconnectindex].gm = MODE_MENU;
+        }
+        else
+        {
+            CONSOLE_HandleInput();
+            if( !CONSOLE_IsActive())
+            {
+                menus();
+            }
+            CONSOLE_Render();
+            if( ud.multimode > 1 )
+            {
+                ControlInfo noshareinfo;
+                if( !CONSOLE_IsActive() )
+                {
+                    CONTROL_GetInput( &noshareinfo );
+                    if( ACTION(gamefunc_SendMessage) )
+                    {
+                        KB_FlushKeyboardQueue();
+                        CONTROL_ClearAction( gamefunc_SendMessage );
+                        ps[myconnectindex].gm = MODE_TYPE;
+                        typebuf[0] = 0;
+                        inputloc = 0;
+                    }
+                }
+
+            }
+        }
+
+        operatefta();
+
+        if(ud.last_camsprite != ud.camerasprite)
+        {
+            ud.last_camsprite = ud.camerasprite;
+            ud.camera_time = totalclock+(TICRATE*2);
+        }
+
+		if (VOLUMEONE)
+			if( ud.show_help == 0 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
+				rotatesprite((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128,0,0,xdim-1,ydim-1);
+
+		getpackets();
+        nextpage();
+
+        if( ps[myconnectindex].gm==MODE_END || ps[myconnectindex].gm==MODE_GAME )
+        {
+            if(foundemo)
+                kclose(recfilep);
+            ud.playing_demo_rev = 0;
+			return 0;
+        }
+    }
+    kclose(recfilep);
+	ud.playing_demo_rev = 0;
+    if(ps[myconnectindex].gm&MODE_MENU)
+	{
+		goto RECHECK;
+	}
+
+    return 1;
+}
+
+char moveloop()
+{
+    long i;
+
+    if (numplayers > 1)
+	{
+		while (fakemovefifoplc < movefifoend[myconnectindex]) 
+		{
+			fakedomovethings();
+		}
+	}
+
+
+    getpackets();
+
+    if (numplayers < 2) bufferjitter = 0;
+    while (movefifoend[myconnectindex]-movefifoplc > bufferjitter)
+    {
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (movefifoplc == movefifoend[i]) break;
+        if (i >= 0) break;
+        if( domovethings() ) return 1;
+    }
+    return 0;
+}
+
+void fakedomovethingscorrect(void)
+{
+     long i;
+     struct player_struct *p;
+
+     if (numplayers < 2) return;
+
+     i = ((movefifoplc-1)&(MOVEFIFOSIZ-1));
+     p = &ps[myconnectindex];
+
+     if (p->posx == myxbak[i] && p->posy == myybak[i] && p->posz == myzbak[i]
+          && p->horiz == myhorizbak[i] && p->ang == myangbak[i]) return;
+
+     myx = p->posx; omyx = p->oposx; myxvel = p->posxv;
+     myy = p->posy; omyy = p->oposy; myyvel = p->posyv;
+     myz = p->posz; omyz = p->oposz; myzvel = p->poszv;
+     myang = p->ang; omyang = p->oang;
+     mycursectnum = p->cursectnum;
+     myhoriz = p->horiz; omyhoriz = p->ohoriz;
+     myhorizoff = p->horizoff; omyhorizoff = p->ohorizoff;
+     myjumpingcounter = p->jumping_counter;
+     myjumpingtoggle = p->jumping_toggle;
+     myonground = p->on_ground;
+     myhardlanding = p->hard_landing;
+     myreturntocenter = p->return_to_center;
+
+     fakemovefifoplc = movefifoplc;
+     while (fakemovefifoplc < movefifoend[myconnectindex])
+          fakedomovethings();
+
+}
+
+void fakedomovethings(void)
+{
+        input *syn;
+        struct player_struct *p;
+        long i, j, k, doubvel, fz, cz, hz, lz, x, y;
+        unsigned long sb_snum;
+        short psect, psectlotag, tempsect, backcstat;
+        char shrunk, spritebridge;
+
+        syn = (input *)&inputfifo[fakemovefifoplc&(MOVEFIFOSIZ-1)][myconnectindex];
+
+        p = &ps[myconnectindex];
+
+        backcstat = sprite[p->i].cstat;
+        sprite[p->i].cstat &= ~257;
+
+        sb_snum = syn->bits;
+
+        psect = mycursectnum;
+        psectlotag = sector[psect].lotag;
+        spritebridge = 0;
+
+        shrunk = (sprite[p->i].yrepeat < 32);
+
+        if( ud.clipping == 0 && ( sector[psect].floorpicnum == MIRROR || psect < 0 || psect >= MAXSECTORS) )
+        {
+            myx = omyx;
+            myy = omyy;
+        }
+        else
+        {
+            omyx = myx;
+            omyy = myy;
+        }
+
+        omyhoriz = myhoriz;
+        omyhorizoff = myhorizoff;
+        omyz = myz;
+        omyang = myang;
+
+        getzrange(myx,myy,myz,psect,&cz,&hz,&fz,&lz,163L,CLIPMASK0);
+
+        j = getflorzofslope(psect,myx,myy);
+
+        if( (lz&49152) == 16384 && psectlotag == 1 && klabs(myz-j) > PHEIGHT+(16<<8) )
+            psectlotag = 0;
+
+        if( p->aim_mode == 0 && myonground && psectlotag != 2 && (sector[psect].floorstat&2) )
+        {
+                x = myx+(sintable[(myang+512)&2047]>>5);
+                y = myy+(sintable[myang&2047]>>5);
+                tempsect = psect;
+                updatesector(x,y,&tempsect);
+                if (tempsect >= 0)
+                {
+                     k = getflorzofslope(psect,x,y);
+                     if (psect == tempsect)
+                          myhorizoff += mulscale16(j-k,160);
+                     else if (klabs(getflorzofslope(tempsect,x,y)-k) <= (4<<8))
+                          myhorizoff += mulscale16(j-k,160);
+                }
+        }
+        if (myhorizoff > 0) myhorizoff -= ((myhorizoff>>3)+1);
+        else if (myhorizoff < 0) myhorizoff += (((-myhorizoff)>>3)+1);
+
+        if(hz >= 0 && (hz&49152) == 49152)
+        {
+                hz &= (MAXSPRITES-1);
+                if (sprite[hz].statnum == 1 && sprite[hz].extra >= 0)
+                {
+                    hz = 0;
+                    cz = getceilzofslope(psect,myx,myy);
+                }
+        }
+
+        if(lz >= 0 && (lz&49152) == 49152)
+        {
+                 j = lz&(MAXSPRITES-1);
+                 if ((sprite[j].cstat&33) == 33)
+                 {
+                        psectlotag = 0;
+                        spritebridge = 1;
+                 }
+                 if(badguy(&sprite[j]) && sprite[j].xrepeat > 24 && klabs(sprite[p->i].z-sprite[j].z) < (84<<8) )
+                 {
+                    j = getangle( sprite[j].x-myx,sprite[j].y-myy);
+                    myxvel -= sintable[(j+512)&2047]<<4;
+                    myyvel -= sintable[j&2047]<<4;
+                }
+        }
+
+        if( sprite[p->i].extra <= 0 )
+        {
+                 if( psectlotag == 2 )
+                 {
+                            if(p->on_warping_sector == 0)
+                            {
+                                     if( klabs(myz-fz) > (PHEIGHT>>1))
+                                             myz += 348;
+                            }
+                            clipmove(&myx,&myy,&myz,&mycursectnum,0,0,164L,(4L<<8),(4L<<8),CLIPMASK0);
+                 }
+
+                 updatesector(myx,myy,&mycursectnum);
+                 pushmove(&myx,&myy,&myz,&mycursectnum,128L,(4L<<8),(20L<<8),CLIPMASK0);
+
+                myhoriz = 100;
+                myhorizoff = 0;
+
+                 goto ENDFAKEPROCESSINPUT;
+        }
+
+        doubvel = TICSPERFRAME;
+
+        if(p->on_crane >= 0) goto FAKEHORIZONLY;
+
+        if(p->one_eighty_count < 0) myang += 128;
+
+        i = 40;
+
+        if( psectlotag == 2)
+        {
+                 myjumpingcounter = 0;
+
+                 if ( sb_snum&1 )
+                 {
+                            if(myzvel > 0) myzvel = 0;
+                            myzvel -= 348;
+                            if(myzvel < -(256*6)) myzvel = -(256*6);
+                 }
+                 else if (sb_snum&(1<<1))
+                 {
+                            if(myzvel < 0) myzvel = 0;
+                            myzvel += 348;
+                            if(myzvel > (256*6)) myzvel = (256*6);
+                 }
+                 else
+                 {
+                    if(myzvel < 0)
+                    {
+                        myzvel += 256;
+                        if(myzvel > 0)
+                            myzvel = 0;
+                    }
+                    if(myzvel > 0)
+                    {
+                        myzvel -= 256;
+                        if(myzvel < 0)
+                            myzvel = 0;
+                    }
+                }
+
+                if(myzvel > 2048) myzvel >>= 1;
+
+                 myz += myzvel;
+
+                 if(myz > (fz-(15<<8)) )
+                            myz += ((fz-(15<<8))-myz)>>1;
+
+                 if(myz < (cz+(4<<8)) )
+                 {
+                            myz = cz+(4<<8);
+                            myzvel = 0;
+                 }
+        }
+
+        else if(p->jetpack_on)
+        {
+                 myonground = 0;
+                 myjumpingcounter = 0;
+                 myhardlanding = 0;
+
+                 if(p->jetpack_on < 11)
+                            myz -= (p->jetpack_on<<7); //Goin up
+
+                 if(shrunk) j = 512;
+                 else j = 2048;
+
+                 if (sb_snum&1)                            //A
+                            myz -= j;
+                 if (sb_snum&(1<<1))                       //Z
+                            myz += j;
+
+                 if(shrunk == 0 && ( psectlotag == 0 || psectlotag == 2 ) ) k = 32;
+                 else k = 16;
+
+                 if(myz > (fz-(k<<8)) )
+                            myz += ((fz-(k<<8))-myz)>>1;
+                 if(myz < (cz+(18<<8)) )
+                            myz = cz+(18<<8);
+        }
+        else if( psectlotag != 2 )
+        {
+            if (psectlotag == 1 && p->spritebridge == 0)
+            {
+                 if(shrunk == 0) i = 34;
+                 else i = 12;
+            }
+                 if(myz < (fz-(i<<8)) && (floorspace(psect)|ceilingspace(psect)) == 0 ) //falling
+                 {
+                            if( (sb_snum&3) == 0 && myonground && (sector[psect].floorstat&2) && myz >= (fz-(i<<8)-(16<<8) ) )
+                                     myz = fz-(i<<8);
+                            else
+                            {
+                                     myonground = 0;
+
+                                     myzvel += (gc+80);
+
+                                     if(myzvel >= (4096+2048)) myzvel = (4096+2048);
+                            }
+                 }
+
+                 else
+                 {
+                            if(psectlotag != 1 && psectlotag != 2 && myonground == 0 && myzvel > (6144>>1))
+                                 myhardlanding = myzvel>>10;
+                            myonground = 1;
+
+                            if(i==40)
+                            {
+                                     //Smooth on the ground
+
+                                     k = ((fz-(i<<8))-myz)>>1;
+                                     if( klabs(k) < 256 ) k = 0;
+                                     myz += k; // ((fz-(i<<8))-myz)>>1;
+                                     myzvel -= 768; // 412;
+                                     if(myzvel < 0) myzvel = 0;
+                            }
+                            else if(myjumpingcounter == 0)
+                            {
+                                myz += ((fz-(i<<7))-myz)>>1; //Smooth on the water
+                                if(p->on_warping_sector == 0 && myz > fz-(16<<8))
+                                {
+                                    myz = fz-(16<<8);
+                                    myzvel >>= 1;
+                                }
+                            }
+
+                            if( sb_snum&2 )
+                                     myz += (2048+768);
+
+                            if( (sb_snum&1) == 0 && myjumpingtoggle == 1)
+                                     myjumpingtoggle = 0;
+
+                            else if( (sb_snum&1) && myjumpingtoggle == 0 )
+                            {
+                                     if( myjumpingcounter == 0 )
+                                             if( (fz-cz) > (56<<8) )
+                                             {
+                                                myjumpingcounter = 1;
+                                                myjumpingtoggle = 1;
+                                             }
+                            }
+                            if( myjumpingcounter && (sb_snum&1) == 0 )
+                                myjumpingcounter = 0;
+                 }
+
+                 if(myjumpingcounter)
+                 {
+                            if( (sb_snum&1) == 0 && myjumpingtoggle == 1)
+                                     myjumpingtoggle = 0;
+
+                            if( myjumpingcounter < (1024+256) )
+                            {
+                                     if(psectlotag == 1 && myjumpingcounter > 768)
+                                     {
+                                             myjumpingcounter = 0;
+                                             myzvel = -512;
+                                     }
+                                     else
+                                     {
+                                             myzvel -= (sintable[(2048-128+myjumpingcounter)&2047])/12;
+                                             myjumpingcounter += 180;
+
+                                             myonground = 0;
+                                     }
+                            }
+                            else
+                            {
+                                     myjumpingcounter = 0;
+                                     myzvel = 0;
+                            }
+                 }
+
+                 myz += myzvel;
+
+                 if(myz < (cz+(4<<8)) )
+                 {
+                            myjumpingcounter = 0;
+                            if(myzvel < 0) myxvel = myyvel = 0;
+                            myzvel = 128;
+                            myz = cz+(4<<8);
+                 }
+
+        }
+
+        if ( p->fist_incs ||
+                     p->transporter_hold > 2 ||
+                     myhardlanding ||
+                     p->access_incs > 0 ||
+                     p->knee_incs > 0 ||
+                     (p->curr_weapon == TRIPBOMB_WEAPON &&
+                      p->kickback_pic > 1 &&
+                      p->kickback_pic < 4 ) )
+        {
+                 doubvel = 0;
+                 myxvel = 0;
+                 myyvel = 0;
+        }
+        else if ( syn->avel )          //p->ang += syncangvel * constant
+        {                         //ENGINE calculates angvel for you
+            long tempang;
+
+            tempang = syn->avel<<1;
+
+            if(psectlotag == 2)
+                myang += (tempang-(tempang>>3))*sgn(doubvel);
+            else myang += (tempang)*sgn(doubvel);
+            myang &= 2047;
+        }
+
+        if ( myxvel || myyvel || syn->fvel || syn->svel )
+        {
+                 if(p->steroids_amount > 0 && p->steroids_amount < 400)
+                     doubvel <<= 1;
+
+                 myxvel += ((syn->fvel*doubvel)<<6);
+                 myyvel += ((syn->svel*doubvel)<<6);
+
+                 if( ( p->curr_weapon == KNEE_WEAPON && p->kickback_pic > 10 && myonground ) || ( myonground && (sb_snum&2) ) )
+                 {
+                            myxvel = mulscale16(myxvel,dukefriction-0x2000);
+                            myyvel = mulscale16(myyvel,dukefriction-0x2000);
+                 }
+                 else
+                 {
+                    if(psectlotag == 2)
+                    {
+                        myxvel = mulscale16(myxvel,dukefriction-0x1400);
+                        myyvel = mulscale16(myyvel,dukefriction-0x1400);
+                    }
+                    else
+                    {
+                        myxvel = mulscale16(myxvel,dukefriction);
+                        myyvel = mulscale16(myyvel,dukefriction);
+                    }
+                 }
+
+                 if( abs(myxvel) < 2048 && abs(myyvel) < 2048 )
+                     myxvel = myyvel = 0;
+
+                 if( shrunk )
+                 {
+                     myxvel =
+                         mulscale16(myxvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2));
+                     myyvel =
+                         mulscale16(myyvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2));
+                 }
+        }
+
+FAKEHORIZONLY:
+        if(psectlotag == 1 || spritebridge == 1) i = (4L<<8); else i = (20L<<8);
+
+        clipmove(&myx,&myy,&myz,&mycursectnum,myxvel,myyvel,164L,4L<<8,i,CLIPMASK0);
+        pushmove(&myx,&myy,&myz,&mycursectnum,164L,4L<<8,4L<<8,CLIPMASK0);
+
+        if( p->jetpack_on == 0 && psectlotag != 1 && psectlotag != 2 && shrunk)
+            myz += 30<<8;
+
+        if ((sb_snum&(1<<18)) || myhardlanding)
+            myreturntocenter = 9;
+
+        if (sb_snum&(1<<13))
+        {
+                myreturntocenter = 9;
+                if (sb_snum&(1<<5)) myhoriz += 6;
+                myhoriz += 6;
+        }
+        else if (sb_snum&(1<<14))
+        {
+                myreturntocenter = 9;
+                if (sb_snum&(1<<5)) myhoriz -= 6;
+                myhoriz -= 6;
+        }
+        else if (sb_snum&(1<<3))
+        {
+                if (sb_snum&(1<<5)) myhoriz += 6;
+                myhoriz += 6;
+        }
+        else if (sb_snum&(1<<4))
+        {
+                if (sb_snum&(1<<5)) myhoriz -= 6;
+                myhoriz -= 6;
+        }
+
+        if (myreturntocenter > 0)
+            if ((sb_snum&(1<<13)) == 0 && (sb_snum&(1<<14)) == 0)
+        {
+             myreturntocenter--;
+             myhoriz += 33-(myhoriz/3);
+        }
+
+        if(p->aim_mode)
+            myhoriz += syn->horz>>1;
+        else
+        {
+            if( myhoriz > 95 && myhoriz < 105) myhoriz = 100;
+            if( myhorizoff > -5 && myhorizoff < 5) myhorizoff = 0;
+        }
+
+        if (myhardlanding > 0)
+        {
+            myhardlanding--;
+            myhoriz -= (myhardlanding<<4);
+        }
+
+        if (myhoriz > 299) myhoriz = 299;
+        else if (myhoriz < -99) myhoriz = -99;
+
+        if(p->knee_incs > 0)
+        {
+            myhoriz -= 48;
+            myreturntocenter = 9;
+        }
+
+
+ENDFAKEPROCESSINPUT:
+
+        myxbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myx;
+        myybak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myy;
+        myzbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myz;
+        myangbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myang;
+        myhorizbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myhoriz;
+        fakemovefifoplc++;
+
+        sprite[p->i].cstat = backcstat;
+}
+
+
+char domovethings(void)
+{
+    short i, j;
+    char ch;
+
+#ifdef DBGRECORD
+	FILE *pFile;
+#endif
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if( sync[i].bits&(1<<17) )
+    {
+        multiflag = 2;
+        multiwhat = (sync[i].bits>>18)&1;
+        multipos = (unsigned) (sync[i].bits>>19)&15;
+        multiwho = i;
+
+        if( multiwhat )
+        {
+			// FIX_00058: Save/load game crash in both single and multiplayer
+            screencapt = 1;
+            displayrooms(myconnectindex,65536);
+            savetemp("duke3d.tmp",waloff[MAXTILES-1],160*100);
+            screencapt = 0;
+
+            saveplayer( multipos );
+            multiflag = 0;
+
+            if(multiwho != myconnectindex)
+            {
+                strcpy(fta_quotes[122],&ud.user_name[multiwho][0]);
+                strcat(fta_quotes[122]," SAVED A MULTIPLAYER GAME");
+                FTA(122,&ps[myconnectindex],1);
+            }
+            else
+            {
+                strcpy(fta_quotes[122],"MULTIPLAYER GAME SAVED");
+                FTA(122,&ps[myconnectindex],1);
+            }
+            break;
+        }
+        else
+        {
+//            waitforeverybody();
+
+            j = loadplayer( multipos );
+
+            multiflag = 0;
+
+            if(j == 0)
+            {
+                if(multiwho != myconnectindex)
+                {
+                    strcpy(fta_quotes[122],&ud.user_name[multiwho][0]);
+                    strcat(fta_quotes[122]," LOADED A MULTIPLAYER GAME");
+                    FTA(122,&ps[myconnectindex],1);
+                }
+                else
+                {
+                    strcpy(fta_quotes[122],"MULTIPLAYER GAME LOADED");
+                    FTA(122,&ps[myconnectindex],1);
+                }
+                return 1;
+            }
+        }
+    }
+
+    ud.camerasprite = -1;
+    lockclock += TICSPERFRAME;
+
+    if(earthquaketime > 0) earthquaketime--;
+    if(rtsplaying > 0) rtsplaying--;
+
+    for(i=0;i<MAXUSERQUOTES;i++)
+         if (user_quote_time[i])
+         {
+             user_quote_time[i]--;
+             if (!user_quote_time[i]) pub = NUMPAGES;
+         }
+     if ((klabs(quotebotgoal-quotebot) <= 16) && (ud.screen_size <= 8))
+         quotebot += ksgn(quotebotgoal-quotebot);
+     else
+         quotebot = quotebotgoal;
+
+    if( show_shareware > 0 )
+    {
+        show_shareware--;
+        if(show_shareware == 0)
+        {
+            pus = NUMPAGES;
+            pub = NUMPAGES;
+        }
+    }
+
+    everyothertime++;
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        copybufbyte(&inputfifo[movefifoplc&(MOVEFIFOSIZ-1)][i],&sync[i],sizeof(input));
+    movefifoplc++;
+
+    updateinterpolations();
+
+    j = -1;
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+     {
+          if ((sync[i].bits&(1<<26)) == 0) { j = i; continue; }
+
+          closedemowrite();
+
+          if (i == myconnectindex) gameexit(" ");
+          if (screenpeek == i)
+          {
+                screenpeek = connectpoint2[i];
+                if (screenpeek < 0) screenpeek = connecthead;
+          }
+
+          if (i == connecthead) connecthead = connectpoint2[connecthead];
+          else connectpoint2[j] = connectpoint2[i];
+
+          numplayers--;
+          ud.multimode--;
+
+          if (numplayers < 2)
+              sound(GENERIC_AMBIENCE17);
+
+          pub = NUMPAGES;
+          pus = NUMPAGES;
+          vscrn();
+
+          sprintf(buf,"%s is history!",ud.user_name[i]);
+
+          quickkill(&ps[i]);
+          deletesprite(ps[i].i);
+
+          adduserquote(buf);
+
+          if(j < 0 && networkmode == 0 )
+              gameexit( " \nThe 'MASTER/First player' just quit the game.  All\nplayers are returned from the game. This only happens in 5-8\nplayer mode as a different network scheme is used.");
+      }
+
+      if ((numplayers >= 2) && ((movefifoplc&7) == 7))
+      {
+            ch = (char)(randomseed&255);
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                 ch += ((ps[i].posx+ps[i].posy+ps[i].posz+ps[i].ang+ps[i].horiz)&255);
+            syncval[myconnectindex][syncvalhead[myconnectindex]&(MOVEFIFOSIZ-1)] = ch;
+            syncvalhead[myconnectindex]++;
+
+#ifdef DBGRECORD
+			pFile = fopen("c:\\temp\\synch.txt","a");
+		             for(i=connecthead;i>=0;i=connectpoint2[i])
+			   fprintf(pFile,"i=%-1d x=%-8x y=%-8x z=%-8x a=%-4x h=%-8x s=%-8x\n", 
+				   i, ps[i].posx, ps[i].posy, ps[i].posz, ps[i].ang, ps[i].horiz,
+				   randomseed&0xFFFFFFFF);
+			   fclose(pFile);
+#endif
+      }
+
+    if(ud.recstat == 1) record();
+
+    if( ud.pause_on == 0 )
+    {
+        global_random = TRAND;
+        movedummyplayers();//ST 13
+    }
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+    {
+        cheatkeys(i);
+
+        if( ud.pause_on == 0 )
+        {
+            processinput(i);
+            checksectors(i);
+        }
+    }
+
+    if( ud.pause_on == 0 )
+    {
+        movefta();//ST 2
+        moveweapons();          //ST 5 (must be last)
+        movetransports();       //ST 9
+
+        moveplayers();          //ST 10
+        movefallers();          //ST 12
+        moveexplosions();       //ST 4
+
+        moveactors();           //ST 1
+        moveeffectors();        //ST 3
+
+        movestandables();       //ST 6
+        doanimations();
+        movefx();               //ST 11
+    }
+
+    fakedomovethingscorrect();
+
+    if( (everyothertime&1) == 0)
+    {
+        animatewalls();
+        movecyclers();
+        pan3dsound();
+    }
+
+
+    return 0;
+}
+
+
+void doorders(void)
+{
+    short i;
+
+    setview(0,0,xdim-1,ydim-1);
+
+    for(i=0;i<63;i+=7) palto(0,0,0,i);
+    ps[myconnectindex].palette = palette;
+    totalclock = 0;
+    KB_FlushKeyboardQueue();
+    rotatesprite(0,0,65536L,0,ORDERING,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();
+
+    for(i=0;i<63;i+=7) palto(0,0,0,i);
+    totalclock = 0;
+    KB_FlushKeyboardQueue();
+    rotatesprite(0,0,65536L,0,ORDERING+1,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();
+
+    for(i=0;i<63;i+=7) palto(0,0,0,i);
+    totalclock = 0;
+    KB_FlushKeyboardQueue();
+    rotatesprite(0,0,65536L,0,ORDERING+2,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();
+
+    for(i=0;i<63;i+=7) palto(0,0,0,i);
+    totalclock = 0;
+    KB_FlushKeyboardQueue();
+    rotatesprite(0,0,65536L,0,ORDERING+3,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+    nextpage(); for(i=63;i>0;i-=7) palto(0,0,0,i);
+    totalclock = 0;while( !KB_KeyWaiting() ) getpackets();
+}
+
+void dobonus(char bonusonly)
+{
+    short t, r, tinc,gfx_offset;
+    long i, y,xfragtotal,yfragtotal;
+    short bonuscnt;
+
+    long breathe[] =
+    {
+         0,  30,VICTORY1+1,176,59,
+        30,  60,VICTORY1+2,176,59,
+        60,  90,VICTORY1+1,176,59,
+        90, 120,0         ,176,59
+    };
+
+    long bossmove[] =
+    {
+         0, 120,VICTORY1+3,86,59,
+       220, 260,VICTORY1+4,86,59,
+       260, 290,VICTORY1+5,86,59,
+       290, 320,VICTORY1+6,86,59,
+       320, 350,VICTORY1+7,86,59,
+       350, 380,VICTORY1+8,86,59
+    };
+
+    bonuscnt = 0;
+
+    for(t=0;t<64;t+=7) palto(0,0,0,t);
+    setview(0,0,xdim-1,ydim-1);
+    clearview(0L);
+    nextpage();
+    flushperms();
+
+    FX_StopAllSounds();
+    clearsoundlocks();
+    FX_SetReverb(0L);
+
+    if(bonusonly) goto FRAGBONUS;
+
+    if(numplayers < 2 && ud.eog && ud.from_bonus == 0)
+        switch(ud.volume_number)
+    {
+        case 0:
+            if(ud.lockout == 0)
+            {
+                clearview(0L);
+                rotatesprite(0,50<<16,65536L,0,VICTORY1,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                nextpage();
+                ps[myconnectindex].palette = endingpal;
+                for(t=63;t>=0;t--) palto(0,0,0,t);
+
+                KB_FlushKeyboardQueue();
+                totalclock = 0; tinc = 0;
+                while( 1 )
+                {
+                    clearview(0L);
+                    rotatesprite(0,50<<16,65536L,0,VICTORY1,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+
+                    // boss
+                    if( totalclock > 390 && totalclock < 780 )
+                        for(t=0;t<35;t+=5) if( bossmove[t+2] && (totalclock%390) > bossmove[t] && (totalclock%390) <= bossmove[t+1] )
+                    {
+                        if(t==10 && bonuscnt == 1) { sound(SHOTGUN_FIRE);sound(SQUISHED); bonuscnt++; }
+                        rotatesprite(bossmove[t+3]<<16,bossmove[t+4]<<16,65536L,0,bossmove[t+2],0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                    }
+
+                    // Breathe
+                    if( totalclock < 450 || totalclock >= 750 )
+                    {
+                        if(totalclock >= 750)
+                        {
+                            rotatesprite(86<<16,59<<16,65536L,0,VICTORY1+8,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                            if(totalclock >= 750 && bonuscnt == 2) { sound(DUKETALKTOBOSS); bonuscnt++; }
+                        }
+                        for(t=0;t<20;t+=5)
+                            if( breathe[t+2] && (totalclock%120) > breathe[t] && (totalclock%120) <= breathe[t+1] )
+                        {
+                                if(t==5 && bonuscnt == 0)
+                                {
+                                    sound(BOSSTALKTODUKE);
+                                    bonuscnt++;
+                                }
+                                rotatesprite(breathe[t+3]<<16,breathe[t+4]<<16,65536L,0,breathe[t+2],0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                        }
+                    }
+
+                    getpackets();
+                    nextpage();
+                    if( KB_KeyWaiting() ) break;
+                }
+            }
+
+            for(t=0;t<64;t++) palto(0,0,0,t);
+
+            KB_FlushKeyboardQueue();
+            ps[myconnectindex].palette = palette;
+
+            rotatesprite(0,0,65536L,0,3292,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+            nextpage(); for(t=63;t>0;t--) palto(0,0,0,t);
+            while( !KB_KeyWaiting() ) getpackets();
+            for(t=0;t<64;t++) palto(0,0,0,t);
+            MUSIC_StopSong();
+            FX_StopAllSounds();
+            clearsoundlocks();
+            break;
+        case 1:
+            MUSIC_StopSong();
+            clearview(0L);
+            nextpage();
+
+            if(ud.lockout == 0)
+            {
+                playanm("cineov2.anm",1);
+                KB_FlushKeyboardQueue();
+                clearview(0L);
+                nextpage();
+            }
+
+            sound(PIPEBOMB_EXPLODE);
+
+            for(t=0;t<64;t++) palto(0,0,0,t);
+            setview(0,0,xdim-1,ydim-1);
+            KB_FlushKeyboardQueue();
+            ps[myconnectindex].palette = palette;
+            rotatesprite(0,0,65536L,0,3293,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
+            nextpage(); for(t=63;t>0;t--) palto(0,0,0,t);
+            while( !KB_KeyWaiting() ) getpackets();
+            for(t=0;t<64;t++) palto(0,0,0,t);
+
+            break;
+
+        case 3:
+
+            setview(0,0,xdim-1,ydim-1);
+
+            MUSIC_StopSong();
+            clearview(0L);
+            nextpage();
+
+            if(ud.lockout == 0)
+            {
+                KB_FlushKeyboardQueue();
+                playanm("vol4e1.anm",8);
+                clearview(0L);
+                nextpage();
+                playanm("vol4e2.anm",10);
+                clearview(0L);
+                nextpage();
+                playanm("vol4e3.anm",11);
+                clearview(0L);
+                nextpage();
+            }
+
+            FX_StopAllSounds();
+            clearsoundlocks();
+            sound(ENDSEQVOL3SND4);
+            KB_FlushKeyboardQueue();
+
+            ps[myconnectindex].palette = palette;
+            palto(0,0,0,63);
+            clearview(0L);
+            menutext(160,60,0,0,"THANKS TO ALL OUR");
+            menutext(160,60+16,0,0,"FANS FOR GIVING");
+            menutext(160,60+16+16,0,0,"US BIG HEADS.");
+            menutext(160,70+16+16+16,0,0,"LOOK FOR A DUKE NUKEM 3D");
+            menutext(160,70+16+16+16+16,0,0,"SEQUEL SOON.");
+            nextpage();
+
+            for(t=63;t>0;t-=3) palto(0,0,0,t);
+            KB_FlushKeyboardQueue();
+            while(!KB_KeyWaiting()) getpackets();
+            for(t=0;t<64;t+=3) palto(0,0,0,t);
+
+            clearview(0L);
+            nextpage();
+
+            playanm("DUKETEAM.ANM",4);
+
+            KB_FlushKeyboardQueue();
+            while(!KB_KeyWaiting()) getpackets();
+
+            clearview(0L);
+            nextpage();
+            palto(0,0,0,63);
+
+            FX_StopAllSounds();
+            clearsoundlocks();
+            KB_FlushKeyboardQueue();
+
+            break;
+
+        case 2:
+
+            MUSIC_StopSong();
+            clearview(0L);
+            nextpage();
+            if(ud.lockout == 0)
+            {
+                for(t=63;t>=0;t--) palto(0,0,0,t);
+                playanm("cineov3.anm",2);
+                KB_FlushKeyboardQueue();
+                ototalclock = totalclock+200;
+                while(totalclock < ototalclock) getpackets();
+                clearview(0L);
+                nextpage();
+
+                FX_StopAllSounds();
+                clearsoundlocks();
+            }
+
+            playanm("RADLOGO.ANM",3);
+
+            if( ud.lockout == 0 && !KB_KeyWaiting() )
+            {
+                sound(ENDSEQVOL3SND5);
+                while(Sound[ENDSEQVOL3SND5].lock>=200) getpackets();
+                if(KB_KeyWaiting()) goto ENDANM;
+                sound(ENDSEQVOL3SND6);
+                while(Sound[ENDSEQVOL3SND6].lock>=200) getpackets();
+                if(KB_KeyWaiting()) goto ENDANM;
+                sound(ENDSEQVOL3SND7);
+                while(Sound[ENDSEQVOL3SND7].lock>=200) getpackets();
+                if(KB_KeyWaiting()) goto ENDANM;
+                sound(ENDSEQVOL3SND8);
+                while(Sound[ENDSEQVOL3SND8].lock>=200) getpackets();
+                if(KB_KeyWaiting()) goto ENDANM;
+                sound(ENDSEQVOL3SND9);
+                while(Sound[ENDSEQVOL3SND9].lock>=200) getpackets();
+            }
+
+            KB_FlushKeyboardQueue();
+            totalclock = 0;
+            while(!KB_KeyWaiting() && totalclock < 120) getpackets();
+
+            ENDANM:
+
+            FX_StopAllSounds();
+            clearsoundlocks();
+
+            KB_FlushKeyboardQueue();
+
+            clearview(0L);
+
+            break;
+    }
+
+    FRAGBONUS:
+
+    ps[myconnectindex].palette = palette;
+    KB_FlushKeyboardQueue();
+    totalclock = 0; tinc = 0;
+    bonuscnt = 0;
+
+    MUSIC_StopSong();
+    FX_StopAllSounds();
+    clearsoundlocks();
+
+    if(playerswhenstarted > 1 && ud.coop != 1 )
+    {
+        if(!(MusicToggle == 0 || MusicDevice == NumSoundCards))
+            sound(BONUSMUSIC);
+
+        rotatesprite(0,0,65536L,0,MENUSCREEN,16,0,2+8+16+64,0,0,xdim-1,ydim-1);
+        rotatesprite(160<<16,34<<16,65536L,0,INGAMEDUKETHREEDEE,0,0,10,0,0,xdim-1,ydim-1);
+        rotatesprite((260)<<16,36<<16,65536L,0,PLUTOPAKSPRITE+2,0,0,2+8,0,0,xdim-1,ydim-1);
+        gametext(160,58+2,"MULTIPLAYER TOTALS",0,2+8+16);
+        gametext(160,58+10,level_names[(ud.volume_number*11)+ud.last_level-1],0,2+8+16);
+
+        gametext(160,165,"PRESS ANY KEY TO CONTINUE",0,2+8+16);
+
+
+        t = 0;
+        minitext(23,80,"   NAME                                           KILLS",8,2+8+16+128);
+        for(i=0;i<playerswhenstarted;i++)
+        {
+            sprintf(tempbuf,"%-4ld",i+1);
+            minitext(92+(i*23),80,tempbuf,3,2+8+16+128);
+        }
+
+        for(i=0;i<playerswhenstarted;i++)
+        {
+            xfragtotal = 0;
+            sprintf(tempbuf,"%ld",i+1);
+
+            minitext(30,90+t,tempbuf,0,2+8+16+128);
+            minitext(38,90+t,ud.user_name[i],ps[i].palookup,2+8+16+128);
+
+            for(y=0;y<playerswhenstarted;y++)
+            {
+                if(i == y)
+                {
+                    sprintf(tempbuf,"%-4ld",ps[y].fraggedself);
+                    minitext(92+(y*23),90+t,tempbuf,2,2+8+16+128);
+                    xfragtotal -= ps[y].fraggedself;
+                }
+                else
+                {
+                    sprintf(tempbuf,"%-4ld",frags[i][y]);
+                    minitext(92+(y*23),90+t,tempbuf,0,2+8+16+128);
+                    xfragtotal += frags[i][y];
+                }
+
+                if(myconnectindex == connecthead)
+                {
+                    sprintf(tempbuf,"stats %ld killed %ld %ld\n",i+1,y+1,frags[i][y]);
+                    sendscore(tempbuf);
+                }
+            }
+
+            sprintf(tempbuf,"%-4ld",xfragtotal);
+            minitext(101+(8*23),90+t,tempbuf,2,2+8+16+128);
+
+            t += 7;
+        }
+
+        for(y=0;y<playerswhenstarted;y++)
+        {
+            yfragtotal = 0;
+            for(i=0;i<playerswhenstarted;i++)
+            {
+                if(i == y)
+                    yfragtotal += ps[i].fraggedself;
+                yfragtotal += frags[i][y];
+            }
+            sprintf(tempbuf,"%-4ld",yfragtotal);
+            minitext(92+(y*23),96+(8*7),tempbuf,2,2+8+16+128);
+        }
+
+        minitext(45,96+(8*7),"DEATHS",8,2+8+16+128);
+        nextpage();
+
+        for(t=0;t<64;t+=7)
+            palto(0,0,0,63-t);
+
+        KB_FlushKeyboardQueue();
+        while(KB_KeyWaiting()==0) getpackets();
+
+        if( KB_KeyPressed( sc_F12 ) )
+        {
+            KB_ClearKeyDown( sc_F12 );
+            takescreenshot();
+        }
+
+        if(bonusonly || ud.multimode > 1) return;
+
+        for(t=0;t<64;t+=7) palto(0,0,0,t);
+    }
+
+    if(bonusonly || ud.multimode > 1) return;
+
+    switch(ud.volume_number)
+    {
+        case 1:
+            gfx_offset = 5;
+            break;
+        default:
+            gfx_offset = 0;
+            break;
+    }
+
+    rotatesprite(0,0,65536L,0,BONUSSCREEN+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+
+    menutext(160,20-6,0,0,&level_names[(ud.volume_number*11)+ud.last_level-1][0]);
+    menutext(160,36-6,0,0,"COMPLETED");
+
+    gametext(160,192,"PRESS ANY KEY TO CONTINUE",16,2+8+16);
+
+    if(!(MusicToggle == 0 || MusicDevice == NumSoundCards))
+        sound(BONUSMUSIC);
+
+    nextpage();
+    KB_FlushKeyboardQueue();
+    for(t=0;t<64;t++) palto(0,0,0,63-t);
+    bonuscnt = 0;
+    totalclock = 0; tinc = 0;
+
+    while( 1 )
+    {
+		sampletimer();    	
+        if(ps[myconnectindex].gm&MODE_EOL)
+        {
+            rotatesprite(0,0,65536L,0,BONUSSCREEN+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+
+            if( totalclock > (1000000000L) && totalclock < (1000000320L) )
+            {
+                switch( (totalclock>>4)%15 )
+                {
+                    case 0:
+                        if(bonuscnt == 6)
+                        {
+                            bonuscnt++;
+                            sound(SHOTGUN_COCK);
+                            switch(rand()&3)
+                            {
+                                case 0:
+                                    sound(BONUS_SPEECH1);
+                                    break;
+                                case 1:
+                                    sound(BONUS_SPEECH2);
+                                    break;
+                                case 2:
+                                    sound(BONUS_SPEECH3);
+                                    break;
+                                case 3:
+                                    sound(BONUS_SPEECH4);
+                                    break;
+                            }
+                        }
+                    case 1:
+                    case 4:
+                    case 5:
+                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+3+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                        break;
+                    case 2:
+                    case 3:
+                       rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+4+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                       break;
+                }
+            }
+            else if( totalclock > (10240+120L) ) break;
+            else
+            {
+                switch( (totalclock>>5)&3 )
+                {
+                    case 1:
+                    case 3:
+                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+1+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                        break;
+                    case 2:
+                        rotatesprite(199<<16,31<<16,65536L,0,BONUSSCREEN+2+gfx_offset,0,0,2+8+16+64+128,0,0,xdim-1,ydim-1);
+                        break;
+                }
+            }
+
+            menutext(160,20-6,0,0,&level_names[(ud.volume_number*11)+ud.last_level-1][0]);
+            menutext(160,36-6,0,0,"COMPLETED");
+
+            gametext(160,192,"PRESS ANY KEY TO CONTINUE",16,2+8+16);
+
+            if( totalclock > (60*3) )
+            {
+                gametext(10,59+9,"Your Time:",0,2+8+16);
+                gametext(10,69+9,"Par time:",0,2+8+16);
+                gametext(10,78+9,"3D Realms' Time:",0,2+8+16);
+                if(bonuscnt == 0)
+                    bonuscnt++;
+
+                if( totalclock > (60*4) )
+                {
+                    if(bonuscnt == 1)
+                    {
+                        bonuscnt++;
+                        sound(PIPEBOMB_EXPLODE);
+                    }
+                    sprintf(tempbuf,"%02ld:%02ld",
+                        (ps[myconnectindex].player_par/(26*60))%60,
+                        (ps[myconnectindex].player_par/26)%60);
+                    gametext((320>>2)+71,60+9,tempbuf,0,2+8+16);
+
+                    sprintf(tempbuf,"%02ld:%02ld",
+                        (partime[ud.volume_number*11+ud.last_level-1]/(26*60))%60,
+                        (partime[ud.volume_number*11+ud.last_level-1]/26)%60);
+                    gametext((320>>2)+71,69+9,tempbuf,0,2+8+16);
+
+                    sprintf(tempbuf,"%02ld:%02ld",
+                        (designertime[ud.volume_number*11+ud.last_level-1]/(26*60))%60,
+                        (designertime[ud.volume_number*11+ud.last_level-1]/26)%60);
+                    gametext((320>>2)+71,78+9,tempbuf,0,2+8+16);
+
+                }
+            }
+            if( totalclock > (60*6) )
+            {
+                gametext(10,94+9,"Enemies Killed:",0,2+8+16);
+                gametext(10,99+4+9,"Enemies Left:",0,2+8+16);
+
+                if(bonuscnt == 2)
+                {
+                    bonuscnt++;
+                    sound(FLY_BY);
+                }
+
+                if( totalclock > (60*7) )
+                {
+                    if(bonuscnt == 3)
+                    {
+                        bonuscnt++;
+                        sound(PIPEBOMB_EXPLODE);
+                    }
+                    sprintf(tempbuf,"%-3ld",ps[myconnectindex].actors_killed);
+                    gametext((320>>2)+70,93+9,tempbuf,0,2+8+16);
+                    if(ud.player_skill > 3 )
+                    {
+                        sprintf(tempbuf,"N/A");
+                        gametext((320>>2)+70,99+4+9,tempbuf,0,2+8+16);
+                    }
+                    else
+                    {
+                        if( (ps[myconnectindex].max_actors_killed-ps[myconnectindex].actors_killed) < 0 )
+                            sprintf(tempbuf,"%-3ld",0);
+                        else sprintf(tempbuf,"%-3ld",ps[myconnectindex].max_actors_killed-ps[myconnectindex].actors_killed);
+                        gametext((320>>2)+70,99+4+9,tempbuf,0,2+8+16);
+                    }
+                }
+            }
+            if( totalclock > (60*9) )
+            {
+                gametext(10,120+9,"Secrets Found:",0,2+8+16);
+                gametext(10,130+9,"Secrets Missed:",0,2+8+16);
+                if(bonuscnt == 4) bonuscnt++;
+
+                if( totalclock > (60*10) )
+                {
+                    if(bonuscnt == 5)
+                    {
+                        bonuscnt++;
+                        sound(PIPEBOMB_EXPLODE);
+                    }
+                    sprintf(tempbuf,"%-3ld",ps[myconnectindex].secret_rooms);
+                    gametext((320>>2)+70,120+9,tempbuf,0,2+8+16);
+                    if( ps[myconnectindex].secret_rooms > 0 )
+                        sprintf(tempbuf,"%-3ld",(100*ps[myconnectindex].secret_rooms/ps[myconnectindex].max_secret_rooms));
+                    sprintf(tempbuf,"%-3ld",ps[myconnectindex].max_secret_rooms-ps[myconnectindex].secret_rooms);
+                    gametext((320>>2)+70,130+9,tempbuf,0,2+8+16);
+                }
+            }
+
+            if(totalclock > 10240 && totalclock < 10240+10240)
+                totalclock = 1024;
+
+            if( KB_KeyWaiting() && totalclock > (60*2) )
+            {
+                if( KB_KeyPressed( sc_F12 ) )
+                {
+                    KB_ClearKeyDown( sc_F12 );
+                    takescreenshot();
+                }
+
+                if( totalclock < (60*13) )
+                {
+                    KB_FlushKeyboardQueue();
+                    totalclock = (60*13);
+                }
+                else if( totalclock < (1000000000L))
+                   totalclock = (1000000000L);
+            }
+        }
+        else break;
+        nextpage();
+    }
+}
+
+
+void cameratext(short i)
+{
+    char flipbits;
+    long x , y;
+
+    if(!T1)
+    {
+        rotatesprite(24<<16,33<<16,65536L,0,CAMCORNER,0,0,2,windowx1,windowy1,windowx2,windowy2);
+        rotatesprite((320-26)<<16,34<<16,65536L,0,CAMCORNER+1,0,0,2,windowx1,windowy1,windowx2,windowy2);
+        rotatesprite(22<<16,163<<16,65536L,512,CAMCORNER+1,0,0,2+4,windowx1,windowy1,windowx2,windowy2);
+        rotatesprite((310-10)<<16,163<<16,65536L,512,CAMCORNER+1,0,0,2,windowx1,windowy1,windowx2,windowy2);
+        if(totalclock&16)
+            rotatesprite(46<<16,32<<16,65536L,0,CAMLIGHT,0,0,2,windowx1,windowy1,windowx2,windowy2);
+    }
+    else
+    {
+        flipbits = (totalclock<<1)&48;
+        for(x=0;x<394;x+=64)
+            for(y=0;y<200;y+=64)
+                rotatesprite(x<<16,y<<16,65536L,0,STATIC,0,0,2+flipbits,windowx1,windowy1,windowx2,windowy2);
+    }
+}
+
+void vglass(long x,long y,short a,short wn,short n)
+{
+    long z, zincs;
+    short sect;
+
+    sect = wall[wn].nextsector;
+    if(sect == -1) return;
+    zincs = ( sector[sect].floorz-sector[sect].ceilingz ) / n;
+
+    for(z = sector[sect].ceilingz;z < sector[sect].floorz; z += zincs )
+        EGS(sect,x,y,z-(TRAND&8191),GLASSPIECES+(z&(TRAND%3)),-32,36,36,a+128-(TRAND&255),16+(TRAND&31),0,-1,5);
+}
+
+void lotsofglass(short i,short wallnum,short n)
+{
+     long j, xv, yv, z, x1, y1;
+     short sect, a;
+
+     sect = -1;
+
+     if(wallnum < 0)
+     {
+        for(j=n-1; j >= 0 ;j--)
+        {
+            a = SA-256+(TRAND&511)+1024;
+            EGS(SECT,SX,SY,SZ,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),1024-(TRAND&1023),i,5);
+        }
+        return;
+     }
+
+     j = n+1;
+
+     x1 = wall[wallnum].x;
+     y1 = wall[wallnum].y;
+
+     xv = wall[wall[wallnum].point2].x-x1;
+     yv = wall[wall[wallnum].point2].y-y1;
+
+     x1 -= ksgn(yv);
+     y1 += ksgn(xv);
+
+     xv /= j;
+     yv /= j;
+
+     for(j=n;j>0;j--)
+         {
+                  x1 += xv;
+                  y1 += yv;
+
+          updatesector(x1,y1,&sect);
+          if(sect >= 0)
+          {
+              z = sector[sect].floorz-(TRAND&(klabs(sector[sect].ceilingz-sector[sect].floorz)));
+              if( z < -(32<<8) || z > (32<<8) )
+                  z = SZ-(32<<8)+(TRAND&((64<<8)-1));
+              a = SA-1024;
+              EGS(SECT,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),-(TRAND&1023),i,5);
+          }
+         }
+}
+
+void spriteglass(short i,short n)
+{
+    long j, k, a, z;
+
+    for(j=n;j>0;j--)
+    {
+        a = TRAND&2047;
+        z = SZ-((TRAND&16)<<8);
+        k = EGS(SECT,SX,SY,z,GLASSPIECES+(j%3),TRAND&15,36,36,a,32+(TRAND&63),-512-(TRAND&2047),i,5);
+        sprite[k].pal = sprite[i].pal;
+    }
+}
+
+void ceilingglass(short i,short sectnum,short n)
+{
+     long j, xv, yv, z, x1, y1;
+     short a,s, startwall,endwall;
+
+     startwall = sector[sectnum].wallptr;
+     endwall = startwall+sector[sectnum].wallnum;
+
+     for(s=startwall;s<(endwall-1);s++)
+     {
+         x1 = wall[s].x;
+         y1 = wall[s].y;
+
+         xv = (wall[s+1].x-x1)/(n+1);
+         yv = (wall[s+1].y-y1)/(n+1);
+
+         for(j=n;j>0;j--)
+         {
+              x1 += xv;
+              y1 += yv;
+              a = TRAND&2047;
+              z = sector[sectnum].ceilingz+((TRAND&15)<<8);
+              EGS(sectnum,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,(TRAND&31),0,i,5);
+          }
+     }
+}
+
+
+
+void lotsofcolourglass(short i,short wallnum,short n)
+{
+     long j, xv, yv, z, x1, y1;
+     short sect = -1, a, k;
+
+     if(wallnum < 0)
+     {
+        for(j=n-1; j >= 0 ;j--)
+        {
+            a = TRAND&2047;
+            k = EGS(SECT,SX,SY,SZ-(TRAND&(63<<8)),GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),1024-(TRAND&2047),i,5);
+            sprite[k].pal = TRAND&15;
+        }
+        return;
+     }
+
+     j = n+1;
+     x1 = wall[wallnum].x;
+     y1 = wall[wallnum].y;
+
+     xv = (wall[wall[wallnum].point2].x-wall[wallnum].x)/j;
+     yv = (wall[wall[wallnum].point2].y-wall[wallnum].y)/j;
+
+     for(j=n;j>0;j--)
+         {
+                  x1 += xv;
+                  y1 += yv;
+
+          updatesector(x1,y1,&sect);
+          z = sector[sect].floorz-(TRAND&(klabs(sector[sect].ceilingz-sector[sect].floorz)));
+          if( z < -(32<<8) || z > (32<<8) )
+              z = SZ-(32<<8)+(TRAND&((64<<8)-1));
+          a = SA-1024;
+          k = EGS(SECT,x1,y1,z,GLASSPIECES+(j%3),-32,36,36,a,32+(TRAND&63),-(TRAND&2047),i,5);
+          sprite[k].pal = TRAND&7;
+         }
+}
+
+void SetupGameButtons( void )
+{
+   CONTROL_DefineFlag(gamefunc_Move_Forward,false);
+   CONTROL_DefineFlag(gamefunc_Move_Backward,false);
+   CONTROL_DefineFlag(gamefunc_Turn_Left,false);
+   CONTROL_DefineFlag(gamefunc_Turn_Right,false);
+   CONTROL_DefineFlag(gamefunc_Strafe,false);
+   CONTROL_DefineFlag(gamefunc_Fire,false);
+   CONTROL_DefineFlag(gamefunc_Open,false);
+   CONTROL_DefineFlag(gamefunc_Run,false);
+   CONTROL_DefineFlag(gamefunc_AutoRun,false);
+   CONTROL_DefineFlag(gamefunc_Jump,false);
+   CONTROL_DefineFlag(gamefunc_Crouch,false);
+   CONTROL_DefineFlag(gamefunc_Look_Up,false);
+   CONTROL_DefineFlag(gamefunc_Look_Down,false);
+   CONTROL_DefineFlag(gamefunc_Look_Left,false);
+   CONTROL_DefineFlag(gamefunc_Look_Right,false);
+   CONTROL_DefineFlag(gamefunc_Strafe_Left,false);
+   CONTROL_DefineFlag(gamefunc_Strafe_Right,false);
+   CONTROL_DefineFlag(gamefunc_Aim_Up,false);
+   CONTROL_DefineFlag(gamefunc_Aim_Down,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_1,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_2,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_3,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_4,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_5,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_6,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_7,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_8,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_9,false);
+   CONTROL_DefineFlag(gamefunc_Weapon_10,false);
+   CONTROL_DefineFlag(gamefunc_Inventory,false);
+   CONTROL_DefineFlag(gamefunc_Inventory_Left,false);
+   CONTROL_DefineFlag(gamefunc_Inventory_Right,false);
+   CONTROL_DefineFlag(gamefunc_Holo_Duke,false);
+   CONTROL_DefineFlag(gamefunc_Jetpack,false);
+   CONTROL_DefineFlag(gamefunc_NightVision,false);
+   CONTROL_DefineFlag(gamefunc_MedKit,false);
+   CONTROL_DefineFlag(gamefunc_TurnAround,false);
+   CONTROL_DefineFlag(gamefunc_SendMessage,false);
+   CONTROL_DefineFlag(gamefunc_Map,false);
+   CONTROL_DefineFlag(gamefunc_Shrink_Screen,false);
+   CONTROL_DefineFlag(gamefunc_Enlarge_Screen,false);
+   CONTROL_DefineFlag(gamefunc_Center_View,false);
+   CONTROL_DefineFlag(gamefunc_Holster_Weapon,false);
+   CONTROL_DefineFlag(gamefunc_Show_Opponents_Weapon,false);
+   CONTROL_DefineFlag(gamefunc_Map_Follow_Mode,false);
+   CONTROL_DefineFlag(gamefunc_See_Coop_View,false);
+   CONTROL_DefineFlag(gamefunc_Mouse_Aiming,false);
+   CONTROL_DefineFlag(gamefunc_Toggle_Crosshair,false);
+   CONTROL_DefineFlag(gamefunc_Steroids,false);
+   CONTROL_DefineFlag(gamefunc_Quick_Kick,false);
+   CONTROL_DefineFlag(gamefunc_Next_Weapon,false);
+   CONTROL_DefineFlag(gamefunc_Previous_Weapon,false);
+   CONTROL_DefineFlag(gamefunc_Console,false);
+}
+
+/*
+===================
+=
+= GetTime
+=
+===================
+*/
+
+long GetTime(void)
+   {
+   return totalclock;
+   }
+
+
+/*
+===================
+=
+= CenterCenter
+=
+===================
+*/
+
+void CenterCenter(void)
+   {
+   printf("Center the joystick and press a button\n");
+   }
+
+/*
+===================
+=
+= UpperLeft
+=
+===================
+*/
+
+void UpperLeft(void)
+   {
+   printf("Move joystick to upper-left corner and press a button\n");
+   }
+
+/*
+===================
+=
+= LowerRight
+=
+===================
+*/
+
+void LowerRight(void)
+   {
+   printf("Move joystick to lower-right corner and press a button\n");
+   }
+
+/*
+===================
+=
+= CenterThrottle
+=
+===================
+*/
+
+void CenterThrottle(void)
+   {
+   printf("Center the throttle control and press a button\n");
+   }
+
+/*
+===================
+=
+= CenterRudder
+=
+===================
+*/
+
+void CenterRudder(void)
+{
+   printf("Center the rudder control and press a button\n");
+}
+
+// FIX_00006: better naming system for screenshots + message when pic is taken. 
+//            Use ./screenshots folder. Screenshot code rerwritten. Faster and
+//            makes smaller files. Doesn't freeze or lag the game anymore.
+void takescreenshot(void)
+{
+	char szFilename[256];
+	int i;
+	char score[20];
+	time_t time4file;
+	struct tm *tmHMS;
+	
+
+	// xduke: Build a nice name w/ date and players name if in multi mode.
+	time(&time4file);
+	tmHMS = localtime(&time4file);
+
+	sprintf(tempbuf, "xDuke(v%d.%d) %.4d.%.2d.%.2d %.2dh%.2dm%.2ds", 
+			XDUKE_REV_X,
+			XDUKE_REV_DOT_Y,
+			tmHMS->tm_year+1900,
+			tmHMS->tm_mon+1,
+			tmHMS->tm_mday,
+			tmHMS->tm_hour,
+			tmHMS->tm_min,
+			tmHMS->tm_sec);
+
+	if(ud.multimode>1) // if more than 1 player, we add name. Then add score if DM
+	{
+		strcat(tempbuf, " [");
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+		{
+			if(!ud.user_name[i][0])
+				strcat(tempbuf, "NoName");
+			else
+				strcat(tempbuf, &ud.user_name[i][0]);
+
+			if(ud.m_coop==0 || ud.m_coop==2)  // if DM or DM No spawn. Add Score as well
+			{
+				strcat(tempbuf, "(");
+				strcat(tempbuf, itoa(ps[i].frag-ps[i].fraggedself, score, 10));
+				strcat(tempbuf, ") vs ");
+			}
+			else
+				strcat(tempbuf, " vs ");
+		}	
+		tempbuf[strlen(tempbuf)-4]=0; // remove last vs
+		strcat(tempbuf, "]");
+	}
+	strcat(tempbuf, ".bmp");
+
+
+	// If this is a TC save it to the TC's directory
+	if(game_dir[0] != '\0')
+	{
+		sprintf(szFilename, "%s\\%s", game_dir, SCREENSHOTPATH);
+		mkdir(szFilename);
+		sprintf(szFilename, "%s\\%s\\%s", game_dir, SCREENSHOTPATH, tempbuf);
+	}
+	// otherwise let's save it to the root.
+	else
+	{
+		mkdir(SCREENSHOTPATH);
+		sprintf(szFilename, "%s\\%s", SCREENSHOTPATH, tempbuf);
+	}
+
+	if(SafeFileExists(szFilename) == 0)
+	{
+		screencapture(szFilename,0);
+		sprintf(fta_quotes[103],"SCREEN SAVED");  
+		sound(EXITMENUSOUND);
+	}
+	else
+		sprintf(fta_quotes[103],"CAN'T WRITE FILE!");
+
+	FTA(103,&ps[screenpeek],1);
+
+}
+
+
+// Rare Multiplayer, when dead, total screen screwup back again!
+// E3l1 (Coop /w monsters) sprite list corrupt 50%
+// Univbe exit, instead, default to screen buffer.
+// Check all caches bounds and memory usages
+// Fix enlarger weapon selections to perfection
+// Need sounds.c
+// Spawning a couple of sounds at the same time
+// Check Weapon Switching
+// FIRE and FIRE2
+// Where should I flash the screen white???
+// Jittery on subs in mp?
+// Check accurate memory amounts!
+// Why squish sound at hit space when dead?
+// Falling Counter Not reset in mp
+// Wierd small freezer
+// Double freeze on player?, still firing
+// Do Mouse Flip option
+// Save mouse aiming
+// Laser bounce off mirrors
+// GEORGE:   Ten in text screen.
+// Alien:
+// Freeze: change
+// Press space holding player
+// Press space
+// tank broke
+// 2d mode fucked in fake mp mode
+// 207
+// Mail not rolling up on conveyers
+// Fix all alien animations
+// Do episode names in .CONS
+// do syntak check for "{?????"
+// Make commline parms set approiate multiplayer flags
+
+// Check all breakables to see if they are exploding properly
+// Fix freezing palette on Alien
+
+// Do a demo make run overnite
+// Fix Super Duck
+// Slime Guies, use quickkick.
+
+// Make Lasers from trip bombs reflect off mirrors
+// Remember for lockout of sound swears
+// Pass sender in packed, NOT
+// Fatal sync give no message for TEN
+// Hitting TEN BUTTON(OPTION) no TEN SCreen
+// Check multioperateswitches for se 31,32
+// Fix pal for ceilings (SE#18)
+// case 31: sprites up one high
+// E1l1 No Kill All troops in room, sleep time
+
+// Fifo for message list
+
+// Bloodsplat on conveyers
+
+// Meclanical
+// Increase sound
+// Mouse Delay at death
+// Wierd slowdown
+
+// Footprints on stuff floating
+
+// Ken, The inside function is called a lot in -1 sectors
+// No loading Univbe message rewrite
+// Expander must cycle with rest of weapons
+// Duck SHOOT PIPEBOMB, red wall
+
+// Get commit source from mark
+
+/*
+1. fix pipebomb bug
+2. check george maps
+4. Save/Restore check (MP and SP)
+5. Check TEN
+6. Get Commit fixed
+8. Is mail slow?
+9. Cacheing
+10. Blue out "PLAY ON TEN" in MULTIPLAYER
+11. Eight Player test
+12. Postal.voc not found.
+13. All Monsters explode in arcade,
+    check SEENINE STRENGTH,
+    Change 28<<8 back to 16<<8 in hitradius
+    Compare 1.3d to 1.4
+14. Check sounds/gfx for for parr lock
+15. Player # Loaded a game
+16. Replace Crane code 1.3d to 1.4
+17. Fix Greenslime
+18. Small Freeze sprite,below floor
+19. Vesa message auto abort in mp?
+20. Fucked Palette in my skip ahead in MP
+21. Load in main menu
+22. Rotated frag screen no game screen
+23. Jibs sounds when killed other dukes
+24. Ten code and /f4 mode
+25. Fix All MP Glitches!!
+26. Unrem Menues anim tenbn
+27. buy groc,clothes,scanner
+28. Why Double Defs in global and game, is so at work
+29. Check that all .objs are erased
+30. Check why 1.3ds gotweapon gamedef coop code no workie
+31. Heavy mods to net code
+32. Make sure all commline stuff works,
+33. killed all waitfor???
+34. 90k stack
+35. double door probs
+36: copy protection
+* when you start a game the duke saying that is played when you choose a skill the sound is cut off.
+* NEWBEASTJUMPING is not deleted at premap in multi-play
+if(*c == '4') no work need objs ask ken, commit
+{
+movesperpacket = 4;
+setpackettimeout(0x3fffffff,0x3fffffff);
+}
+remember, netcode load
+*/
+//  Ai Problem in god mode.
+// Checkplayerhurtwall for forcefields bigforce
+// Nuddie, posters. IMF
+// Release commit.c to public?
+// Document Save bug with mp
+// Check moves per packet /f4 waitforeverybody over net?
+// Kill IDF OBJ
+// No shotguns under water @ tanker
+// Unrem copyprotect
+// Look for printf and puts
+// Check con rewrites
+// erase mmulti.c, or get newest objs
+// Why nomonsters screwy in load menu in mp
+// load last > 'y' == NOT
+// Check xptr oos when dead rising to surface.
+//    diaginal warping with shotguns
+// Test white room.  Lasertripbomb arming crash
+// The Bog
+// Run Duke Out of windows
+// Put Version number in con files
+// Test diff. version playing together
+// Reorganize dukecd
+// Put out patch w/ two weeks testing
+// Print draw3d
+// Double Klick
+
+/*
+Duke Nukem V
+
+Layout:
+
+      Settings:
+        Suburbs
+          Duke inflitrating neighborhoods inf. by aliens
+        Death Valley:
+          Sorta like a western.  Bull-skulls half buried in the sand
+          Military compound:  Aliens take over nuke-missle silo, duke
+            must destroy.
+          Abondend Aircraft field
+        Vegas:
+          Blast anything bright!  Alien lights camoflauged.
+          Alien Drug factory. The Blue Liquid
+        Mountainal Cave:
+          Interior cave battles.
+        Jungle:
+          Trees, canopee, animals, a mysterious hole in the earth with
+          gas seaping thru.
+        Penetencury:
+          Good use of spotlights:
+        Mental ward:
+          People whom have claimed to be slowly changing into an
+          alien species
+
+      Inventory:
+        Wood,
+        Metal,
+        Torch,
+        Rope,
+        Plastique,
+        Cloth,
+        Wiring,
+        Glue,
+        Cigars,
+        Food,
+        Duck Tape,
+        Nails,
+        Piping,
+        Petrol,
+        Uranium,
+        Gold,
+        Prism,
+        Power Cell,
+
+        Hand spikes (Limited usage, they become dull)
+        Oxygent     (Oxygen mixed with stimulant)
+
+
+      Player Skills:
+        R-Left,R-Right,Foward,Back
+        Strafe, Jump, Double Flip Jump for distance
+        Help, Escape
+        Fire/Use
+        Use Menu
+
+    After a brief resbit, Duke decides to get back to work.
+
+Cmdr:   "Duke, we've got a lot of scared people down there.
+         Some reports even claim that people are already
+         slowly changing into aliens."
+Duke:   "No problem, my speciality is in croud control."
+Cmdr:   "Croud control, my ass!  Remember that incident
+         during the war?  You created nuthin' but death and
+         destruction."
+Duke:   "Not destruction, justice."
+Cmdr:   "I'll take no responsibility for your actions.  Your on
+         your own!  Behave your self, damnit!  You got that,
+         soldger?"
+Duke:   "I've always been on my own...   Face it, it's ass kickin' time,
+         SIR!"
+Cmdr:   "Get outta here...!"
+        (Duke gives the Cmdr a hard stair, then cocks his weapon and
+         walks out of the room)
+Cmdr:   In a wisper: "Good luck, my friend."
+
+        (Cut to a scene where aliens are injecting genetic material
+         into an unconcious subject)
+
+Programming:   ( the functions I need )
+     Images: Polys
+     Actors:
+       Multi-Object sections for change (head,arms,legs,torsoe,all change)
+       Facial expressions.  Pal lookup per poly?
+
+     struct imagetype
+        {
+            int *itable; // AngX,AngY,AngZ,Xoff,Yoff,Zoff;
+            int *idata;
+            struct imagetype *prev, *next;
+        }
+
+*/
+
+
+// Test frag screen name fuckup
+// Test all xptrs
+// Make Jibs stick to ceiling
+// Save Game menu crash
+// Cache len sum err
+// Loading in main (MP), reset totalclock?
+// White Room
+// Sound hitch with repeat bits
+// Rewrite saved menues so no crash
+// Put a getpackets after loadplayer in menus
+// Put "loading..." before waitfor in loadpla
+// No ready2send = 0 for loading
+// Test Joystick
+// Ten
+// Bog
+// Test Blimp respawn
+// move 1 in player???
+
+
--- /dev/null
+++ b/Game/src/game.h
@@ -1,0 +1,8 @@
+#ifndef _GAME_H_
+#define _GAME_H_
+
+//extern char game_dir[512];
+char* getgamedir();
+
+#endif  // include-once header.
+
--- /dev/null
+++ b/Game/src/gamedef.c
@@ -1,0 +1,3316 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "cache1d.h"
+
+extern short otherp;
+
+static short total_lines,line_number;
+static char checking_ifelse,parsing_state,*last_used_text;
+static short num_squigilly_brackets;
+static long last_used_size;
+
+static short g_i,g_p;
+static long g_x;
+static long *g_t;
+static spritetype *g_sp;
+
+#define NUMKEYWORDS     112
+
+char *keyw[NUMKEYWORDS] =
+{
+    "definelevelname",  // 0
+    "actor",            // 1    [#]
+    "addammo",   // 2    [#]
+    "ifrnd",            // 3    [C]
+    "enda",             // 4    [:]
+    "ifcansee",         // 5    [C]
+    "ifhitweapon",      // 6    [#]
+    "action",           // 7    [#]
+    "ifpdistl",         // 8    [#]
+    "ifpdistg",         // 9    [#]
+    "else",             // 10   [#]
+    "strength",         // 11   [#]
+    "break",            // 12   [#]
+    "shoot",            // 13   [#]
+    "palfrom",          // 14   [#]
+    "sound",            // 15   [filename.voc]
+    "fall",             // 16   []
+    "state",            // 17
+    "ends",             // 18
+    "define",           // 19
+    "//",               // 20
+    "ifai",             // 21
+    "killit",           // 22
+    "addweapon",        // 23
+    "ai",               // 24
+    "addphealth",       // 25
+    "ifdead",           // 26
+    "ifsquished",       // 27
+    "sizeto",           // 28
+    "{",                // 29
+    "}",                // 30
+    "spawn",            // 31
+    "move",             // 32
+    "ifwasweapon",      // 33
+    "ifaction",         // 34
+    "ifactioncount",    // 35
+    "resetactioncount", // 36
+    "debris",           // 37
+    "pstomp",           // 38
+    "/*",               // 39
+    "cstat",            // 40
+    "ifmove",           // 41
+    "resetplayer",      // 42
+    "ifonwater",        // 43
+    "ifinwater",        // 44
+    "ifcanshoottarget", // 45
+    "ifcount",          // 46
+    "resetcount",       // 47
+    "addinventory",     // 48
+    "ifactornotstayput",// 49
+    "hitradius",        // 50
+    "ifp",              // 51
+    "count",            // 52
+    "ifactor",          // 53
+    "music",            // 54
+    "include",          // 55
+    "ifstrength",       // 56
+    "definesound",      // 57
+    "guts",             // 58
+    "ifspawnedby",      // 59
+    "gamestartup",      // 60
+    "wackplayer",       // 61
+    "ifgapzl",          // 62
+    "ifhitspace",       // 63
+    "ifoutside",        // 64
+    "ifmultiplayer",    // 65
+    "operate",          // 66
+    "ifinspace",        // 67
+    "debug",            // 68
+    "endofgame",        // 69
+    "ifbulletnear",     // 70
+    "ifrespawn",        // 71
+    "iffloordistl",     // 72
+    "ifceilingdistl",   // 73
+    "spritepal",        // 74
+    "ifpinventory",     // 75
+    "betaname",         // 76
+    "cactor",           // 77
+    "ifphealthl",       // 78
+    "definequote",      // 79
+    "quote",            // 80
+    "ifinouterspace",   // 81
+    "ifnotmoving",      // 82
+    "respawnhitag",        // 83
+    "tip",             // 84
+    "ifspritepal",      // 85
+    "money",         // 86
+    "soundonce",         // 87
+    "addkills",         // 88
+    "stopsound",        // 89
+    "ifawayfromwall",       // 90
+    "ifcanseetarget",   // 91
+    "globalsound",  // 92
+    "lotsofglass", // 93
+    "ifgotweaponce", // 94
+    "getlastpal", // 95
+    "pkick",  // 96
+    "mikesnd", // 97
+    "useractor",  // 98
+    "sizeat",  // 99
+    "addstrength", // 100   [#]
+    "cstator", // 101
+    "mail", // 102
+    "paper", // 103
+    "tossweapon", // 104
+    "sleeptime", // 105
+    "nullop", // 106
+    "definevolumename", // 107
+    "defineskillname", // 108
+    "ifnosounds", // 109
+    "clipdist", // 110
+    "ifangdiffl" // 111
+};
+
+
+short getincangle(short a,short na)
+{
+    a &= 2047;
+    na &= 2047;
+
+    if(klabs(a-na) < 1024)
+        return (na-a);
+    else
+    {
+        if(na > 1024) na -= 2048;
+        if(a > 1024) a -= 2048;
+
+        na -= 2048;
+        a -= 2048;
+        return (na-a);
+    }
+}
+
+char ispecial(char c)
+{
+    if(c == 0x0a)
+    {
+        line_number++;
+        return 1;
+    }
+
+    if(c == ' ' || c == 0x0d)
+        return 1;
+
+    return 0;
+}
+
+char isaltok(char c)
+{
+    return ( isalnum(c) || c == '{' || c == '}' || c == '/' || c == '*' || c == '-' || c == '_' || c == '.');
+}
+
+void getglobalz(short i)
+{
+    long hz,lz,zr;
+
+    spritetype *s = &sprite[i];
+
+    if( s->statnum == 10 || s->statnum == 6 || s->statnum == 2 || s->statnum == 1 || s->statnum == 4)
+    {
+        if(s->statnum == 4)
+            zr = 4L;
+        else zr = 127L;
+
+        getzrange(s->x,s->y,s->z-(FOURSLEIGHT),s->sectnum,&hittype[i].ceilingz,&hz,&hittype[i].floorz,&lz,zr,CLIPMASK0);
+
+        if( (lz&49152) == 49152 && (sprite[lz&(MAXSPRITES-1)].cstat&48) == 0 )
+        {
+            lz &= (MAXSPRITES-1);
+            if( badguy(&sprite[lz]) && sprite[lz].pal != 1)
+            {
+                if( s->statnum != 4 )
+                {
+                    hittype[i].dispicnum = -4; // No shadows on actors
+                    s->xvel = -256;
+                    ssp(i,CLIPMASK0);
+                }
+            }
+            else if(sprite[lz].picnum == APLAYER && badguy(s) )
+            {
+                hittype[i].dispicnum = -4; // No shadows on actors
+                s->xvel = -256;
+                ssp(i,CLIPMASK0);
+            }
+            else if(s->statnum == 4 && sprite[lz].picnum == APLAYER)
+                if(s->owner == lz)
+            {
+                hittype[i].ceilingz = sector[s->sectnum].ceilingz;
+                hittype[i].floorz   = sector[s->sectnum].floorz;
+            }
+        }
+    }
+    else
+    {
+        hittype[i].ceilingz = sector[s->sectnum].ceilingz;
+        hittype[i].floorz   = sector[s->sectnum].floorz;
+    }
+}
+
+
+void makeitfall(short i)
+{
+    spritetype *s = &sprite[i];
+    long hz,lz,c;
+
+    if( floorspace(s->sectnum) )
+        c = 0;
+    else
+    {
+        if( ceilingspace(s->sectnum) || sector[s->sectnum].lotag == 2)
+            c = gc/6;
+        else c = gc;
+    }
+
+    if( ( s->statnum == 1 || s->statnum == 10 || s->statnum == 2 || s->statnum == 6 ) )
+        getzrange(s->x,s->y,s->z-(FOURSLEIGHT),s->sectnum,&hittype[i].ceilingz,&hz,&hittype[i].floorz,&lz,127L,CLIPMASK0);
+    else
+    {
+        hittype[i].ceilingz = sector[s->sectnum].ceilingz;
+        hittype[i].floorz   = sector[s->sectnum].floorz;
+    }
+
+    if( s->z < hittype[i].floorz-(FOURSLEIGHT) )
+    {
+        if( sector[s->sectnum].lotag == 2 && s->zvel > 3122 )
+            s->zvel = 3144;
+        if(s->zvel < 6144)
+            s->zvel += c;
+        else s->zvel = 6144;
+        s->z += s->zvel;
+    }
+    if( s->z >= hittype[i].floorz-(FOURSLEIGHT) )
+    {
+        s->z = hittype[i].floorz - FOURSLEIGHT;
+        s->zvel = 0;
+    }
+}
+
+
+void getlabel(void)
+{
+    long i;
+
+    while( isalnum(*textptr) == 0 )
+    {
+        if(*textptr == 0x0a) line_number++;
+        textptr++;
+        if( *textptr == 0)
+            return;
+    }
+
+    i = 0;
+    while( ispecial(*textptr) == 0 )
+        label[(labelcnt<<6)+i++] = *(textptr++);
+
+    label[(labelcnt<<6)+i] = 0;
+}
+
+long keyword(void)
+{
+    long i;
+    char *temptextptr;
+
+    temptextptr = textptr;
+
+    while( isaltok(*temptextptr) == 0 )
+    {
+        temptextptr++;
+        if( *temptextptr == 0 )
+            return 0;
+    }
+
+    i = 0;
+    while( isaltok(*temptextptr) )
+    {
+        tempbuf[i] = *(temptextptr++);
+        i++;
+    }
+    tempbuf[i] = 0;
+
+    for(i=0;i<NUMKEYWORDS;i++)
+        if( strcmp( tempbuf,keyw[i]) == 0 )
+            return i;
+
+    return -1;
+}
+
+long transword(void) //Returns its code #
+{
+    long i, l;
+
+    while( isaltok(*textptr) == 0 )
+    {
+        if(*textptr == 0x0a) line_number++;
+        if( *textptr == 0 )
+            return -1;
+        textptr++;
+    }
+
+    l = 0;
+    while( isaltok(*(textptr+l)) )
+    {
+        tempbuf[l] = textptr[l];
+        l++;
+    }
+    tempbuf[l] = 0;
+
+    for(i=0;i<NUMKEYWORDS;i++)
+    {
+        if( strcmp( tempbuf,keyw[i]) == 0 )
+        {
+            *scriptptr = i;
+            textptr += l;
+            scriptptr++;
+            return i;
+        }
+    }
+
+    textptr += l;
+
+    if( tempbuf[0] == '{' && tempbuf[1] != 0)
+        printf("  * ERROR!(L%ld) Expecting a SPACE or CR between '{' and '%s'.\n",line_number,tempbuf+1);
+    else if( tempbuf[0] == '}' && tempbuf[1] != 0)
+        printf("  * ERROR!(L%ld) Expecting a SPACE or CR between '}' and '%s'.\n",line_number,tempbuf+1);
+    else if( tempbuf[0] == '/' && tempbuf[1] == '/' && tempbuf[2] != 0 )
+        printf("  * ERROR!(L%ld) Expecting a SPACE between '//' and '%s'.\n",line_number,tempbuf+2);
+    else if( tempbuf[0] == '/' && tempbuf[1] == '*' && tempbuf[2] != 0 )
+        printf("  * ERROR!(L%ld) Expecting a SPACE between '/*' and '%s'.\n",line_number,tempbuf+2);
+    else if( tempbuf[0] == '*' && tempbuf[1] == '/' && tempbuf[2] != 0 )
+        printf("  * ERROR!(L%ld) Expecting a SPACE between '*/' and '%s'.\n",line_number,tempbuf+2);
+    else printf("  * ERROR!(L%ld) Expecting key word, but found '%s'.\n",line_number,tempbuf);
+
+    error++;
+    return -1;
+}
+
+void transnum(void)
+{
+    long i, l;
+
+    while( isaltok(*textptr) == 0 )
+    {
+        if(*textptr == 0x0a) line_number++;
+        textptr++;
+        if( *textptr == 0 )
+            return;
+    }
+
+
+    l = 0;
+    while( isaltok(*(textptr+l)) )
+    {
+        tempbuf[l] = textptr[l];
+        l++;
+    }
+    tempbuf[l] = 0;
+
+    for(i=0;i<NUMKEYWORDS;i++)
+        if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+    {
+        error++;
+        printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+        textptr+=l;
+    }
+
+
+    for(i=0;i<labelcnt;i++)
+    {
+        if( strcmp(tempbuf,label+(i<<6)) == 0 )
+        {
+            *scriptptr = labelcode[i];
+            scriptptr++;
+            textptr += l;
+            return;
+        }
+    }
+
+    if( isdigit(*textptr) == 0 && *textptr != '-')
+    {
+        printf("  * ERROR!(L%ld) Parameter '%s' is undefined.\n",line_number,tempbuf);
+        error++;
+        textptr+=l;
+        return;
+    }
+
+    *scriptptr = atol(textptr);
+    scriptptr++;
+
+    textptr += l;
+}
+
+
+char parsecommand(int readfromGRP)
+{
+    long i, j, k, *tempscrptr;
+    char done, *origtptr, temp_ifelse_check, tw;
+    short temp_line_number;
+    int fp;
+
+    if( error > 12 || ( *textptr == '\0' ) || ( *(textptr+1) == '\0' ) ) return 1;
+
+    tw = transword();
+
+    switch(tw)
+    {
+        default:
+        case -1:
+            return 0; //End
+        case 39:      //Rem endrem
+            scriptptr--;
+            j = line_number;
+            do
+            {
+                textptr++;
+                if(*textptr == 0x0a) line_number++;
+                if( *textptr == 0 )
+                {
+                    printf("  * ERROR!(L%ld) Found '/*' with no '*/'.\n",j,label+(labelcnt<<6));
+                    error++;
+                    return 0;
+                }
+            }
+            while( *textptr != '*' || *(textptr+1) != '/' );
+            textptr+=2;
+            return 0;
+        case 17:
+            if( parsing_actor == 0 && parsing_state == 0 )
+            {
+                getlabel();
+                scriptptr--;
+                labelcode[labelcnt] = (long) scriptptr;
+                labelcnt++;
+
+                parsing_state = 1;
+
+                return 0;
+            }
+
+            getlabel();
+
+            for(i=0;i<NUMKEYWORDS;i++)
+                if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+                {
+                    error++;
+                    printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+                    return 0;
+                }
+
+            for(j=0;j<labelcnt;j++)
+            {
+                if( strcmp(label+(j<<6),label+(labelcnt<<6)) == 0 )
+                {
+                    *scriptptr = labelcode[j];
+                    break;
+                }
+            }
+
+            if(j==labelcnt)
+            {
+                printf("  * ERROR!(L%ld) State '%s' not found.\n",line_number,label+(labelcnt<<6));
+                error++;
+            }
+            scriptptr++;
+            return 0;
+
+        case 15:
+        case 92:
+        case 87:
+        case 89:
+        case 93:
+            transnum();
+            return 0;
+
+        case 18:
+            if( parsing_state == 0 )
+            {
+                printf("  * ERROR!(L%ld) Found 'ends' with no 'state'.\n",line_number);
+                error++;
+            }
+//            else
+            {
+                if( num_squigilly_brackets > 0 )
+                {
+                    printf("  * ERROR!(L%ld) Found more '{' than '}' before 'ends'.\n",line_number);
+                    error++;
+                }
+                if( num_squigilly_brackets < 0 )
+                {
+                    printf("  * ERROR!(L%ld) Found more '}' than '{' before 'ends'.\n",line_number);
+                    error++;
+                }
+                parsing_state = 0;
+            }
+            return 0;
+        case 19:
+            getlabel();
+            // Check to see it's already defined
+
+            for(i=0;i<NUMKEYWORDS;i++)
+                if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+                {
+                    error++;
+                    printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+                    return 0;
+                }
+
+            for(i=0;i<labelcnt;i++)
+            {
+                if( strcmp(label+(labelcnt<<6),label+(i<<6)) == 0 )
+                {
+                    warning++;
+                    printf("  * WARNING.(L%ld) Duplicate definition '%s' ignored.\n",line_number,label+(labelcnt<<6));
+                    break;
+                }
+            }
+
+            transnum();
+            if(i == labelcnt)
+                labelcode[labelcnt++] = *(scriptptr-1);
+            scriptptr -= 2;
+            return 0;
+        case 14:
+
+            for(j = 0;j < 4;j++)
+            {
+                if( keyword() == -1 )
+                    transnum();
+                else break;
+            }
+
+            while(j < 4)
+            {
+                *scriptptr = 0;
+                scriptptr++;
+                j++;
+            }
+            return 0;
+
+        case 32:
+            if( parsing_actor || parsing_state )
+            {
+                transnum();
+
+                j = 0;
+                while(keyword() == -1)
+                {
+                    transnum();
+                    scriptptr--;
+                    j |= *scriptptr;
+                }
+                *scriptptr = j;
+                scriptptr++;
+            }
+            else
+            {
+                scriptptr--;
+                getlabel();
+                // Check to see it's already defined
+
+                for(i=0;i<NUMKEYWORDS;i++)
+                    if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+                {
+                    error++;
+                    printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+                    return 0;
+                }
+
+                for(i=0;i<labelcnt;i++)
+                    if( strcmp(label+(labelcnt<<6),label+(i<<6)) == 0 )
+                    {
+                        warning++;
+                        printf("  * WARNING.(L%ld) Duplicate move '%s' ignored.\n",line_number,label+(labelcnt<<6));
+                        break;
+                    }
+                if(i == labelcnt)
+                    labelcode[labelcnt++] = (long) scriptptr;
+                for(j=0;j<2;j++)
+                {
+                    if(keyword() >= 0) break;
+                    transnum();
+                }
+                for(k=j;k<2;k++)
+                {
+                    *scriptptr = 0;
+                    scriptptr++;
+                }
+            }
+            return 0;
+
+        case 54:
+            {
+                scriptptr--;
+                transnum(); // Volume Number (0/4)
+                scriptptr--;
+
+                k = *scriptptr-1;
+
+                if(k >= 0) // if it's background music
+                {
+                    i = 0;
+                    while(keyword() == -1)
+                    {
+                        while( isaltok(*textptr) == 0 )
+                        {
+                            if(*textptr == 0x0a) line_number++;
+                            textptr++;
+                            if( *textptr == 0 ) break;
+                        }
+                        j = 0;
+                        while( isaltok(*(textptr+j)) )
+                        {
+                            music_fn[k][i][j] = textptr[j];
+                            j++;
+                        }
+                        music_fn[k][i][j] = '\0';
+                        textptr += j;
+                        if(i > 9) break;
+                        i++;
+                    }
+                }
+                else
+                {
+                    i = 0;
+                    while(keyword() == -1)
+                    {
+                        while( isaltok(*textptr) == 0 )
+                        {
+                            if(*textptr == 0x0a) line_number++;
+                            textptr++;
+                            if( *textptr == 0 ) break;
+                        }
+                        j = 0;
+                        while( isaltok(*(textptr+j)) )
+                        {
+                            env_music_fn[i][j] = textptr[j];
+                            j++;
+                        }
+                        env_music_fn[i][j] = '\0';
+
+                        textptr += j;
+                        if(i > 9) break;
+                        i++;
+                    }
+                }
+            }
+            return 0;
+        case 55: // include other con files.
+			{
+				char includedconfile[512];
+				scriptptr--;
+				while( isaltok(*textptr) == 0 )
+				{
+					if(*textptr == 0x0a) line_number++;
+					textptr++;
+					if( *textptr == 0 ) break;
+				}
+				j = 0;
+				while( isaltok(*textptr) )
+				{
+					tempbuf[j] = *(textptr++);
+					j++;
+				}
+				tempbuf[j] = '\0';
+
+				// fix path for unix. (doesn't really matter...)			
+				FixFilePath(tempbuf);
+/*
+				// Are we loading a TC?
+				if(game_dir[0] != '\0')
+				{
+					// Yes
+					sprintf(includedconfile, "%s\\%s", game_dir, tempbuf);
+				}
+				else
+				{
+					// No
+					sprintf(includedconfile, "%s", tempbuf);
+				}
+*/
+				sprintf(includedconfile, "%s", tempbuf);
+
+				fp = TCkopen4load(includedconfile,readfromGRP);
+				if(fp <= 0)
+				{
+					error++;
+					printf("  * ERROR!(ln%ld) Could not find '%s'.\n",line_number,label+(labelcnt<<6));
+
+					printf("ERROR: could not open (%s)\n", includedconfile);
+					gameexit("");
+					return 0;
+				}
+
+				j = kfilelength(fp);
+
+				printf("Including: '%s'.\n", includedconfile);
+
+				temp_line_number = line_number;
+				line_number = 1;
+				temp_ifelse_check = checking_ifelse;
+				checking_ifelse = 0;
+				origtptr = textptr;
+				textptr = last_used_text+last_used_size;
+
+				*(textptr+j) = 0;
+
+				kread(fp,(char *)textptr,j);
+				kclose(fp);
+				ud.conCRC[0] = crc32_update((char *)textptr, j, ud.conCRC[0]);
+
+				do
+					done = parsecommand(readfromGRP);
+				while( done == 0 );
+
+				textptr = origtptr;
+				total_lines += line_number;
+				line_number = temp_line_number;
+				checking_ifelse = temp_ifelse_check;
+
+				return 0;
+			}
+        case 24:
+            if( parsing_actor || parsing_state )
+                transnum();
+            else
+            {
+                scriptptr--;
+                getlabel();
+
+                for(i=0;i<NUMKEYWORDS;i++)
+                    if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+                    {
+                        error++;
+                        printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+                        return 0;
+                    }
+
+                for(i=0;i<labelcnt;i++)
+                    if( strcmp(label+(labelcnt<<6),label+(i<<6)) == 0 )
+                    {
+                        warning++;
+                        printf("  * WARNING.(L%ld) Duplicate ai '%s' ignored.\n",line_number,label+(labelcnt<<6));
+                        break;
+                    }
+
+                if(i == labelcnt)
+                    labelcode[labelcnt++] = (long) scriptptr;
+
+                for(j=0;j<3;j++)
+                {
+                    if(keyword() >= 0) break;
+                    if(j == 2)
+                    {
+                        k = 0;
+                        while(keyword() == -1)
+                        {
+                            transnum();
+                            scriptptr--;
+                            k |= *scriptptr;
+                        }
+                        *scriptptr = k;
+                        scriptptr++;
+                        return 0;
+                    }
+                    else transnum();
+                }
+                for(k=j;k<3;k++)
+                {
+                    *scriptptr = 0;
+                    scriptptr++;
+                }
+            }
+            return 0;
+
+        case 7:
+            if( parsing_actor || parsing_state )
+                transnum();
+            else
+            {
+                scriptptr--;
+                getlabel();
+                // Check to see it's already defined
+
+                for(i=0;i<NUMKEYWORDS;i++)
+                    if( strcmp( label+(labelcnt<<6),keyw[i]) == 0 )
+                    {
+                        error++;
+                        printf("  * ERROR!(L%ld) Symbol '%s' is a key word.\n",line_number,label+(labelcnt<<6));
+                        return 0;
+                    }
+
+                for(i=0;i<labelcnt;i++)
+                    if( strcmp(label+(labelcnt<<6),label+(i<<6)) == 0 )
+                    {
+                        warning++;
+                        printf("  * WARNING.(L%ld) Duplicate action '%s' ignored.\n",line_number,label+(labelcnt<<6));
+                        break;
+                    }
+
+                if(i == labelcnt)
+                    labelcode[labelcnt++] = (long) scriptptr;
+
+                for(j=0;j<5;j++)
+                {
+                    if(keyword() >= 0) break;
+                    transnum();
+                }
+                for(k=j;k<5;k++)
+                {
+                    *scriptptr = 0;
+                    scriptptr++;
+                }
+            }
+            return 0;
+
+        case 1:
+            if( parsing_state )
+            {
+                printf("  * ERROR!(L%ld) Found 'actor' within 'state'.\n",line_number);
+                error++;
+            }
+
+            if( parsing_actor )
+            {
+                printf("  * ERROR!(L%ld) Found 'actor' within 'actor'.\n",line_number);
+                error++;
+            }
+
+            num_squigilly_brackets = 0;
+            scriptptr--;
+            parsing_actor = scriptptr;
+
+            transnum();
+            scriptptr--;
+            actorscrptr[*scriptptr] = parsing_actor;
+
+            for(j=0;j<4;j++)
+            {
+                *(parsing_actor+j) = 0;
+                if(j == 3)
+                {
+                    j = 0;
+                    while(keyword() == -1)
+                    {
+                        transnum();
+                        scriptptr--;
+                        j |= *scriptptr;
+                    }
+                    *scriptptr = j;
+                    scriptptr++;
+                    break;
+                }
+                else
+                {
+                    if(keyword() >= 0)
+                    {
+                        scriptptr += (4-j);
+                        break;
+                    }
+                    transnum();
+
+                    *(parsing_actor+j) = *(scriptptr-1);
+                }
+            }
+
+            checking_ifelse = 0;
+
+            return 0;
+
+        case 98:
+
+            if( parsing_state )
+            {
+                printf("  * ERROR!(L%ld) Found 'useritem' within 'state'.\n",line_number);
+                error++;
+            }
+
+            if( parsing_actor )
+            {
+                printf("  * ERROR!(L%ld) Found 'useritem' within 'actor'.\n",line_number);
+                error++;
+            }
+
+            num_squigilly_brackets = 0;
+            scriptptr--;
+            parsing_actor = scriptptr;
+
+            transnum();
+            scriptptr--;
+            j = *scriptptr;
+
+            transnum();
+            scriptptr--;
+            actorscrptr[*scriptptr] = parsing_actor;
+            actortype[*scriptptr] = j;
+
+            for(j=0;j<4;j++)
+            {
+                *(parsing_actor+j) = 0;
+                if(j == 3)
+                {
+                    j = 0;
+                    while(keyword() == -1)
+                    {
+                        transnum();
+                        scriptptr--;
+                        j |= *scriptptr;
+                    }
+                    *scriptptr = j;
+                    scriptptr++;
+                    break;
+                }
+                else
+                {
+                    if(keyword() >= 0)
+                    {
+                        scriptptr += (4-j);
+                        break;
+                    }
+                    transnum();
+
+                    *(parsing_actor+j) = *(scriptptr-1);
+                }
+            }
+
+            checking_ifelse = 0;
+
+            return 0;
+
+
+
+        case 11:
+        case 13:
+        case 25:
+        case 31:
+        case 40:
+        case 52:
+        case 69:
+        case 74:
+        case 77:
+        case 80:
+        case 86:
+        case 88:
+        case 68:
+        case 100:
+        case 101:
+        case 102:
+        case 103:
+        case 105:
+        case 110:
+            transnum();
+            return 0;
+
+        case 2:
+        case 23:
+        case 28:
+        case 99:
+        case 37:
+        case 48:
+        case 58:
+            transnum();
+            transnum();
+            break;
+        case 50:
+            transnum();
+            transnum();
+            transnum();
+            transnum();
+            transnum();
+            break;
+        case 10:
+            if( checking_ifelse )
+            {
+                checking_ifelse--;
+                tempscrptr = scriptptr;
+                scriptptr++; //Leave a spot for the fail location
+                parsecommand(readfromGRP);
+                *tempscrptr = (long) scriptptr;
+            }
+            else
+            {
+                scriptptr--;
+                error++;
+                printf("  * ERROR!(L%ld) Found 'else' with no 'if'.\n",line_number);
+            }
+
+            return 0;
+
+        case 75:
+            transnum();
+        case 3:
+        case 8:
+        case 9:
+        case 21:
+        case 33:
+        case 34:
+        case 35:
+        case 41:
+        case 46:
+        case 53:
+        case 56:
+        case 59:
+        case 62:
+        case 72:
+        case 73:
+//        case 74:
+        case 78:
+        case 85:
+        case 94:
+        case 111:
+            transnum();
+        case 43:
+        case 44:
+        case 49:
+        case 5:
+        case 6:
+        case 27:
+        case 26:
+        case 45:
+        case 51:
+        case 63:
+        case 64:
+        case 65:
+        case 67:
+        case 70:
+        case 71:
+        case 81:
+        case 82:
+        case 90:
+        case 91:
+        case 109:
+
+            if(tw == 51)
+            {
+                j = 0;
+                do
+                {
+                    transnum();
+                    scriptptr--;
+                    j |= *scriptptr;
+                }
+                while(keyword() == -1);
+                *scriptptr = j;
+                scriptptr++;
+            }
+
+            tempscrptr = scriptptr;
+            scriptptr++; //Leave a spot for the fail location
+
+            do
+            {
+                j = keyword();
+                if(j == 20 || j == 39)
+                    parsecommand(readfromGRP);
+            } while(j == 20 || j == 39);
+
+            parsecommand(readfromGRP);
+
+            *tempscrptr = (long) scriptptr;
+
+            checking_ifelse++;
+            return 0;
+        case 29:
+            num_squigilly_brackets++;
+            do
+                done = parsecommand(readfromGRP);
+            while( done == 0 );
+            return 0;
+        case 30:
+            num_squigilly_brackets--;
+            if( num_squigilly_brackets < 0 )
+            {
+                printf("  * ERROR!(L%ld) Found more '}' than '{'.\n",line_number);
+                error++;
+            }
+            return 1;
+        case 76:
+            scriptptr--;
+            j = 0;
+            while( *textptr != 0x0a )
+            {
+                betaname[j] = *textptr;
+                j++; textptr++;
+            }
+            betaname[j] = 0;
+            return 0;
+        case 20:
+            scriptptr--; //Negate the rem
+            while( *textptr != 0x0a )
+                textptr++;
+
+            // line_number++;
+            return 0;
+
+        case 107:
+            scriptptr--;
+            transnum();
+            scriptptr--;
+            j = *scriptptr;
+            while( *textptr == ' ' ) textptr++;
+
+            i = 0;
+
+            while( *textptr != 0x0a )
+            {
+                volume_names[j][i] = toupper(*textptr);
+                textptr++,i++;
+                if(i >= 32)
+                {
+                    printf("  * ERROR!(L%ld) Volume name exceeds character size limit of 32.\n",line_number);
+                    error++;
+                    while( *textptr != 0x0a ) textptr++;
+                    break;
+                }
+            }
+#ifdef PLATFORM_UNIX
+            volume_names[j][i] = '\0';
+#else
+            volume_names[j][i-1] = '\0';
+#endif
+            return 0;
+        case 108:
+            scriptptr--;
+            transnum();
+            scriptptr--;
+            j = *scriptptr;
+            while( *textptr == ' ' ) textptr++;
+
+            i = 0;
+
+            while( *textptr != 0x0a )
+            {
+                skill_names[j][i] = toupper(*textptr);
+                textptr++,i++;
+                if(i >= 32)
+                {
+                    printf("  * ERROR!(L%ld) Skill name exceeds character size limit of 32.\n",line_number);
+                    error++;
+                    while( *textptr != 0x0a ) textptr++;
+                    break;
+                }
+            }
+#ifdef PLATFORM_UNIX
+            skill_names[j][i] = '\0';
+#else
+            skill_names[j][i-1] = '\0';
+#endif
+            return 0;
+
+        case 0:
+            scriptptr--;
+            transnum();
+            scriptptr--;
+            j = *scriptptr;
+            transnum();
+            scriptptr--;
+            k = *scriptptr;
+            while( *textptr == ' ' ) textptr++;
+
+            i = 0;
+            while( *textptr != ' ' && *textptr != 0x0a )
+            {
+                level_file_names[j*11+k][i] = *textptr;
+                textptr++,i++;
+                if(i > 127)
+                {
+                    printf("  * ERROR!(L%ld) Level file name exceeds character size limit of 128.\n",line_number);
+                    error++;
+                    while( *textptr != ' ') textptr++;
+                    break;
+                }
+            }
+#ifdef PLATFORM_UNIX
+            level_names[j*11+k][i] = '\0';
+#else
+            level_names[j*11+k][i-1] = '\0';
+#endif
+
+            while( *textptr == ' ' ) textptr++;
+
+            partime[j*11+k] =
+                (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*26*60)+
+                (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*26);
+
+            textptr += 5;
+            while( *textptr == ' ' ) textptr++;
+
+            designertime[j*11+k] =
+                (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*26*60)+
+                (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*26);
+
+            textptr += 5;
+            while( *textptr == ' ' ) textptr++;
+
+            i = 0;
+
+            while( *textptr != 0x0a )
+            {
+                level_names[j*11+k][i] = toupper(*textptr);
+                textptr++,i++;
+                if(i >= 32)
+                {
+                    printf("  * ERROR!(L%ld) Level name exceeds character size limit of 32.\n",line_number);
+                    error++;
+                    while( *textptr != 0x0a ) textptr++;
+                    break;
+                }
+            }
+#ifdef PLATFORM_UNIX
+            level_names[j*11+k][i] = '\0';
+#else
+            level_names[j*11+k][i-1] = '\0';
+#endif
+            return 0;
+
+        case 79:
+            scriptptr--;
+            transnum();
+            k = *(scriptptr-1);
+            if(k >= NUMOFFIRSTTIMEACTIVE)
+            {
+                printf("  * ERROR!(L%ld) Quote amount exceeds limit of %ld characters.\n",line_number,NUMOFFIRSTTIMEACTIVE);
+                error++;
+            }
+            scriptptr--;
+            i = 0;
+            while( *textptr == ' ' )
+                textptr++;
+
+            while( *textptr != 0x0a )
+            {
+                fta_quotes[k][i] = *textptr;
+                textptr++,i++;
+                if(i >= 64)
+                {
+                    printf("  * ERROR!(L%ld) Quote exceeds character size limit of 64.\n",line_number);
+                    error++;
+                    while( *textptr != 0x0a ) textptr++;
+                    break;
+                }
+            }
+            fta_quotes[k][i] = '\0';
+            return 0;
+        case 57:
+            scriptptr--;
+            transnum();
+            k = *(scriptptr-1);
+            if(k >= NUM_SOUNDS)
+            {
+                printf("  * ERROR!(L%ld) Exceeded sound limit of %ld.\n",line_number,NUM_SOUNDS);
+                error++;
+            }
+            scriptptr--;
+            i = 0;
+            while( *textptr == ' ')
+                textptr++;
+
+            while( *textptr != ' ' )
+            {
+                sounds[k][i] = *textptr;
+                textptr++,i++;
+                if(i >= 13)
+                {
+                    puts(sounds[k]);
+                    printf("  * ERROR!(L%ld) Sound filename exceeded limit of 13 characters.\n",line_number);
+                    error++;
+                    while( *textptr != ' ' ) textptr++;
+                    break;
+                }
+            }
+            sounds[k][i] = '\0';
+
+            transnum();
+            soundps[k] = *(scriptptr-1);
+            scriptptr--;
+            transnum();
+            soundpe[k] = *(scriptptr-1);
+            scriptptr--;
+            transnum();
+            soundpr[k] = *(scriptptr-1);
+            scriptptr--;
+            transnum();
+            soundm[k] = *(scriptptr-1);
+            scriptptr--;
+            transnum();
+            soundvo[k] = *(scriptptr-1);
+            scriptptr--;
+            return 0;
+        
+        case 4:
+            if( parsing_actor == 0 )
+            {
+                printf("  * ERROR!(L%ld) Found 'enda' without defining 'actor'.\n",line_number);
+                error++;
+            }
+//            else
+            {
+                if( num_squigilly_brackets > 0 )
+                {
+                    printf("  * ERROR!(L%ld) Found more '{' than '}' before 'enda'.\n",line_number);
+                    error++;
+                }
+                parsing_actor = 0;
+            }
+
+            return 0;
+        case 12:
+        case 16:
+        case 84:
+//        case 21:
+        case 22:    //KILLIT
+        case 36:
+        case 38:
+        case 42:
+        case 47:
+        case 61:
+        case 66:
+        case 83:
+        case 95:
+        case 96:
+        case 97:
+        case 104:
+        case 106:
+            return 0;
+        case 60:
+		{
+			long params[30];
+
+			scriptptr--;
+			for(j = 0; j < 30; j++)
+        	{
+				transnum();
+				scriptptr--;
+				params[j] = *scriptptr;
+
+				if (j != 25) continue; // we try to guess if we are using 1.3/1.3d or 1.4/1.5 con files
+
+				if (keyword() != -1) // Is the 26th variable set? If so then it's probably a 1.4/1.5 con file
+				{
+					break;
+				}
+				else 
+				{
+					conVersion = 15;
+				}
+			}
+
+			/* From Jonathon's code --mk
+			v1.3d					v1.5
+
+			DEFAULTVISIBILITY		DEFAULTVISIBILITY
+			GENERICIMPACTDAMAGE		GENERICIMPACTDAMAGE
+			MAXPLAYERHEALTH			MAXPLAYERHEALTH
+			STARTARMORHEALTH		STARTARMORHEALTH
+			RESPAWNACTORTIME		RESPAWNACTORTIME
+			RESPAWNITEMTIME			RESPAWNITEMTIME
+			RUNNINGSPEED			RUNNINGSPEED
+									GRAVITATIONALCONSTANT
+			RPGBLASTRADIUS			RPGBLASTRADIUS
+			PIPEBOMBRADIUS			PIPEBOMBRADIUS
+			SHRINKERBLASTRADIUS		SHRINKERBLASTRADIUS
+			TRIPBOMBBLASTRADIUS		TRIPBOMBBLASTRADIUS
+			MORTERBLASTRADIUS		MORTERBLASTRADIUS
+			BOUNCEMINEBLASTRADIUS	BOUNCEMINEBLASTRADIUS
+			SEENINEBLASTRADIUS		SEENINEBLASTRADIUS
+			MAXPISTOLAMMO			MAXPISTOLAMMO
+			MAXSHOTGUNAMMO			MAXSHOTGUNAMMO
+			MAXCHAINGUNAMMO			MAXCHAINGUNAMMO
+			MAXRPGAMMO				MAXRPGAMMO
+			MAXHANDBOMBAMMO			MAXHANDBOMBAMMO
+			MAXSHRINKERAMMO			MAXSHRINKERAMMO
+			MAXDEVISTATORAMMO		MAXDEVISTATORAMMO
+			MAXTRIPBOMBAMMO			MAXTRIPBOMBAMMO
+			MAXFREEZEAMMO			MAXFREEZEAMMO
+									MAXGROWAMMO
+			CAMERASDESTRUCTABLE		CAMERASDESTRUCTABLE
+			NUMFREEZEBOUNCES		NUMFREEZEBOUNCES
+			FREEZERHURTOWNER		FREEZERHURTOWNER
+									QSIZE
+									TRIPBOMBLASERMODE
+			*/
+			
+			// Used Jonathon Fowler's parser. Cool to make the code 
+			// robust to 1.3 con files --mk
+
+			j = 0;
+
+			ud.const_visibility		= params[j++];
+			impact_damage			= params[j++];
+			max_player_health		= params[j++];
+			max_armour_amount		= params[j++];
+			respawnactortime		= params[j++];
+			respawnitemtime			= params[j++];
+			dukefriction			= params[j++];
+			if (conVersion == 15) 
+				gc					= params[j++];
+			else
+				gc					= 176; // default (guess) when using 1.3d CONs
+			rpgblastradius			= params[j++];
+			pipebombblastradius		= params[j++];
+			shrinkerblastradius		= params[j++];
+			tripbombblastradius		= params[j++];
+			morterblastradius		= params[j++];
+			bouncemineblastradius	= params[j++];
+			seenineblastradius		= params[j++];
+			max_ammo_amount[PISTOL_WEAPON]		= params[j++];
+			max_ammo_amount[SHOTGUN_WEAPON]		= params[j++];
+			max_ammo_amount[CHAINGUN_WEAPON]	= params[j++];
+			max_ammo_amount[RPG_WEAPON]			= params[j++];
+			max_ammo_amount[HANDBOMB_WEAPON]	= params[j++];
+			max_ammo_amount[SHRINKER_WEAPON]	= params[j++];
+			max_ammo_amount[DEVISTATOR_WEAPON]	= params[j++];
+			max_ammo_amount[TRIPBOMB_WEAPON]	= params[j++];
+			max_ammo_amount[FREEZE_WEAPON]		= params[j++];
+			if (conVersion == 15) 
+				max_ammo_amount[GROW_WEAPON]	= params[j++];
+			else
+				max_ammo_amount[GROW_WEAPON]	= 50; // default (guess) when using 1.3d CONs
+			camerashitable			= params[j++];
+			numfreezebounces		= params[j++];
+			freezerhurtowner		= params[j++];
+			if (conVersion == 15) 
+			{
+				spriteqamount		= params[j++];
+
+				if(spriteqamount > 1024) 
+					spriteqamount	= 1024;
+				else if(spriteqamount < 0) 
+					spriteqamount	= 0;
+				lasermode			= params[j++];
+			}
+			else
+			{
+				// spriteqamount = 64 is the default
+				lasermode = 0; // default (guess) when using 1.3d CONs
+			}
+		}
+		return 0;
+
+	} // end of switch(tw)
+
+	return 0;
+}
+
+
+void passone(int readfromGRP)
+{
+
+    while( parsecommand(readfromGRP) == 0 );
+
+    if( (error+warning) > 12)
+        puts(  "  * ERROR! Too many warnings or errors.");
+
+}
+
+char *defaultcons[3] =
+{
+     "GAME.CON",
+     "USER.CON",
+     "DEFS.CON"
+};
+
+void copydefaultcons(void)
+{
+    long i, fs, fpi;
+    FILE *fpo;
+
+    for(i=0;i<3;i++)
+    {
+        fpi = TCkopen4load(defaultcons[i],1);
+        fpo = fopen( defaultcons[i],"wb");
+
+        if(fpi == 0)
+        {
+// CTW - MODIFICATION
+//          if(fpo == -1) fclose(fpo);
+            if(fpo == NULL) fclose(fpo);
+// CTW END - MODIFICATION
+            continue;
+        }
+// CTW - MODIFICATION
+//      if(fpo == -1)
+        if(fpo == NULL)
+// CTW END - MODIFICATION
+        {
+            if(fpi == 0) kclose(fpi);
+            continue;
+        }
+
+        fs = kfilelength(fpi);
+
+        kread(fpi,&hittype[0],fs);
+        fwrite(&hittype[0],fs,1,fpo);
+
+        kclose(fpi);
+        fclose(fpo);
+    }
+}
+
+void loadefs(char *filenam, char *mptr, int readfromGRP)
+{
+    int i;
+    long fs,fp;
+	char kbdKey;
+
+	memset(script, 0, sizeof(script));
+
+	// FIX_00071: do not ask for internal default con if 
+	// external con or internal con is buggy
+    fp = TCkopen4load(filenam,readfromGRP);
+    if( fp <= 0 )
+    {
+		Error(EXIT_SUCCESS, "ERROR: CON(%s) not found.\n", filenam);
+    }
+    else
+    {
+        printf("Compiling: '%s'.\n",filenam);
+
+        fs = kfilelength(fp);
+
+        last_used_text = textptr = (char *) mptr;
+        last_used_size = fs;
+
+        kread(fp,(char *)textptr,fs);
+        kclose(fp);
+		ud.conCRC[0]=0;
+		ud.conCRC[0] = crc32_update((char *)textptr, fs, ud.conCRC[0]);
+    }
+
+#ifdef PLATFORM_UNIX
+    textptr[fs - 1] = 0;
+#else
+    textptr[fs - 2] = 0;
+#endif
+
+    clearbuf(actorscrptr,MAXSPRITES,0L);
+    clearbufbyte(actortype,MAXSPRITES,0L);
+
+    labelcnt = 0;
+    scriptptr = script+1;
+    warning = 0;
+    error = 0;
+    line_number = 1;
+    total_lines = 0;
+
+    passone(readfromGRP); //Tokenize
+    *script = (long) scriptptr;
+
+    if(warning|error)
+        printf("Found %ld warning(s), %ld error(s).\n",warning,error);
+
+    if(error)
+    {
+		Error(EXIT_SUCCESS, "ERROR in CON(%s)\n", filenam);
+    }
+    else
+    {
+        total_lines += line_number;
+        printf("Code Size:%ld bytes(%ld labels).\n",(long)((scriptptr-script)<<2)-4,labelcnt);
+		ud.conSize[0] = (long)(scriptptr-script)-1;
+
+		// FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5
+		if( ud.conSize[0] == 16208 && labelcnt == 1794 && conVersion == 15)
+			conVersion = 14;
+		printf("Con version: Looks like v%d\n", conVersion);
+
+			// FIX_00022: Automatically recognize the shareware grp (v1.3) + full version (1.3d) +
+			//            atomic (1.4/1.5 grp) and the con files version (either 1.3 or 1.4) (JonoF's idea)
+
+		if(conVersion != 13 && (groupefil_crc32[0]==CRC_BASE_GRP_SHAREWARE_13 || 
+			groupefil_crc32[0]==CRC_BASE_GRP_FULL_13) && !groupefil_crc32[1])
+		{
+			printf(	"\nYou are trying to use a v1.3 Shareware/Full *.GRP with v1.4 or v1.5\n"
+					"external *.CON files. You may run in troubles by doing so and/or get\n"
+					"Out Of Synch errors. You can safely delete those files so xDuke will\n"
+					"always use the GRP internal CON files.\n"
+					"\nReload normal GRP internal *.CON files? (Y/N) : ");
+			do
+				kbdKey = getch() | ' ';
+			while(kbdKey != 'y' && kbdKey != 'n');
+			printf("%c\n", kbdKey);
+
+			if(kbdKey == 'y')
+			{
+				conVersion = 13;
+				loadefs(filenam, mptr, 1); // force GRP con files
+			}
+		}
+		else if(conVersion != 15 && groupefil_crc32[0]==CRC_BASE_GRP_ATOMIC_15 && !groupefil_crc32[1])
+		{
+			printf(	"\nYou are trying to use a v1.5 ATOMIC *.GRP with v1.4 or v1.3\n"
+					"external *.CON files. You may run in troubles by doing so and/or get\n"
+					"Out Of Synch errors. You can safely delete those files so xDuke will\n"
+					"always use the GRP internal CON files.\n"
+					"\nReload normal GRP internal *.CON files? (Y/N) : ");
+			do
+				kbdKey = getch() | ' ';
+			while(kbdKey != 'y' && kbdKey != 'n');
+			printf("%c\n", kbdKey);
+
+			if(kbdKey == 'y')
+			{
+				loadefs(filenam, mptr, 1); // force GRP con files
+			}
+		}else if(conVersion != 14 && groupefil_crc32[0]==CRC_BASE_GRP_PLUTONIUM_14 && !groupefil_crc32[1])
+		{
+			printf(	"\nYou are trying to use a v1.4 PLUTONIUM *.GRP with v1.3 or v1.5\n"
+					"external *.CON files. You may run in troubles by doing so and/or get\n"
+					"Out Of Synch errors. You can safely delete those files so xDuke will\n"
+					"always use the GRP internal CON files.\n"
+					"\nReload normal GRP internal *.CON files? (Y/N) : ");
+			do
+				kbdKey = getch() | ' ';
+			while(kbdKey != 'y' && kbdKey != 'n');
+			printf("%c\n", kbdKey);
+
+			if(kbdKey == 'y')
+			{
+				loadefs(filenam, mptr, 1); // force GRP con files
+			}
+		}
+	}
+}
+
+char dodge(spritetype *s)
+{
+    short i;
+    long bx,by,mx,my,bxvect,byvect,mxvect,myvect,d;
+
+    mx = s->x;
+    my = s->y;
+    mxvect = sintable[(s->ang+512)&2047]; myvect = sintable[s->ang&2047];
+
+    for(i=headspritestat[4];i>=0;i=nextspritestat[i]) //weapons list
+    {
+        if( OW == i || SECT != s->sectnum)
+            continue;
+
+        bx = SX-mx;
+        by = SY-my;
+        bxvect = sintable[(SA+512)&2047]; byvect = sintable[SA&2047];
+
+        if (mxvect*bx + myvect*by >= 0)
+            if (bxvect*bx + byvect*by < 0)
+        {
+            d = bxvect*by - byvect*bx;
+            if (klabs(d) < 65536*64)
+            {
+                s->ang -= 512+(TRAND&1024);
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+short furthestangle(short i,short angs)
+{
+    short j, hitsect,hitwall,hitspr,furthest_angle, angincs;
+    long hx, hy, hz, d, greatestd;
+    spritetype *s = &sprite[i];
+
+    greatestd = -(1<<30);
+    angincs = 2048/angs;
+
+    if(s->picnum != APLAYER)
+        if( (g_t[0]&63) > 2 ) return( s->ang + 1024 );
+
+    for(j=s->ang;j<(2048+s->ang);j+=angincs)
+    {
+        hitscan(s->x, s->y, s->z-(8<<8), s->sectnum,
+            sintable[(j+512)&2047],
+            sintable[j&2047],0,
+            &hitsect,&hitwall,&hitspr,&hx,&hy,&hz,CLIPMASK1);
+
+        d = klabs(hx-s->x) + klabs(hy-s->y);
+
+        if(d > greatestd)
+        {
+            greatestd = d;
+            furthest_angle = j;
+        }
+    }
+    return (furthest_angle&2047);
+}
+
+short furthestcanseepoint(short i,spritetype *ts,long *dax,long *day)
+{
+    short j, hitsect,hitwall,hitspr, angincs, tempang;
+    long hx, hy, hz, d, da;//, d, cd, ca,tempx,tempy,cx,cy;
+    spritetype *s = &sprite[i];
+
+    if( (g_t[0]&63) ) return -1;
+
+    if(ud.multimode < 2 && ud.player_skill < 3)
+        angincs = 2048/2;
+    else angincs = 2048/(1+(TRAND&1));
+
+    for(j=ts->ang;j<(2048+ts->ang);j+=(angincs-(TRAND&511)))
+    {
+        hitscan(ts->x, ts->y, ts->z-(16<<8), ts->sectnum,
+            sintable[(j+512)&2047],
+            sintable[j&2047],16384-(TRAND&32767),
+            &hitsect,&hitwall,&hitspr,&hx,&hy,&hz,CLIPMASK1);
+
+        d = klabs(hx-ts->x)+klabs(hy-ts->y);
+        da = klabs(hx-s->x)+klabs(hy-s->y);
+
+        if( d < da )
+            if(cansee(hx,hy,hz,hitsect,s->x,s->y,s->z-(16<<8),s->sectnum))
+        {
+            *dax = hx;
+            *day = hy;
+            return hitsect;
+        }
+    }
+    return -1;
+}
+
+
+
+
+void alterang(short a)
+{
+    short aang, angdif, goalang,j;
+    long ticselapsed, *moveptr;
+
+    moveptr = (long *)g_t[1];
+
+    ticselapsed = (g_t[0])&31;
+
+    aang = g_sp->ang;
+
+    g_sp->xvel += (*moveptr-g_sp->xvel)/5;
+    if(g_sp->zvel < 648) g_sp->zvel += ((*(moveptr+1)<<4)-g_sp->zvel)/5;
+
+    if(a&seekplayer)
+    {
+        j = ps[g_p].holoduke_on;
+
+        if(j >= 0 && cansee(sprite[j].x,sprite[j].y,sprite[j].z,sprite[j].sectnum,g_sp->x,g_sp->y,g_sp->z,g_sp->sectnum) )
+            g_sp->owner = j;
+        else g_sp->owner = ps[g_p].i;
+
+        if(sprite[g_sp->owner].picnum == APLAYER)
+            goalang = getangle(hittype[g_i].lastvx-g_sp->x,hittype[g_i].lastvy-g_sp->y);
+        else
+            goalang = getangle(sprite[g_sp->owner].x-g_sp->x,sprite[g_sp->owner].y-g_sp->y);
+
+        if(g_sp->xvel && g_sp->picnum != DRONE)
+        {
+            angdif = getincangle(aang,goalang);
+
+            if(ticselapsed < 2)
+            {
+                if( klabs(angdif) < 256)
+                {
+                    j = 128-(TRAND&256);
+                    g_sp->ang += j;
+                    if( hits(g_i) < 844 )
+                        g_sp->ang -= j;
+                }
+            }
+            else if(ticselapsed > 18 && ticselapsed < 26) // choose
+            {
+                if(klabs(angdif>>2) < 128) g_sp->ang = goalang;
+                else g_sp->ang += angdif>>2;
+            }
+        }
+        else g_sp->ang = goalang;
+    }
+
+    if(ticselapsed < 1)
+    {
+        j = 2;
+        if(a&furthestdir)
+        {
+            goalang = furthestangle(g_i,j);
+            g_sp->ang = goalang;
+            g_sp->owner = ps[g_p].i;
+        }
+
+        if(a&fleeenemy)
+        {
+            goalang = furthestangle(g_i,j);
+            g_sp->ang = goalang; // += angdif; //  = getincangle(aang,goalang)>>1;
+        }
+    }
+}
+
+void move()
+{
+    long l, *moveptr;
+    short j, a, goalang, angdif;
+    long daxvel;
+
+    a = g_sp->hitag;
+
+    if(a == -1) a = 0;
+
+    g_t[0]++;
+
+    if(a&face_player)
+    {
+        if(ps[g_p].newowner >= 0)
+            goalang = getangle(ps[g_p].oposx-g_sp->x,ps[g_p].oposy-g_sp->y);
+        else goalang = getangle(ps[g_p].posx-g_sp->x,ps[g_p].posy-g_sp->y);
+        angdif = getincangle(g_sp->ang,goalang)>>2;
+        if(angdif > -8 && angdif < 0) angdif = 0;
+        g_sp->ang += angdif;
+    }
+
+    if(a&spin)
+        g_sp->ang += sintable[ ((g_t[0]<<3)&2047) ]>>6;
+
+    if(a&face_player_slow)
+    {
+        if(ps[g_p].newowner >= 0)
+            goalang = getangle(ps[g_p].oposx-g_sp->x,ps[g_p].oposy-g_sp->y);
+        else goalang = getangle(ps[g_p].posx-g_sp->x,ps[g_p].posy-g_sp->y);
+        angdif = ksgn(getincangle(g_sp->ang,goalang))<<5;
+        if(angdif > -32 && angdif < 0)
+        {
+            angdif = 0;
+            g_sp->ang = goalang;
+        }
+        g_sp->ang += angdif;
+    }
+
+
+    if((a&jumptoplayer) == jumptoplayer)
+    {
+        if(g_t[0] < 16)
+            g_sp->zvel -= (sintable[(512+(g_t[0]<<4))&2047]>>5);
+    }
+
+    if(a&face_player_smart)
+    {
+        long newx,newy;
+
+        newx = ps[g_p].posx+(ps[g_p].posxv/768);
+        newy = ps[g_p].posy+(ps[g_p].posyv/768);
+        goalang = getangle(newx-g_sp->x,newy-g_sp->y);
+        angdif = getincangle(g_sp->ang,goalang)>>2;
+        if(angdif > -8 && angdif < 0) angdif = 0;
+        g_sp->ang += angdif;
+    }
+
+    if( g_t[1] == 0 || a == 0 )
+    {
+        if( ( badguy(g_sp) && g_sp->extra <= 0 ) || (hittype[g_i].bposx != g_sp->x) || (hittype[g_i].bposy != g_sp->y) )
+        {
+            hittype[g_i].bposx = g_sp->x;
+            hittype[g_i].bposy = g_sp->y;
+            setsprite(g_i,g_sp->x,g_sp->y,g_sp->z);
+        }
+        return;
+    }
+
+    moveptr = (long *)g_t[1];
+
+    if(a&geth) g_sp->xvel += (*moveptr-g_sp->xvel)>>1;
+    if(a&getv) g_sp->zvel += ((*(moveptr+1)<<4)-g_sp->zvel)>>1;
+
+    if(a&dodgebullet)
+        dodge(g_sp);
+
+    if(g_sp->picnum != APLAYER)
+        alterang(a);
+
+    if(g_sp->xvel > -6 && g_sp->xvel < 6 ) g_sp->xvel = 0;
+
+    a = badguy(g_sp);
+
+    if(g_sp->xvel || g_sp->zvel)
+    {
+        if(a && g_sp->picnum != ROTATEGUN)
+        {
+            if( (g_sp->picnum == DRONE || g_sp->picnum == COMMANDER) && g_sp->extra > 0)
+            {
+                if(g_sp->picnum == COMMANDER)
+                {
+                    hittype[g_i].floorz = l = getflorzofslope(g_sp->sectnum,g_sp->x,g_sp->y);
+                    if( g_sp->z > (l-(8<<8)) )
+                    {
+                        if( g_sp->z > (l-(8<<8)) ) g_sp->z = l-(8<<8);
+                        g_sp->zvel = 0;
+                    }
+
+                    hittype[g_i].ceilingz = l = getceilzofslope(g_sp->sectnum,g_sp->x,g_sp->y);
+                    if( (g_sp->z-l) < (80<<8) )
+                    {
+                        g_sp->z = l+(80<<8);
+                        g_sp->zvel = 0;
+                    }
+                }
+                else
+                {
+                    if( g_sp->zvel > 0 )
+                    {
+                        hittype[g_i].floorz = l = getflorzofslope(g_sp->sectnum,g_sp->x,g_sp->y);
+                        if( g_sp->z > (l-(30<<8)) )
+                            g_sp->z = l-(30<<8);
+                    }
+                    else
+                    {
+                        hittype[g_i].ceilingz = l = getceilzofslope(g_sp->sectnum,g_sp->x,g_sp->y);
+                        if( (g_sp->z-l) < (50<<8) )
+                        {
+                            g_sp->z = l+(50<<8);
+                            g_sp->zvel = 0;
+                        }
+                    }
+                }
+            }
+            else if(g_sp->picnum != ORGANTIC)
+            {
+                if(g_sp->zvel > 0 && hittype[g_i].floorz < g_sp->z)
+                    g_sp->z = hittype[g_i].floorz;
+                if( g_sp->zvel < 0)
+                {
+                    l = getceilzofslope(g_sp->sectnum,g_sp->x,g_sp->y);
+                    if( (g_sp->z-l) < (66<<8) )
+                    {
+                        g_sp->z = l+(66<<8);
+                        g_sp->zvel >>= 1;
+                    }
+                }
+            }
+        }
+        else if(g_sp->picnum == APLAYER)
+            if( (g_sp->z-hittype[g_i].ceilingz) < (32<<8) )
+                g_sp->z = hittype[g_i].ceilingz+(32<<8);
+
+        daxvel = g_sp->xvel;
+        angdif = g_sp->ang;
+
+        if( a && g_sp->picnum != ROTATEGUN )
+        {
+            if( g_x < 960 && g_sp->xrepeat > 16 )
+            {
+
+                daxvel = -(1024-g_x);
+                angdif = getangle(ps[g_p].posx-g_sp->x,ps[g_p].posy-g_sp->y);
+
+                if(g_x < 512)
+                {
+                    ps[g_p].posxv = 0;
+                    ps[g_p].posyv = 0;
+                }
+                else
+                {
+                    ps[g_p].posxv = mulscale(ps[g_p].posxv,dukefriction-0x2000,16);
+                    ps[g_p].posyv = mulscale(ps[g_p].posyv,dukefriction-0x2000,16);
+                }
+            }
+            else if(g_sp->picnum != DRONE && g_sp->picnum != SHARK && g_sp->picnum != COMMANDER)
+            {
+                if( hittype[g_i].bposz != g_sp->z || ( ud.multimode < 2 && ud.player_skill < 2 ) )
+                {
+                    if( (g_t[0]&1) || ps[g_p].actorsqu == g_i ) return;
+                    else daxvel <<= 1;
+                }
+                else
+                {
+                    if( (g_t[0]&3) || ps[g_p].actorsqu == g_i ) return;
+                    else daxvel <<= 2;
+                }
+            }
+        }
+
+        hittype[g_i].movflag = movesprite(g_i,
+            (daxvel*(sintable[(angdif+512)&2047]))>>14,
+            (daxvel*(sintable[angdif&2047]))>>14,g_sp->zvel,CLIPMASK0);
+   }
+
+   if( a )
+   {
+       if (sector[g_sp->sectnum].ceilingstat&1)
+           g_sp->shade += (sector[g_sp->sectnum].ceilingshade-g_sp->shade)>>1;
+       else g_sp->shade += (sector[g_sp->sectnum].floorshade-g_sp->shade)>>1;
+
+       if( sector[g_sp->sectnum].floorpicnum == MIRROR )
+           deletesprite(g_i);
+   }
+}
+
+char parse(void);
+
+void parseifelse(long condition)
+{
+    if( condition )
+    {
+        insptr+=2;
+        parse();
+    }
+    else
+    {
+        insptr = (long *) *(insptr+1);
+        if(*insptr == 10)
+        {
+            insptr+=2;
+            parse();
+        }
+    }
+}
+
+// long *it = 0x00589a04;
+
+char parse(void)
+{
+    long j, l, s;
+
+    if(killit_flag) return 1;
+
+//    if(*it == 1668249134L) gameexit("\nERR");
+
+    switch(*insptr)
+    {
+        case 3:
+            insptr++;
+            parseifelse( rnd(*insptr));
+            break;
+        case 45:
+
+            if(g_x > 1024)
+            {
+                short temphit, sclip, angdif;
+
+                if( badguy(g_sp) && g_sp->xrepeat > 56 )
+                {
+                    sclip = 3084;
+                    angdif = 48;
+                }
+                else
+                {
+                    sclip = 768;
+                    angdif = 16;
+                }
+
+                j = hitasprite(g_i,&temphit);
+                if(j == (1<<30))
+                {
+                    parseifelse(1);
+                    break;
+                }
+                if(j > sclip)
+                {
+                    if(temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
+                        j = 0;
+                    else
+                    {
+                        g_sp->ang += angdif;j = hitasprite(g_i,&temphit);g_sp->ang -= angdif;
+                        if(j > sclip)
+                        {
+                            if(temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
+                                j = 0;
+                            else
+                            {
+                                g_sp->ang -= angdif;j = hitasprite(g_i,&temphit);g_sp->ang += angdif;
+                                if( j > 768 )
+                                {
+                                    if(temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
+                                        j = 0;
+                                    else j = 1;
+                                }
+                                else j = 0;
+                            }
+                        }
+                        else j = 0;
+                    }
+                }
+                else j =  0;
+            }
+            else j = 1;
+
+            parseifelse(j);
+            break;
+        case 91:
+            j = cansee(g_sp->x,g_sp->y,g_sp->z-((TRAND&41)<<8),g_sp->sectnum,ps[g_p].posx,ps[g_p].posy,ps[g_p].posz/*-((TRAND&41)<<8)*/,sprite[ps[g_p].i].sectnum);
+            parseifelse(j);
+            if( j ) hittype[g_i].timetosleep = SLEEPTIME;
+            break;
+
+        case 49:
+            parseifelse(hittype[g_i].actorstayput == -1);
+            break;
+        case 5:
+        {
+            spritetype *s;
+            short sect;
+
+            if(ps[g_p].holoduke_on >= 0)
+            {
+                s = &sprite[ps[g_p].holoduke_on];
+                j = cansee(g_sp->x,g_sp->y,g_sp->z-(TRAND&((32<<8)-1)),g_sp->sectnum,
+                       s->x,s->y,s->z,s->sectnum);
+                if(j == 0)
+                    s = &sprite[ps[g_p].i];
+            }
+            else s = &sprite[ps[g_p].i];
+
+            j = cansee(g_sp->x,g_sp->y,g_sp->z-(TRAND&((47<<8))),g_sp->sectnum,
+                s->x,s->y,s->z-(24<<8),s->sectnum);
+
+            if(j == 0)
+            {
+                if( ( klabs(hittype[g_i].lastvx-g_sp->x)+klabs(hittype[g_i].lastvy-g_sp->y) ) <
+                    ( klabs(hittype[g_i].lastvx-s->x)+klabs(hittype[g_i].lastvy-s->y) ) )
+                        j = 0;
+
+                if( j == 0 )
+                {
+                    j = furthestcanseepoint(g_i,s,&hittype[g_i].lastvx,&hittype[g_i].lastvy);
+
+                    if(j == -1) j = 0;
+                    else j = 1;
+                }
+            }
+            else
+            {
+                hittype[g_i].lastvx = s->x;
+                hittype[g_i].lastvy = s->y;
+            }
+
+            if( j == 1 && ( g_sp->statnum == 1 || g_sp->statnum == 6 ) )
+                hittype[g_i].timetosleep = SLEEPTIME;
+
+            parseifelse(j == 1);
+            break;
+        }
+
+        case 6:
+            parseifelse(ifhitbyweapon(g_i) >= 0);
+            break;
+        case 27:
+            parseifelse( ifsquished(g_i, g_p) == 1);
+            break;
+        case 26:
+            {
+                j = g_sp->extra;
+                if(g_sp->picnum == APLAYER)
+                    j--;
+                parseifelse(j < 0);
+            }
+            break;
+        case 24:
+            insptr++;
+            g_t[5] = *insptr;
+            g_t[4] = *(long *)(g_t[5]);       // Action
+            g_t[1] = *(long *)(g_t[5]+4);       // move
+            g_sp->hitag = *(long *)(g_t[5]+8);    // Ai
+            g_t[0] = g_t[2] = g_t[3] = 0;
+            if(g_sp->hitag&random_angle)
+                g_sp->ang = TRAND&2047;
+            insptr++;
+            break;
+        case 7:
+            insptr++;
+            g_t[2] = 0;
+            g_t[3] = 0;
+			// FIX_00093: fixed crashbugs in multiplayer (mine/blimp)
+			// This is the blimp bug.
+			// *.con code 1.3 and 1.4 are buggy when you try to blow up the 
+			// blimp in multiplayer. duke3d_w32 /q2 /m /v3 /l9
+			// This is because the con code gives a timeout value of 2048 
+			// as a action address instead of giving a real action address.
+			// We simply counter this specific con code bug by resetting 
+			// the action address to 0 when we get an address "2048":
+			g_t[4] = ((*insptr)==2048)?0:(*insptr);
+            insptr++;
+            break;
+
+        case 8:
+            insptr++;
+            parseifelse(g_x < *insptr);
+            if(g_x > MAXSLEEPDIST && hittype[g_i].timetosleep == 0)
+                hittype[g_i].timetosleep = SLEEPTIME;
+            break;
+        case 9:
+            insptr++;
+            parseifelse(g_x > *insptr);
+            if(g_x > MAXSLEEPDIST && hittype[g_i].timetosleep == 0)
+                hittype[g_i].timetosleep = SLEEPTIME;
+            break;
+        case 10:
+            insptr = (long *) *(insptr+1);
+            break;
+        case 100:
+            insptr++;
+            g_sp->extra += *insptr;
+            insptr++;
+            break;
+        case 11:
+            insptr++;
+            g_sp->extra = *insptr;
+            insptr++;
+            break;
+        case 94:
+            insptr++;
+
+            if(ud.coop >= 1 && ud.multimode > 1)
+            {
+                if(*insptr == 0)
+                {
+                    for(j=0;j < ps[g_p].weapreccnt;j++)
+                        if( ps[g_p].weaprecs[j] == g_sp->picnum )
+                            break;
+
+                    parseifelse(j < ps[g_p].weapreccnt && g_sp->owner == g_i);
+                }
+                else if(ps[g_p].weapreccnt < 16)
+                {
+                    ps[g_p].weaprecs[ps[g_p].weapreccnt++] = g_sp->picnum;
+                    parseifelse(g_sp->owner == g_i);
+                }
+            }
+            else parseifelse(0);
+            break;
+        case 95:
+            insptr++;
+            if(g_sp->picnum == APLAYER)
+                g_sp->pal = ps[g_sp->yvel].palookup;
+            else g_sp->pal = hittype[g_i].tempang;
+            hittype[g_i].tempang = 0;
+            break;
+        case 104:
+            insptr++;
+            checkweapons(&ps[g_sp->yvel]);
+            break;
+        case 106:
+            insptr++;
+            break;
+        case 97:
+            insptr++;
+            if(Sound[g_sp->yvel].num == 0)
+                spritesound(g_sp->yvel,g_i);
+            break;
+        case 96:
+            insptr++;
+
+            if( ud.multimode > 1 && g_sp->picnum == APLAYER )
+            {
+                if(ps[otherp].quick_kick == 0)
+                    ps[otherp].quick_kick = 14;
+            }
+            else if(g_sp->picnum != APLAYER && ps[g_p].quick_kick == 0)
+                ps[g_p].quick_kick = 14;
+            break;
+        case 28:
+            insptr++;
+
+            j = ((*insptr)-g_sp->xrepeat)<<1;
+            g_sp->xrepeat += ksgn(j);
+
+            insptr++;
+
+            if( ( g_sp->picnum == APLAYER && g_sp->yrepeat < 36 ) || *insptr < g_sp->yrepeat || ((g_sp->yrepeat*(tilesizy[g_sp->picnum]+8))<<2) < (hittype[g_i].floorz - hittype[g_i].ceilingz) )
+            {
+                j = ((*insptr)-g_sp->yrepeat)<<1;
+                if( klabs(j) ) g_sp->yrepeat += ksgn(j);
+            }
+
+            insptr++;
+
+            break;
+        case 99:
+            insptr++;
+            g_sp->xrepeat = (char) *insptr;
+            insptr++;
+            g_sp->yrepeat = (char) *insptr;
+            insptr++;
+            break;
+        case 13:
+            insptr++;
+            shoot(g_i,(short)*insptr);
+            insptr++;
+            break;
+        case 87:
+            insptr++;
+            if( Sound[*insptr].num == 0 )
+                spritesound((short) *insptr,g_i);
+            insptr++;
+            break;
+        case 89:
+            insptr++;
+            if( Sound[*insptr].num > 0 )
+                stopsound((short)*insptr);
+            insptr++;
+            break;
+        case 92:
+            insptr++;
+            if(g_p == screenpeek || ud.coop==1)
+                spritesound((short) *insptr,ps[screenpeek].i);
+            insptr++;
+            break;
+        case 15:
+            insptr++;
+            spritesound((short) *insptr,g_i);
+            insptr++;
+            break;
+        case 84:
+            insptr++;
+            ps[g_p].tipincs = 26;
+            break;
+        case 16:
+            insptr++;
+            g_sp->xoffset = 0;
+            g_sp->yoffset = 0;
+//            if(!gotz)
+            {
+                long c;
+
+                if( floorspace(g_sp->sectnum) )
+                    c = 0;
+                else
+                {
+                    if( ceilingspace(g_sp->sectnum) || sector[g_sp->sectnum].lotag == 2)
+                        c = gc/6;
+                    else c = gc;
+                }
+
+                if( hittype[g_i].cgg <= 0 || (sector[g_sp->sectnum].floorstat&2) )
+                {
+                    getglobalz(g_i);
+                    hittype[g_i].cgg = 6;
+                }
+                else hittype[g_i].cgg --;
+
+                if( g_sp->z < (hittype[g_i].floorz-FOURSLEIGHT) )
+                {
+                    g_sp->zvel += c;
+                    g_sp->z+=g_sp->zvel;
+
+                    if(g_sp->zvel > 6144) g_sp->zvel = 6144;
+                }
+                else
+                {
+                    g_sp->z = hittype[g_i].floorz - FOURSLEIGHT;
+
+                    if( badguy(g_sp) || ( g_sp->picnum == APLAYER && g_sp->owner >= 0) )
+                    {
+
+                        if( g_sp->zvel > 3084 && g_sp->extra <= 1)
+                        {
+                            if(g_sp->pal != 1 && g_sp->picnum != DRONE)
+                            {
+                                if(g_sp->picnum == APLAYER && g_sp->extra > 0)
+                                    goto SKIPJIBS;
+                                guts(g_sp,JIBS6,15,g_p);
+                                spritesound(SQUISHED,g_i);
+                                spawn(g_i,BLOODPOOL);
+                            }
+
+                            SKIPJIBS:
+
+                            hittype[g_i].picnum = SHOTSPARK1;
+                            hittype[g_i].extra = 1;
+                            g_sp->zvel = 0;
+                        }
+                        else if(g_sp->zvel > 2048 && sector[g_sp->sectnum].lotag != 1)
+                        {
+
+                            j = g_sp->sectnum;
+                            pushmove(&g_sp->x,&g_sp->y,&g_sp->z,(short*)&j,128L,(4L<<8),(4L<<8),CLIPMASK0);
+                            if(j != g_sp->sectnum && j >= 0 && j < MAXSECTORS)
+                                changespritesect(g_i,j);
+
+                            spritesound(THUD,g_i);
+                        }
+                    }
+                    if(sector[g_sp->sectnum].lotag == 1)
+                        switch (g_sp->picnum)
+                        {
+                            case OCTABRAIN:
+                            case COMMANDER:
+                            case DRONE:
+                                break;
+                            default:
+                                g_sp->z += (24<<8);
+                                break;
+                        }
+                    else g_sp->zvel = 0;
+                }
+            }
+
+            break;
+        case 4:
+        case 12:
+        case 18:
+            return 1;
+        case 30:
+            insptr++;
+            return 1;
+        case 2:
+            insptr++;
+            if( ps[g_p].ammo_amount[*insptr] >= max_ammo_amount[*insptr] )
+            {
+                killit_flag = 2;
+                break;
+            }
+            addammo( *insptr, &ps[g_p], *(insptr+1) );
+            if(ps[g_p].curr_weapon == KNEE_WEAPON)
+                if( ps[g_p].gotweapon[*insptr] )
+                    addweapon( &ps[g_p], *insptr );
+            insptr += 2;
+            break;
+        case 86:
+            insptr++;
+            lotsofmoney(g_sp,*insptr);
+            insptr++;
+            break;
+        case 102:
+            insptr++;
+            lotsofmail(g_sp,*insptr);
+            insptr++;
+            break;
+        case 105:
+            insptr++;
+            hittype[g_i].timetosleep = (short)*insptr;
+            insptr++;
+            break;
+        case 103:
+            insptr++;
+            lotsofpaper(g_sp,*insptr);
+            insptr++;
+            break;
+        case 88:
+            insptr++;
+            ps[g_p].actors_killed += *insptr;
+            hittype[g_i].actorstayput = -1;
+            insptr++;
+            break;
+        case 93:
+            insptr++;
+            spriteglass(g_i,*insptr);
+            insptr++;
+            break;
+        case 22:
+            insptr++;
+            killit_flag = 1;
+            break;
+        case 23:
+            insptr++;
+            if( ps[g_p].gotweapon[*insptr] == 0 ) addweapon( &ps[g_p], *insptr );
+            else if( ps[g_p].ammo_amount[*insptr] >= max_ammo_amount[*insptr] )
+            {
+                 killit_flag = 2;
+                 break;
+            }
+            addammo( *insptr, &ps[g_p], *(insptr+1) );
+            if(ps[g_p].curr_weapon == KNEE_WEAPON)
+                if( ps[g_p].gotweapon[*insptr] )
+                    addweapon( &ps[g_p], *insptr );
+            insptr+=2;
+            break;
+        case 68:
+            insptr++;
+            printf("%ld\n",*insptr);
+            insptr++;
+            break;
+        case 69:
+            insptr++;
+            ps[g_p].timebeforeexit = *insptr;
+            ps[g_p].customexitsound = -1;
+            ud.eog = 1;
+            insptr++;
+            break;
+        case 25:
+            insptr++;
+
+            if(ps[g_p].newowner >= 0)
+            {
+                ps[g_p].newowner = -1;
+                ps[g_p].posx = ps[g_p].oposx;
+                ps[g_p].posy = ps[g_p].oposy;
+                ps[g_p].posz = ps[g_p].oposz;
+                ps[g_p].ang = ps[g_p].oang;
+                updatesector(ps[g_p].posx,ps[g_p].posy,&ps[g_p].cursectnum);
+                setpal(&ps[g_p]);
+
+                j = headspritestat[1];
+                while(j >= 0)
+                {
+                    if(sprite[j].picnum==CAMERA1)
+                        sprite[j].yvel = 0;
+                    j = nextspritestat[j];
+                }
+            }
+
+            j = sprite[ps[g_p].i].extra;
+
+            if(g_sp->picnum != ATOMICHEALTH)
+            {
+                if( j > max_player_health && *insptr > 0 )
+                {
+                    insptr++;
+                    break;
+                }
+                else
+                {
+                    if(j > 0)
+                        j += *insptr;
+                    if ( j > max_player_health && *insptr > 0 )
+                        j = max_player_health;
+                }
+            }
+            else
+            {
+                if( j > 0 )
+                    j += *insptr;
+                if ( j > (max_player_health<<1) )
+                    j = (max_player_health<<1);
+            }
+
+            if(j < 0) j = 0;
+
+            if(ud.god == 0)
+            {
+                if(*insptr > 0)
+                {
+                    if( ( j - *insptr ) < (max_player_health>>2) &&
+                        j >= (max_player_health>>2) )
+                            spritesound(DUKE_GOTHEALTHATLOW,ps[g_p].i);
+
+                    ps[g_p].last_extra = j;
+                }
+
+                sprite[ps[g_p].i].extra = j;
+            }
+
+            insptr++;
+            break;
+        case 17:
+            {
+                long *tempscrptr;
+
+                tempscrptr = insptr+2;
+
+                insptr = (long *) *(insptr+1);
+                while(1) if(parse()) break;
+                insptr = tempscrptr;
+            }
+            break;
+        case 29:
+            insptr++;
+            while(1) if(parse()) break;
+            break;
+        case 32:
+            g_t[0]=0;
+            insptr++;
+            g_t[1] = *insptr;
+            insptr++;
+            g_sp->hitag = *insptr;
+            insptr++;
+            if(g_sp->hitag&random_angle)
+                g_sp->ang = TRAND&2047;
+            break;
+        case 31:
+            insptr++;
+            if(g_sp->sectnum >= 0 && g_sp->sectnum < MAXSECTORS)
+                spawn(g_i,*insptr);
+            insptr++;
+            break;
+        case 33:
+            insptr++;
+            parseifelse( hittype[g_i].picnum == *insptr);
+            break;
+        case 21:
+            insptr++;
+            parseifelse(g_t[5] == *insptr);
+            break;
+        case 34:
+            insptr++;
+            parseifelse(g_t[4] == *insptr);
+            break;
+        case 35:
+            insptr++;
+            parseifelse(g_t[2] >= *insptr);
+            break;
+        case 36:
+            insptr++;
+            g_t[2] = 0;
+            break;
+        case 37:
+            {
+                short dnum;
+
+                insptr++;
+                dnum = *insptr;
+                insptr++;
+
+                if(g_sp->sectnum >= 0 && g_sp->sectnum < MAXSECTORS)
+                    for(j=(*insptr)-1;j>=0;j--)
+                {
+                    if(g_sp->picnum == BLIMP && dnum == SCRAP1)
+                        s = 0;
+                    else s = (TRAND%3);
+
+                    l = EGS(g_sp->sectnum,
+                            g_sp->x+(TRAND&255)-128,g_sp->y+(TRAND&255)-128,g_sp->z-(8<<8)-(TRAND&8191),
+                            dnum+s,g_sp->shade,32+(TRAND&15),32+(TRAND&15),
+                            TRAND&2047,(TRAND&127)+32,
+                            -(TRAND&2047),g_i,5);
+                    if(g_sp->picnum == BLIMP && dnum == SCRAP1)
+                        sprite[l].yvel = weaponsandammosprites[j%14];
+                    else sprite[l].yvel = -1;
+                    sprite[l].pal = g_sp->pal;
+                }
+                insptr++;
+            }
+            break;
+        case 52:
+            insptr++;
+            g_t[0] = (short) *insptr;
+            insptr++;
+            break;
+        case 101:
+            insptr++;
+            g_sp->cstat |= (short)*insptr;
+            insptr++;
+            break;
+        case 110:
+            insptr++;
+            g_sp->clipdist = (short) *insptr;
+            insptr++;
+            break;
+        case 40:
+            insptr++;
+            g_sp->cstat = (short) *insptr;
+            insptr++;
+            break;
+        case 41:
+            insptr++;
+            parseifelse(g_t[1] == *insptr);
+            break;
+        case 42:
+            insptr++;
+
+            if(ud.multimode < 2)
+            {
+                if( lastsavedpos >= 0 && ud.recstat != 2 )
+                {
+                    ps[g_p].gm = MODE_MENU;
+                    KB_ClearKeyDown(sc_Space);
+                    cmenu(15000);
+                }
+                else ps[g_p].gm = MODE_RESTART;
+                killit_flag = 2;
+            }
+            else
+            {
+                pickrandomspot(g_p);
+                g_sp->x = hittype[g_i].bposx = ps[g_p].bobposx = ps[g_p].oposx = ps[g_p].posx;
+                g_sp->y = hittype[g_i].bposy = ps[g_p].bobposy = ps[g_p].oposy =ps[g_p].posy;
+                g_sp->z = hittype[g_i].bposy = ps[g_p].oposz =ps[g_p].posz;
+                updatesector(ps[g_p].posx,ps[g_p].posy,&ps[g_p].cursectnum);
+                setsprite(ps[g_p].i,ps[g_p].posx,ps[g_p].posy,ps[g_p].posz+PHEIGHT);
+                g_sp->cstat = 257;
+
+                g_sp->shade = -12;
+                g_sp->clipdist = 64;
+                g_sp->xrepeat = 42;
+                g_sp->yrepeat = 36;
+                g_sp->owner = g_i;
+                g_sp->xoffset = 0;
+                g_sp->pal = ps[g_p].palookup;
+
+                ps[g_p].last_extra = g_sp->extra = max_player_health;
+                ps[g_p].wantweaponfire = -1;
+                ps[g_p].horiz = 100;
+                ps[g_p].on_crane = -1;
+                ps[g_p].frag_ps = g_p;
+                ps[g_p].horizoff = 0;
+                ps[g_p].opyoff = 0;
+                ps[g_p].wackedbyactor = -1;
+                ps[g_p].shield_amount = max_armour_amount;
+                ps[g_p].dead_flag = 0;
+                ps[g_p].pals_time = 0;
+                ps[g_p].footprintcount = 0;
+                ps[g_p].weapreccnt = 0;
+                ps[g_p].fta = 0;
+                ps[g_p].ftq = 0;
+                ps[g_p].posxv = ps[g_p].posyv = 0;
+                ps[g_p].rotscrnang = 0;
+
+                ps[g_p].falling_counter = 0;
+
+                hittype[g_i].extra = -1;
+                hittype[g_i].owner = g_i;
+
+                hittype[g_i].cgg = 0;
+                hittype[g_i].movflag = 0;
+                hittype[g_i].tempang = 0;
+                hittype[g_i].actorstayput = -1;
+                hittype[g_i].dispicnum = 0;
+                hittype[g_i].owner = ps[g_p].i;
+
+                resetinventory(g_p);
+                resetweapons(g_p);
+
+                cameradist = 0;
+                cameraclock = totalclock;
+            }
+            setpal(&ps[g_p]);
+
+            break;
+        case 43:
+            parseifelse( klabs(g_sp->z-sector[g_sp->sectnum].floorz) < (32<<8) && sector[g_sp->sectnum].lotag == 1);
+            break;
+        case 44:
+            parseifelse( sector[g_sp->sectnum].lotag == 2);
+            break;
+        case 46:
+            insptr++;
+            parseifelse(g_t[0] >= *insptr);
+            break;
+        case 53:
+            insptr++;
+            parseifelse(g_sp->picnum == *insptr);
+            break;
+        case 47:
+            insptr++;
+            g_t[0] = 0;
+            break;
+        case 48:
+            insptr+=2;
+            switch(*(insptr-1))
+            {
+                case 0:
+                    ps[g_p].steroids_amount = *insptr;
+                    ps[g_p].inven_icon = 2;
+                    break;
+                case 1:
+                    ps[g_p].shield_amount +=          *insptr;// 100;
+                    if(ps[g_p].shield_amount > max_player_health)
+                        ps[g_p].shield_amount = max_player_health;
+                    break;
+                case 2:
+                    ps[g_p].scuba_amount =             *insptr;// 1600;
+                    ps[g_p].inven_icon = 6;
+                    break;
+                case 3:
+                    ps[g_p].holoduke_amount =          *insptr;// 1600;
+                    ps[g_p].inven_icon = 3;
+                    break;
+                case 4:
+                    ps[g_p].jetpack_amount =           *insptr;// 1600;
+                    ps[g_p].inven_icon = 4;
+                    break;
+                case 6:
+                    switch(g_sp->pal)
+                    {
+                        case  0: ps[g_p].got_access |= 1;break;
+                        case 21: ps[g_p].got_access |= 2;break;
+                        case 23: ps[g_p].got_access |= 4;break;
+                    }
+                    break;
+                case 7:
+                    ps[g_p].heat_amount = *insptr;
+                    ps[g_p].inven_icon = 5;
+                    break;
+                case 9:
+                    ps[g_p].inven_icon = 1;
+                    ps[g_p].firstaid_amount = *insptr;
+                    break;
+                case 10:
+                    ps[g_p].inven_icon = 7;
+                    ps[g_p].boot_amount = *insptr;
+                    break;
+            }
+            insptr++;
+            break;
+        case 50:
+            hitradius(g_i,*(insptr+1),*(insptr+2),*(insptr+3),*(insptr+4),*(insptr+5));
+            insptr+=6;
+            break;
+        case 51:
+            {
+                insptr++;
+
+                l = *insptr;
+                j = 0;
+
+                s = g_sp->xvel;
+
+                if( (l&8) && ps[g_p].on_ground && (sync[g_p].bits&2) )
+                       j = 1;
+                else if( (l&16) && ps[g_p].jumping_counter == 0 && !ps[g_p].on_ground &&
+                    ps[g_p].poszv > 2048 )
+                        j = 1;
+                else if( (l&32) && ps[g_p].jumping_counter > 348 )
+                       j = 1;
+                else if( (l&1) && s >= 0 && s < 8)
+                       j = 1;
+                else if( (l&2) && s >= 8 && !(sync[g_p].bits&(1<<5)) )
+                       j = 1;
+                else if( (l&4) && s >= 8 && sync[g_p].bits&(1<<5) )
+                       j = 1;
+                else if( (l&64) && ps[g_p].posz < (g_sp->z-(48<<8)) )
+                       j = 1;
+                else if( (l&128) && s <= -8 && !(sync[g_p].bits&(1<<5)) )
+                       j = 1;
+                else if( (l&256) && s <= -8 && (sync[g_p].bits&(1<<5)) )
+                       j = 1;
+                else if( (l&512) && ( ps[g_p].quick_kick > 0 || ( ps[g_p].curr_weapon == KNEE_WEAPON && ps[g_p].kickback_pic > 0 ) ) )
+                       j = 1;
+                else if( (l&1024) && sprite[ps[g_p].i].xrepeat < 32 )
+                       j = 1;
+                else if( (l&2048) && ps[g_p].jetpack_on )
+                       j = 1;
+                else if( (l&4096) && ps[g_p].steroids_amount > 0 && ps[g_p].steroids_amount < 400 )
+                       j = 1;
+                else if( (l&8192) && ps[g_p].on_ground)
+                       j = 1;
+                else if( (l&16384) && sprite[ps[g_p].i].xrepeat > 32 && sprite[ps[g_p].i].extra > 0 && ps[g_p].timebeforeexit == 0 )
+                       j = 1;
+                else if( (l&32768) && sprite[ps[g_p].i].extra <= 0)
+                       j = 1;
+                else if( (l&65536L) )
+                {
+                    if(g_sp->picnum == APLAYER && ud.multimode > 1)
+                        j = getincangle(ps[otherp].ang,getangle(ps[g_p].posx-ps[otherp].posx,ps[g_p].posy-ps[otherp].posy));
+                    else
+                        j = getincangle(ps[g_p].ang,getangle(g_sp->x-ps[g_p].posx,g_sp->y-ps[g_p].posy));
+
+                    if( j > -128 && j < 128 )
+                        j = 1;
+                    else
+                        j = 0;
+                }
+
+                parseifelse((long) j);
+
+            }
+            break;
+        case 56:
+            insptr++;
+            parseifelse(g_sp->extra <= *insptr);
+            break;
+        case 58:
+            insptr += 2;
+            guts(g_sp,*(insptr-1),*insptr,g_p);
+            insptr++;
+            break;
+        case 59:
+            insptr++;
+//            if(g_sp->owner >= 0 && sprite[g_sp->owner].picnum == *insptr)
+  //              parseifelse(1);
+//            else
+            parseifelse( hittype[g_i].picnum == *insptr);
+            break;
+        case 61:
+            insptr++;
+            forceplayerangle(&ps[g_p]);
+            return 0;
+        case 62:
+            insptr++;
+            parseifelse( (( hittype[g_i].floorz - hittype[g_i].ceilingz ) >> 8 ) < *insptr);
+            break;
+        case 63:
+            parseifelse( sync[g_p].bits&(1<<29));
+            break;
+        case 64:
+            parseifelse(sector[g_sp->sectnum].ceilingstat&1);
+            break;
+        case 65:
+            parseifelse(ud.multimode > 1);
+            break;
+        case 66:
+            insptr++;
+            if( sector[g_sp->sectnum].lotag == 0 )
+            {
+                neartag(g_sp->x,g_sp->y,g_sp->z-(32<<8),g_sp->sectnum,g_sp->ang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,768L,1);
+                if( neartagsector >= 0 && isanearoperator(sector[neartagsector].lotag) )
+                    if( (sector[neartagsector].lotag&0xff) == 23 || sector[neartagsector].floorz == sector[neartagsector].ceilingz )
+                        if( (sector[neartagsector].lotag&16384) == 0 )
+                            if( (sector[neartagsector].lotag&32768) == 0 )
+                        {
+                            j = headspritesect[neartagsector];
+                            while(j >= 0)
+                            {
+                                if(sprite[j].picnum == ACTIVATOR)
+                                    break;
+                                j = nextspritesect[j];
+                            }
+                            if(j == -1)
+                                operatesectors(neartagsector,g_i);
+                        }
+            }
+            break;
+        case 67:
+            parseifelse(ceilingspace(g_sp->sectnum));
+            break;
+
+        case 74:
+            insptr++;
+            if(g_sp->picnum != APLAYER)
+                hittype[g_i].tempang = g_sp->pal;
+            g_sp->pal = *insptr;
+            insptr++;
+            break;
+
+        case 77:
+            insptr++;
+            g_sp->picnum = *insptr;
+            insptr++;
+            break;
+
+        case 70:
+            parseifelse( dodge(g_sp) == 1);
+            break;
+        case 71:
+            if( badguy(g_sp) )
+                parseifelse( ud.respawn_monsters );
+            else if( inventory(g_sp) )
+                parseifelse( ud.respawn_inventory );
+            else
+                parseifelse( ud.respawn_items );
+            break;
+        case 72:
+            insptr++;
+//            getglobalz(g_i);
+            parseifelse( (hittype[g_i].floorz - g_sp->z) <= ((*insptr)<<8));
+            break;
+        case 73:
+            insptr++;
+//            getglobalz(g_i);
+            parseifelse( ( g_sp->z - hittype[g_i].ceilingz ) <= ((*insptr)<<8));
+            break;
+        case 14:
+
+            insptr++;
+            ps[g_p].pals_time = *insptr;
+            insptr++;
+            for(j=0;j<3;j++)
+            {
+                ps[g_p].pals[j] = *insptr;
+                insptr++;
+            }
+            break;
+
+/*        case 74:
+            insptr++;
+            getglobalz(g_i);
+            parseifelse( (( hittype[g_i].floorz - hittype[g_i].ceilingz ) >> 8 ) >= *insptr);
+            break;
+*/
+        case 78:
+            insptr++;
+            parseifelse( sprite[ps[g_p].i].extra < *insptr);
+            break;
+
+        case 75:
+            {
+                insptr++;
+                j = 0;
+                switch(*(insptr++))
+                {
+                    case 0:if( ps[g_p].steroids_amount != *insptr)
+                           j = 1;
+                        break;
+                    case 1:if(ps[g_p].shield_amount != max_player_health )
+                            j = 1;
+                        break;
+                    case 2:if(ps[g_p].scuba_amount != *insptr) j = 1;break;
+                    case 3:if(ps[g_p].holoduke_amount != *insptr) j = 1;break;
+                    case 4:if(ps[g_p].jetpack_amount != *insptr) j = 1;break;
+                    case 6:
+                        switch(g_sp->pal)
+                        {
+                            case  0: if(ps[g_p].got_access&1) j = 1;break;
+                            case 21: if(ps[g_p].got_access&2) j = 1;break;
+                            case 23: if(ps[g_p].got_access&4) j = 1;break;
+                        }
+                        break;
+                    case 7:if(ps[g_p].heat_amount != *insptr) j = 1;break;
+                    case 9:
+                        if(ps[g_p].firstaid_amount != *insptr) j = 1;break;
+                    case 10:
+                        if(ps[g_p].boot_amount != *insptr) j = 1;break;
+                }
+
+                parseifelse(j);
+                break;
+            }
+        case 38:
+            insptr++;
+            if( ps[g_p].knee_incs == 0 && sprite[ps[g_p].i].xrepeat >= 40 )
+                if( cansee(g_sp->x,g_sp->y,g_sp->z-(4<<8),g_sp->sectnum,ps[g_p].posx,ps[g_p].posy,ps[g_p].posz+(16<<8),sprite[ps[g_p].i].sectnum) )
+            {
+                ps[g_p].knee_incs = 1;
+                if(ps[g_p].weapon_pos == 0)
+                    ps[g_p].weapon_pos = -1;
+                ps[g_p].actorsqu = g_i;
+            }
+            break;
+        case 90:
+            {
+                short s1;
+
+                s1 = g_sp->sectnum;
+
+                j = 0;
+
+                    updatesector(g_sp->x+108,g_sp->y+108,&s1);
+                    if( s1 == g_sp->sectnum )
+                    {
+                        updatesector(g_sp->x-108,g_sp->y-108,&s1);
+                        if( s1 == g_sp->sectnum )
+                        {
+                            updatesector(g_sp->x+108,g_sp->y-108,&s1);
+                            if( s1 == g_sp->sectnum )
+                            {
+                                updatesector(g_sp->x-108,g_sp->y+108,&s1);
+                                if( s1 == g_sp->sectnum )
+                                    j = 1;
+                            }
+                        }
+                    }
+                    parseifelse( j );
+            }
+
+            break;
+        case 80:
+            insptr++;
+            FTA(*insptr,&ps[g_p],0);
+            insptr++;
+            break;
+        case 81:
+            parseifelse( floorspace(g_sp->sectnum));
+            break;
+        case 82:
+            parseifelse( (hittype[g_i].movflag&49152) > 16384 );
+            break;
+        case 83:
+            insptr++;
+            switch(g_sp->picnum)
+            {
+                case FEM1:
+                case FEM2:
+                case FEM3:
+                case FEM4:
+                case FEM5:
+                case FEM6:
+                case FEM7:
+                case FEM8:
+                case FEM9:
+                case FEM10:
+                case PODFEM1:
+                case NAKED1:
+                case STATUE:
+                    if(g_sp->yvel) operaterespawns(g_sp->yvel);
+                    break;
+                default:
+                    if(g_sp->hitag >= 0) operaterespawns(g_sp->hitag);
+                    break;
+            }
+            break;
+        case 85:
+            insptr++;
+            parseifelse( g_sp->pal == *insptr);
+            break;
+
+        case 111:
+            insptr++;
+            j = klabs(getincangle(ps[g_p].ang,g_sp->ang));
+            parseifelse( j <= *insptr);
+            break;
+
+        case 109:
+
+            for(j=1;j<NUM_SOUNDS;j++)
+                if( SoundOwner[j][0].i == g_i )
+                    break;
+
+            parseifelse( j == NUM_SOUNDS );
+            break;
+        default:
+            killit_flag = 1;
+            break;
+    }
+    return 0;
+}
+
+void execute(short i,short p,long x)
+{
+    char done;
+
+    g_i = i;
+    g_p = p;
+    g_x = x;
+    g_sp = &sprite[g_i];
+    g_t = &hittype[g_i].temp_data[0];
+
+    if( actorscrptr[g_sp->picnum] == 0 ) return;
+
+    insptr = 4 + (actorscrptr[g_sp->picnum]);
+
+    killit_flag = 0;
+
+    if(g_sp->sectnum < 0 || g_sp->sectnum >= MAXSECTORS)
+    {
+        if(badguy(g_sp))
+            ps[g_p].actors_killed++;
+        deletesprite(g_i);
+        return;
+    }
+
+
+    if(g_t[4])
+    {
+        g_sp->lotag += TICSPERFRAME;
+        if(g_sp->lotag > *(long *)(g_t[4]+16) )
+        {
+            g_t[2]++;
+            g_sp->lotag = 0;
+            g_t[3] +=  *(long *)( g_t[4]+12 );
+        }
+        if( klabs(g_t[3]) >= klabs( *(long *)(g_t[4]+4) * *(long *)(g_t[4]+12) ) )
+            g_t[3] = 0;
+    }
+
+    do
+        done = parse();
+    while( done == 0 );
+
+    if(killit_flag == 1)
+    {
+        if(ps[g_p].actorsqu == g_i)
+            ps[g_p].actorsqu = -1;
+        deletesprite(g_i);
+    }
+    else
+    {
+        move();
+
+        if( g_sp->statnum == 1)
+        {
+            if( badguy(g_sp) )
+            {
+                if( g_sp->xrepeat > 60 ) return;
+                if( ud.respawn_monsters == 1 && g_sp->extra <= 0 ) return;
+            }
+            else if( ud.respawn_items == 1 && (g_sp->cstat&32768) ) return;
+
+            if(hittype[g_i].timetosleep > 1)
+                hittype[g_i].timetosleep--;
+            else if(hittype[g_i].timetosleep == 1)
+                 changespritestat(g_i,2);
+        }
+
+        else if(g_sp->statnum == 6)
+            switch(g_sp->picnum)
+            {
+                case RUBBERCAN:
+                case EXPLODINGBARREL:
+                case WOODENHORSE:
+                case HORSEONSIDE:
+                case CANWITHSOMETHING:
+                case FIREBARREL:
+                case NUKEBARREL:
+                case NUKEBARRELDENTED:
+                case NUKEBARRELLEAKED:
+                case TRIPBOMB:
+                case EGG:
+                    if(hittype[g_i].timetosleep > 1)
+                        hittype[g_i].timetosleep--;
+                    else if(hittype[g_i].timetosleep == 1)
+                        changespritestat(g_i,2);
+                    break;
+            }
+    }
+}
+
+
+
+
+
+// "Duke 2000"
+// "Virchua Duke"
+// "Son of Death
+// "Cromium"
+// "Potent"
+// "Flotsom"
+
+// Volume One
+// "Duke is brain dead",
+// "BOOT TO THE HEAD"
+// Damage too duke
+// Weapons are computer cont.  Only logical thinking
+// is disappearing.
+// " Flips! "
+// Flash on screen, inst.
+// "BUMS"
+// "JAIL"/"MENTAL WARD (Cop code for looney?  T. asks Cop.)"
+// "GUTS OR GLORY"
+
+// ( Duke's Mission
+
+// Duke:    "Looks like some kind of transporter...?"
+// Byte:    "...YES"
+
+// Duke:    "Waa, here goes nuthin'. "
+// (Duke puts r. arm in device)
+
+// Duke:    AAAAHHHHHHHHHHHHHHHHHHHHHHHHH!!!
+// (Duke's arm is seved.)
+// Byte:    NO.NO.NO.NO.NO.NO.NO...
+// ( Byte directs duke to the nearest heat source)
+// (Shut Up Mode)
+// ( Duke Staggers, end of arm bleeding, usual oozing arm guts. )
+// Byte: Left, Left, Left, Left, Right.
+// ( Duke, loozing consc, trips on broken pipe, )
+// ( hits temple on edge of step. )
+// ( Rats everywhere, byte pushing them away with weapon,
+// ( eventually covered, show usual groosums, Duke appears dead
+// ( Duke wakes up, in hospital, vision less blurry
+// ( Hospital doing brain scan, 1/3 cran. mass MISSING!
+// Doc: Hummm?  ( Grabbing upper lip to "appear" smart. )
+
+// Stand back boys
+
+// Schrapnel has busted my scull!
+// Now I'm insane, Mental ward, got to escape.
+// Search light everywhere.
+
+// (M)Mendor, The Tree Dweller.
+// (M)BashMan, The Destructor.
+// (M)Lash, The Scavenger.
+// (F)Mag, The Slut.
+// (F)
+// NRA OR SOMETHIN'
+
+// Duke Nukem
+// 5th Dimention
+// Pentagon Man!
+
+
+// I Hope your not stupid!
+// The 70's meet the future.
+// Dirty Harry style.  70's music with futuristic edge
+// The Instant De-Welder(tm)
+// I think I'm going to puke...
+// Badge attitude.
+// He's got a Badge(LA 3322), a Bulldog, a Bronco (beat up/bondoed).
+// Gfx:
+// Lite rail systems
+// A church.  Large cross
+// Sniper Scope,
+// Really use the phone
+// The Boiler Room
+// The IRS, nuking other government buildings?
+// You wouldn't have a belt of booz, would ya?
+// Slow turning signes
+// More persise shooting/descructions
+// Faces, use phoneoms and its lookup.  Talking, getting in fights.
+// Drug dealers, pimps, and all galore
+// Weapons, Anything lying around.
+// Trees to clime, burning trees.
+// Sledge Hammer, Sledge hammer with Spike
+// sancurary, get away from it all.
+// Goodlife = ( War + Greed ) / Peace
+// Monsterism           (ACTION)
+// Global Hunter        (RPG)
+// Slick a Wick         (PUZZLE)
+// Roach Condo          (FUNNY)
+// AntiProfit           (RPG)
+// Pen Patrol           (TD SIM)
+// 97.5 KPIG! - Wanker County
+// "Fauna" - Native Indiginouns Animal Life
+
--- /dev/null
+++ b/Game/src/gamedefs.h
@@ -1,0 +1,186 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+//****************************************************************************
+//
+// gamedefs.h
+//
+// common defines between the game and the setup program
+//
+//****************************************************************************
+
+#ifndef _gamedefs_public_
+#define _gamedefs_public_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//****************************************************************************
+//
+// DEFINES
+//
+//****************************************************************************
+
+//
+// Setup program defines
+//
+#define SETUPFILENAME "duke3d.cfg"
+
+
+// Max number of players
+#define MAXPLAYERS 16
+
+// Number of Mouse buttons
+#define MAXMOUSEBUTTONS 7
+
+// Number of JOY buttons
+#define MAXJOYBUTTONS 32
+
+// Number of joystick "top hats"
+#define MAXJOYHATS 6
+
+// Number of EXTERNAL buttons
+
+//#define MAXEXTERNALBUTTONS 6
+
+//
+// modem string defines
+//
+
+#define MAXMODEMSTRING 50
+
+// MACRO defines
+
+#define MAXMACROS      10
+#define MAXMACROLENGTH 34
+
+// Phone list defines
+
+#define PHONENUMBERLENGTH 28
+#define PHONENAMELENGTH   16
+#define MAXPHONEENTRIES   10
+
+// length of program functions
+
+#define MAXFUNCTIONLENGTH 30
+
+// length of axis functions
+
+#define MAXAXISFUNCTIONLENGTH 30
+
+// Max Player Name length
+
+#define MAXPLAYERNAMELENGTH 11
+
+// Max RTS Name length
+
+#define MAXRTSNAMELENGTH 15
+
+// Number of Mouse Axes
+
+#define MAXMOUSEAXES 2
+
+// Number of JOY axes
+
+#define MAXJOYAXES 6
+    //4
+
+// Number of GAMEPAD axes
+
+#define MAXGAMEPADAXES 2
+
+// MIN/MAX scale value for controller scales
+
+#define MAXCONTROLSCALEVALUE (1<<19)
+
+// DEFAULT scale value for controller scales
+
+#define DEFAULTCONTROLSCALEVALUE (1<<16)
+
+// base value for controller scales
+
+#define BASECONTROLSCALEVALUE (1<<16)
+
+enum
+   {
+   gametype_network=3,
+   gametype_serial=1,
+   gametype_modem=2
+   };
+
+enum
+   {
+   connecttype_dialing=0,
+   connecttype_answer=1,
+   connecttype_alreadyconnected=2
+   };
+
+enum
+   {
+   screenbuffer_320x200,
+   screenbuffer_640x400,
+   screenbuffer_640x480,
+   screenbuffer_800x600,
+   screenbuffer_1024x768,
+   screenbuffer_1280x1024,
+   screenbuffer_1600x1200
+   };
+
+enum
+   {
+   vesa_320x200,
+   vesa_360x200,
+   vesa_320x240,
+   vesa_360x240,
+   vesa_320x400,
+   vesa_360x400,
+   vesa_640x350,
+   vesa_640x400,
+   vesa_640x480,
+   vesa_800x600,
+   vesa_1024x768,
+   vesa_1280x1024,
+   vesa_1600x1200
+   };
+
+enum
+   {
+   screenmode_chained,
+   screenmode_vesa,
+   screenmode_buffered,
+   screenmode_tseng,
+   screenmode_paradise,
+   screenmode_s3,
+   screenmode_crystal,
+   screenmode_redblue,
+   };
+
+
+#ifdef __cplusplus
+};
+#endif
+#endif
+
--- /dev/null
+++ b/Game/src/global.c
@@ -1,0 +1,967 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "duke3d.h"
+
+char *mymembuf;
+char MusicPtr[72000];
+
+
+crc32_t crc32lookup[] = {
+		// good:
+		{ CRC_BASE_GRP_SHAREWARE_13,	"SHAREWARE 1.3D", 11035779 },
+		{ CRC_BASE_GRP_FULL_13,			"FULL 1.3D     ", 26524524 },
+		{ CRC_BASE_GRP_PLUTONIUM_14,	"PLUTONIUM 1.4 ", 44348015 },
+		{ CRC_BASE_GRP_ATOMIC_15,		"ATOMIC 1.5    ", 44356548 },
+		// unknown:
+		{ 0,							"HACK/UNKNOWN  ", 0}
+	};
+
+unsigned char conVersion = 13;
+unsigned char grpVersion = 0;
+
+// FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116 and 117 only)
+
+// For BYTEVERSION diff, 27/116 vs 28/117 see extras\duke3d.h vs source\duke3d.h
+// from the official source code release. 
+
+int BYTEVERSION_27 = 27; // 1.3 under 1.4 Plutonium. Not supported anymore
+int BYTEVERSION_116 = 116; // 1.4 Plutonium. Not supported anymore
+
+int BYTEVERSION_28 = 28; // 1.3 under 1.5 engine
+int BYTEVERSION_117 = 117; // 1.5 Atomic
+
+int BYTEVERSION_29 = 29; // 1.3 under xDuke v19.6.
+int BYTEVERSION_118 = 118; // 1.5 Atomic under xDuke v19.6.
+
+int BYTEVERSION_1_3 = 1; // for 1.3 demos (Not compatible)
+
+int BYTEVERSION			= 119; // xDuke v19.7
+
+short global_random;
+short neartagsector, neartagwall, neartagsprite;
+
+long gc,neartaghitdist,lockclock,max_player_health,max_armour_amount,max_ammo_amount[MAX_WEAPONS];
+
+// long temp_data[MAXSPRITES][6];
+struct weaponhit hittype[MAXSPRITES];
+short spriteq[1024],spriteqloc,spriteqamount=64;
+
+// ported build engine has this, too.  --ryan.
+#if PLATFORM_DOS
+short moustat = 0;
+#endif
+
+struct animwalltype animwall[MAXANIMWALLS];
+short numanimwalls;
+long *animateptr[MAXANIMATES], animategoal[MAXANIMATES], animatevel[MAXANIMATES], animatecnt;
+// long oanimateval[MAXANIMATES];
+short animatesect[MAXANIMATES];
+long msx[2048],msy[2048];
+short cyclers[MAXCYCLERS][6],numcyclers;
+
+char fta_quotes[NUMOFFIRSTTIMEACTIVE][64];
+
+unsigned char tempbuf[2048], packbuf[576];
+
+char buf[80];
+
+short camsprite;
+short mirrorwall[64], mirrorsector[64], mirrorcnt;
+
+int current_menu;
+
+char betaname[80];
+
+char level_names[44][33],level_file_names[44][128];
+long partime[44],designertime[44];
+char volume_names[4][33] = { "L.A. MELTDOWN", "LUNAR APOCALYPSE", "SHRAPNEL CITY", "" }; // Names are not in 1.3 con files. MUST be in code.
+char skill_names[5][33] = { "PIECE OF CAKE", "LET'S ROCK", "COME GET SOME", "DAMN I'M GOOD", "" };
+
+volatile long checksume;
+long soundsiz[NUM_SOUNDS];
+
+short soundps[NUM_SOUNDS],soundpe[NUM_SOUNDS],soundvo[NUM_SOUNDS];
+char soundm[NUM_SOUNDS],soundpr[NUM_SOUNDS];
+char sounds[NUM_SOUNDS][14];
+
+short title_zoom;
+
+fx_device device;
+
+SAMPLE Sound[ NUM_SOUNDS ];
+SOUNDOWNER SoundOwner[NUM_SOUNDS][4];
+
+char numplayersprites,earthquaketime;
+
+long fricxv,fricyv;
+struct player_orig po[MAXPLAYERS];
+struct player_struct ps[MAXPLAYERS];
+struct user_defs ud;
+
+char pus, pub;
+char syncstat, syncval[MAXPLAYERS][MOVEFIFOSIZ];
+long syncvalhead[MAXPLAYERS], syncvaltail, syncvaltottail;
+
+input sync[MAXPLAYERS], loc;
+input recsync[RECSYNCBUFSIZ];
+long avgfvel, avgsvel, avgavel, avghorz, avgbits;
+
+
+input inputfifo[MOVEFIFOSIZ][MAXPLAYERS];
+input recsync[RECSYNCBUFSIZ];
+
+long movefifosendplc;
+
+  //Multiplayer syncing variables
+short screenpeek;
+long movefifoend[MAXPLAYERS];
+
+
+    //Game recording variables
+
+char playerreadyflag[MAXPLAYERS],ready2send;
+char playerquitflag[MAXPLAYERS];
+long vel, svel, angvel, horiz, ototalclock, respawnactortime=768, respawnitemtime=768, groupfile;
+
+long script[MAXSCRIPTSIZE],*scriptptr,*insptr,*labelcode,labelcnt;
+long *actorscrptr[MAXTILES],*parsing_actor;
+char *label,*textptr,error,warning,killit_flag;
+char *music_pointer;
+char actortype[MAXTILES];
+
+
+char display_mirror,typebuflen,typebuf[41];
+
+char music_fn[4][11][13],music_select;
+char env_music_fn[4][13];
+char rtsplaying;
+
+
+short weaponsandammosprites[15] = {
+        RPGSPRITE,
+        CHAINGUNSPRITE,
+        DEVISTATORAMMO,
+        RPGAMMO,
+        RPGAMMO,
+        JETPACK,
+        SHIELD,
+        FIRSTAID,
+        STEROIDS,
+        RPGAMMO,
+        RPGAMMO,
+        RPGSPRITE,
+        RPGAMMO,
+        FREEZESPRITE,
+        FREEZEAMMO
+    };
+
+long impact_damage;
+
+        //GLOBAL.C - replace the end "my's" with this
+long myx, omyx, myxvel, myy, omyy, myyvel, myz, omyz, myzvel;
+short myhoriz, omyhoriz, myhorizoff, omyhorizoff;
+short myang, omyang, mycursectnum, myjumpingcounter,frags[MAXPLAYERS][MAXPLAYERS];
+
+char myjumpingtoggle, myonground, myhardlanding, myreturntocenter;
+signed char multiwho, multipos, multiwhat, multiflag;
+
+long fakemovefifoplc,movefifoplc;
+long myxbak[MOVEFIFOSIZ], myybak[MOVEFIFOSIZ], myzbak[MOVEFIFOSIZ];
+long myhorizbak[MOVEFIFOSIZ],dukefriction = 0xcc00, show_shareware;
+
+short myangbak[MOVEFIFOSIZ];
+char myname[2048] = {"XDUKE"};
+char camerashitable,freezerhurtowner=0,lasermode;
+// CTW - MODIFICATION
+// char networkmode = 255, movesperpacket = 1,gamequit = 0,playonten = 0,everyothertime;
+char networkmode = 255, movesperpacket = 1,gamequit = 0,everyothertime;
+// CTW END - MODIFICATION
+long numfreezebounces=3,rpgblastradius,pipebombblastradius,tripbombblastradius,shrinkerblastradius,morterblastradius,bouncemineblastradius,seenineblastradius;
+STATUSBARTYPE sbar;
+
+long myminlag[MAXPLAYERS], mymaxlag, otherminlag, bufferjitter = 1;
+short numclouds,clouds[128],cloudx[128],cloudy[128];
+long cloudtotalclock = 0,totalmemory = 0;
+long numinterpolations = 0, startofdynamicinterpolations = 0;
+long oldipos[MAXINTERPOLATIONS];
+long bakipos[MAXINTERPOLATIONS];
+long *curipos[MAXINTERPOLATIONS];
+
+
+// portability stuff.  --ryan.
+// A good portion of this was ripped from GPL'd Rise of the Triad.  --ryan.
+
+void FixFilePath(char *filename)
+{
+#if PLATFORM_UNIX
+    char *ptr;
+    char *lastsep = filename;
+
+    if ((!filename) || (*filename == '\0'))
+        return;
+
+    if (access(filename, F_OK) == 0)  /* File exists; we're good to go. */
+        return;
+
+    for (ptr = filename; 1; ptr++)
+    {
+        if (*ptr == '\\')
+            *ptr = PATH_SEP_CHAR;
+
+        if ((*ptr == PATH_SEP_CHAR) || (*ptr == '\0'))
+        {
+            char pch = *ptr;
+            struct dirent *dent = NULL;
+            DIR *dir;
+
+            if ((pch == PATH_SEP_CHAR) && (*(ptr + 1) == '\0'))
+                return; /* eos is pathsep; we're done. */
+
+            if (lastsep == ptr)
+                continue;  /* absolute path; skip to next one. */
+
+            *ptr = '\0';
+            if (lastsep == filename) {
+                dir = opendir((*lastsep == PATH_SEP_CHAR) ? ROOTDIR : CURDIR);
+                
+                if (*lastsep == PATH_SEP_CHAR) {
+                    lastsep++;
+                }
+            } 
+            else
+            {
+                *lastsep = '\0';
+                dir = opendir(filename);
+                *lastsep = PATH_SEP_CHAR;
+                lastsep++;
+            }
+
+            if (dir == NULL)
+            {
+                *ptr = PATH_SEP_CHAR;
+                return;  /* maybe dir doesn't exist? give up. */
+            }
+
+            while ((dent = readdir(dir)) != NULL)
+            {
+                if (strcasecmp(dent->d_name, lastsep) == 0)
+                {
+                    /* found match; replace it. */
+                    strcpy(lastsep, dent->d_name);
+                    break;
+                }
+            }
+
+            closedir(dir);
+            *ptr = pch;
+            lastsep = ptr;
+
+            if (dent == NULL)
+                return;  /* no match. oh well. */
+
+            if (pch == '\0')  /* eos? */
+                return;
+        }
+    }
+#endif
+}
+
+
+#if PLATFORM_DOS
+ /* no-op. */
+
+#elif PLATFORM_WIN32
+int _dos_findfirst(char *filename, int x, struct find_t *f)
+{
+    long rc = _findfirst(filename, &f->data);
+    f->handle = rc;
+    if (rc != -1)
+    {
+        strncpy(f->name, f->data.name, sizeof (f->name) - 1);
+        f->name[sizeof (f->name) - 1] = '\0';
+        return(0);
+    }
+    return(1);
+}
+
+int _dos_findnext(struct find_t *f)
+{
+    int rc = 0;
+    if (f->handle == -1)
+        return(1);   /* invalid handle. */
+
+    rc = _findnext(f->handle, &f->data);
+    if (rc == -1)
+    {
+        _findclose(f->handle);
+        f->handle = -1;
+        return(1);
+    }
+
+    strncpy(f->name, f->data.name, sizeof (f->name) - 1);
+    f->name[sizeof (f->name) - 1] = '\0';
+    return(0);
+}
+
+#elif PLATFORM_UNIX 
+int _dos_findfirst(char *filename, int x, struct find_t *f)
+{
+    char *ptr;
+
+    if (strlen(filename) >= sizeof (f->pattern))
+        return(1);
+
+    strcpy(f->pattern, filename);
+    FixFilePath(f->pattern);
+    ptr = strrchr(f->pattern, PATH_SEP_CHAR);
+
+    if (ptr == NULL)
+    {
+        ptr = filename;
+        f->dir = opendir(CURDIR);
+    }
+    else
+    {
+        *ptr = '\0';
+        f->dir = opendir(f->pattern);
+        memmove(f->pattern, ptr + 1, strlen(ptr + 1) + 1);
+    }
+
+    return(_dos_findnext(f));
+}
+
+
+static int check_pattern_nocase(const char *x, const char *y)
+{
+    if ((x == NULL) || (y == NULL))
+        return(0);  /* not a match. */
+
+    while ((*x) && (*y))
+    {
+        if (*x == '*')
+        {
+            x++;
+            while (*y != '\0')
+            {
+                if (toupper((int) *x) == toupper((int) *y))
+                    break;
+                y++;
+            }
+        }
+
+        else if (*x == '?')
+        {
+            if (*y == '\0')
+                return(0);  /* anything but EOS is okay. */
+        }
+
+        else
+        {
+            if (toupper((int) *x) != toupper((int) *y))
+                return(0);  /* not a match. */
+        }
+
+        x++;
+        y++;
+    }
+
+    return(*x == *y);  /* it's a match (both should be EOS). */
+}
+
+int _dos_findnext(struct find_t *f)
+{
+    struct dirent *dent;
+
+    if (f->dir == NULL)
+        return(1);  /* no such dir or we're just done searching. */
+
+    while ((dent = readdir(f->dir)) != NULL)
+    {
+        if (check_pattern_nocase(f->pattern, dent->d_name))
+        {
+            if (strlen(dent->d_name) < sizeof (f->name))
+            {
+                strcpy(f->name, dent->d_name);
+                return(0);  /* match. */
+            }
+        }
+    }
+
+    closedir(f->dir);
+    f->dir = NULL;
+    return(1);  /* no match in whole directory. */
+}
+#else
+#error please define for your platform.
+#endif
+
+
+#if !PLATFORM_DOS
+void _dos_getdate(struct dosdate_t *date)
+{
+	time_t curtime = time(NULL);
+	struct tm *tm;
+	
+	if (date == NULL) {
+		return;
+	}
+	
+	memset(date, 0, sizeof(struct dosdate_t));
+	
+	if ((tm = localtime(&curtime)) != NULL) {
+		date->day = tm->tm_mday;
+		date->month = tm->tm_mon + 1;
+		date->year = tm->tm_year + 1900;
+		date->dayofweek = tm->tm_wday + 1;
+	}
+}
+#endif
+
+
+int FindDistance2D(int ix, int iy)
+{
+  int   t;
+
+  ix= abs(ix);        /* absolute values */
+  iy= abs(iy);
+
+  if (ix<iy)
+  {
+     int tmp = ix;
+     ix = iy;
+     iy = tmp;
+  }
+
+  t = iy + (iy>>1);
+
+  return (ix - (ix>>5) - (ix>>7)  + (t>>2) + (t>>6));
+}
+
+int FindDistance3D(int ix, int iy, int iz)
+{
+   int   t;
+
+   ix= abs(ix);           /* absolute values */
+   iy= abs(iy);
+   iz= abs(iz);
+
+   if (ix<iy)
+   {
+     int tmp = ix;
+     ix = iy;
+     iy = tmp;
+   }
+
+   if (ix<iz)
+   {
+     int tmp = ix;
+     ix = iz;
+     iz = tmp;
+   }
+
+   t = iy + iz;
+
+   return (ix - (ix>>4) + (t>>2) + (t>>3));
+}
+
+void Error (int errorType, char *error, ...)
+{
+   va_list argptr;
+
+   SDL_Quit();
+
+   if(errorType==EXIT_FAILURE)
+	   printf("ERROR: Please copy that screen and visit http://duke3d.m-klein.com for report:\n");
+	else
+		printf("http://duke3d.m-klein.com\n");
+
+   va_start (argptr, error);
+   vprintf(error, argptr);
+   va_end (argptr);
+
+   printf("Press any key to continue...\n");
+
+	// FIX_00043: Nicer exit on error. Ask the user to hit a key on exits and error exits.
+   getch();
+
+   exit (errorType);
+}
+
+void write2disk(int line, char* cfilename, char *filename2write, char *message)
+{
+	// usage: write2disk(__LINE__, __FILE__, "c:\temp\my_dbug_file.txt", char* msg);
+
+	int i, k=0;
+	char filename[2048];
+	FILE *pFile;
+
+	for(i=0; cfilename[i]; i++)
+	{
+		if(cfilename[i]=='\\')
+		{
+			i++;
+			k = 0;
+		}
+		filename[k++]=(cfilename[i]=='.')?0:cfilename[i];
+	}
+	pFile = fopen(filename2write,"a");
+	fprintf(pFile,"%-4d %-5s %s", line, filename, message);
+	fclose(pFile);
+}
+
+int32 SafeOpenAppend (const char *_filename, int32 filetype)
+{
+	int	handle;
+    char filename[MAX_PATH];
+    strncpy(filename, _filename, sizeof (filename));
+    filename[sizeof (filename) - 1] = '\0';
+    FixFilePath(filename);
+
+#if (defined PLATFORM_WIN32)
+    handle = open(filename,O_RDWR | O_BINARY | O_CREAT | O_APPEND );
+#else
+	handle = open(filename,O_RDWR | O_BINARY | O_CREAT | O_APPEND
+	, S_IREAD | S_IWRITE);
+#endif
+
+	if (handle == -1)
+		Error (EXIT_FAILURE, "Error opening for append %s: %s",filename,strerror(errno));
+
+	return handle;
+}
+
+boolean SafeFileExists ( const char * _filename )
+{
+    char filename[MAX_PATH];
+    strncpy(filename, _filename, sizeof (filename));
+    filename[sizeof (filename) - 1] = '\0';
+    FixFilePath(filename);
+
+#if( defined PLATFORM_WIN32)
+        return(access(filename, 6) == 0);
+#else
+    return(access(filename, F_OK) == 0);
+#endif
+}
+
+
+int32 SafeOpenWrite (const char *_filename, int32 filetype)
+{
+	int	handle;
+    char filename[MAX_PATH];
+    strncpy(filename, _filename, sizeof (filename));
+    filename[sizeof (filename) - 1] = '\0';
+    FixFilePath(filename);
+
+#if (defined PLATFORM_WIN32)
+    handle = open(filename,O_RDWR | O_BINARY | O_CREAT | O_TRUNC );
+#else
+	handle = open(filename,O_RDWR | O_BINARY | O_CREAT | O_TRUNC
+	, S_IREAD | S_IWRITE);
+#endif
+
+	if (handle == -1)
+		Error (EXIT_FAILURE, "Error opening %s: %s",filename,strerror(errno));
+
+	return handle;
+}
+
+int32 SafeOpenRead (const char *_filename, int32 filetype)
+{
+	int	handle;
+    char filename[MAX_PATH];
+    strncpy(filename, _filename, sizeof (filename));
+    filename[sizeof (filename) - 1] = '\0';
+    FixFilePath(filename);
+
+	handle = open(filename,O_RDONLY | O_BINARY);
+
+	if (handle == -1)
+		Error (EXIT_FAILURE, "Error opening %s: %s",filename,strerror(errno));
+
+	return handle;
+}
+
+
+void SafeRead (int32 handle, void *buffer, int32 count)
+{
+	unsigned	iocount;
+
+	while (count)
+	{
+		iocount = count > 0x8000 ? 0x8000 : count;
+		if (read (handle,buffer,iocount) != (int)iocount)
+			Error (EXIT_FAILURE, "File read failure reading %ld bytes",count);
+		buffer = (void *)( (byte *)buffer + iocount );
+		count -= iocount;
+	}
+}
+
+
+void SafeWrite (int32 handle, void *buffer, int32 count)
+{
+	unsigned	iocount;
+
+	while (count)
+	{
+		iocount = count > 0x8000 ? 0x8000 : count;
+		if (write (handle,buffer,iocount) != (int)iocount)
+			Error (EXIT_FAILURE, "File write failure writing %ld bytes",count);
+		buffer = (void *)( (byte *)buffer + iocount );
+		count -= iocount;
+	}
+}
+
+void SafeWriteString (int handle, char * buffer)
+{
+	unsigned	iocount;
+
+   iocount=strlen(buffer);
+	if (write (handle,buffer,iocount) != (int)iocount)
+			Error (EXIT_FAILURE, "File write string failure writing %s\n",buffer);
+}
+
+void *SafeMalloc (long size)
+{
+	void *ptr;
+
+#if 0
+   if (zonememorystarted==false)
+      Error(EXIT_FAILURE, "Called SafeMalloc without starting zone memory\n");
+	ptr = Z_Malloc (size,PU_STATIC,NULL);
+#else
+    ptr = malloc(size);
+#endif
+
+	if (!ptr)
+      Error (EXIT_FAILURE, "SafeMalloc failure for %lu bytes",size);
+
+	return ptr;
+}
+
+void SafeRealloc (void **x, int32 size)
+{
+	void *ptr;
+
+#if 0
+   if (zonememorystarted==false)
+      Error(EXIT_FAILURE, "Called SafeMalloc without starting zone memory\n");
+	ptr = Z_Malloc (size,PU_STATIC,NULL);
+#else
+    ptr = realloc(*x, size);
+#endif
+
+	if (!ptr)
+      Error (EXIT_FAILURE, "SafeRealloc failure for %lu bytes",size);
+
+    *x = ptr;
+}
+
+void *SafeLevelMalloc (long size)
+{
+	void *ptr;
+
+#if 0
+   if (zonememorystarted==false)
+      Error(EXIT_FAILURE, "Called SafeLevelMalloc without starting zone memory\n");
+   ptr = Z_LevelMalloc (size,PU_STATIC,NULL);
+#else
+    ptr = malloc(size);
+#endif
+
+	if (!ptr)
+      Error (EXIT_FAILURE, "SafeLevelMalloc failure for %lu bytes",size);
+
+	return ptr;
+}
+
+void SafeFree (void * ptr)
+{
+   if ( ptr == NULL )
+      Error (EXIT_FAILURE, "SafeFree : Tried to free a freed pointer\n");
+
+#if 0
+	Z_Free (ptr);
+#else
+    free(ptr);
+#endif
+}
+
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#if PLATFORM_DOS
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#if PLATFORM_WIN32
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef BYTE_ORDER
+#error Please define your platform.
+#endif
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define KeepShort IntelShort
+#define SwapShort MotoShort
+#define KeepLong IntelLong
+#define SwapLong MotoLong
+#else
+#define KeepShort MotoShort
+#define SwapShort IntelShort
+#define KeepLong MotoLong
+#define SwapLong IntelLong
+#endif
+
+short	SwapShort (short l)
+{
+	byte	b1,b2;
+
+	b1 = l&255;
+	b2 = (l>>8)&255;
+
+	return (b1<<8) + b2;
+}
+
+short	KeepShort (short l)
+{
+	return l;
+}
+
+
+long	SwapLong (long l)
+{
+	byte	b1,b2,b3,b4;
+
+	b1 = l&255;
+	b2 = (l>>8)&255;
+	b3 = (l>>16)&255;
+	b4 = (l>>24)&255;
+
+	return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4;
+}
+
+long	KeepLong (long l)
+{
+	return l;
+}
+
+
+#undef KeepShort
+#undef KeepLong
+#undef SwapShort
+#undef SwapLong
+
+void SwapIntelLong(long *l)
+{
+    *l = IntelLong(*l);
+}
+
+void SwapIntelShort(short *s)
+{
+    *s = IntelShort(*s);
+}
+
+void SwapIntelLongArray(long *l, int num)
+{
+    while (num--) {
+        SwapIntelLong(l);
+        l++;
+    }
+}
+
+void SwapIntelShortArray(short *s, int num)
+{
+    while (num--) {
+        SwapIntelShort(s);
+        s++;
+    }
+}
+
+
+/* 
+  Copied over from Wolf3D Linux: http://www.icculus.org/wolf3d/
+  Modified for ROTT.
+  Stolen for Duke3D, too.
+ */
+ 
+#if PLATFORM_UNIX
+char *strlwr(char *s)
+{
+	char *p = s;
+	
+	while (*p) {
+		*p = tolower(*p);
+		p++;
+	}
+	
+	return s;
+}
+
+char *strupr(char *s)
+{
+	char *p = s;
+	
+	while (*p) {
+		*p = toupper(*p);
+		p++;
+	}
+	
+	return s;
+}
+	
+char *itoa(int value, char *string, int radix)
+{
+	switch (radix) {
+		case 10:
+			sprintf(string, "%d", value);
+			break;
+		case 16:
+			sprintf(string, "%x", value);
+			break;
+		default:
+			STUBBED("unknown radix");
+			break;
+	}
+	
+	return string;
+}
+
+char *ltoa(long value, char *string, int radix)
+{
+	switch (radix) {
+		case 10:
+			sprintf(string, "%ld", value);
+			break;
+		case 16:
+			sprintf(string, "%lx", value);
+			break;
+		default:
+			STUBBED("unknown radix");
+			break;
+	}
+	
+	return string;
+}
+
+char *ultoa(unsigned long value, char *string, int radix)
+{
+	switch (radix) {
+		case 10:
+			sprintf(string, "%lu", value);
+			break;
+		case 16:
+			sprintf(string, "%lux", value);
+			break;
+		default:
+			STUBBED("unknown radix");
+			break;
+	}
+	
+	return string;
+}
+#endif
+
+char ApogeePath[256];
+
+int setup_homedir (void)
+{
+#if PLATFORM_UNIX
+	int err;
+
+	snprintf (ApogeePath, sizeof (ApogeePath), "%s/.duke3d/", getenv ("HOME"));
+
+	err = mkdir (ApogeePath, S_IRWXU);
+	if (err == -1 && errno != EEXIST)
+	{
+		fprintf (stderr, "Couldn't create preferences directory: %s\n", 
+				strerror (errno));
+		return -1;
+	}
+#else
+    sprintf(ApogeePath, ".%s", PATH_SEP_STR);
+#endif
+
+	return 0;
+}
+
+
+char   CheckParm (char *check)
+{
+    int i;
+    for (i = 1; i < _argc; i++)
+    {
+        if ((*(_argv[i]) == '-') && (strcmpi(_argv[i] + 1, check) == 0))
+            return(i);
+    }
+
+    return(0);
+}
+
+
+static void (*shutdown_func)(void) = NULL;
+
+void RegisterShutdownFunction( void (* shutdown) (void) )
+{
+    shutdown_func = shutdown;
+}
+
+void Shutdown(void)
+{
+    if (shutdown_func != NULL)
+    {
+        shutdown_func();
+        shutdown_func = NULL;
+    }
+}
+
+
+/*
+ * From Ryan's buildengine CHANGELOG:
+ *  Removed global var: cachedebug in engine.c, and put #define
+ *  BUILD_CACHEDEBUG 0 at the top of the source. Flip it to 1 if you ever
+ *  need to tinker in the cache code.
+ */
+char cachedebug = 0;
+
--- /dev/null
+++ b/Game/src/joystick.h
@@ -1,0 +1,13 @@
+#ifndef __joystick_h
+#define __joystick_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void   JOYSTICK_UpdateHats( void );
+
+#ifdef __cplusplus
+};
+
+#endif
+#endif /* __joystick_h */
\ No newline at end of file
--- /dev/null
+++ b/Game/src/keyboard.c
@@ -1,0 +1,442 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "keyboard.h"
+
+/*
+=============================================================================
+
+                               GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+volatile byte  KB_KeyDown[ MAXKEYBOARDSCAN ];   // Keyboard state array
+volatile kb_scancode KB_LastScan;
+
+static volatile boolean keyIsWaiting = 0;
+
+static char scancodeToASCII[ MAXKEYBOARDSCAN ];
+static char shiftedScancodeToASCII[ MAXKEYBOARDSCAN ];
+static char extscanToSC[ MAXKEYBOARDSCAN ];
+
+/*
+=============================================================================
+FUNCTIONS
+=============================================================================
+*/
+
+void keyhandler(void)
+{
+    static boolean gotextended = false;
+    
+    int rawkey = _readlastkeyhit();
+    int lastkey = rawkey & 0x7f;
+    
+    // 128 bit means key was released.
+    int pressed = !(rawkey & 0x80);
+    
+    if (rawkey == 0xe0 && !gotextended)
+    {
+    	gotextended = true;
+        return;
+    }
+
+    if (rawkey == 0xe1)
+    {
+    	/* SBF - build doesn't actually generate this for Pause/Break */
+    	STUBBED("Another extended key!");
+    	return;
+    }
+        
+    if (gotextended)
+    {
+    	gotextended = false;
+    	
+    	/* remap extended key to Duke3D equivalent */
+    	lastkey = extscanToSC[lastkey];
+    }
+    
+    if (lastkey >= MAXKEYBOARDSCAN)
+    {
+        STUBBED("Scancode out of range!");
+        return;
+    }
+
+    if (pressed)
+    {
+         KB_LastScan = lastkey;
+    }
+
+    KB_KeyDown[lastkey] = pressed;
+
+    keyIsWaiting = ((keyIsWaiting) || (KB_KeyDown[lastkey]));
+    
+    CONTROL_UpdateKeyboardState(lastkey, pressed);
+}
+
+void KB_KeyEvent( int scancode, boolean keypressed )
+{
+	STUBBED("KB_KeyEvent");
+}
+
+boolean KB_KeyWaiting( void )
+{
+    _handle_events();
+    return keyIsWaiting;
+}
+
+char KB_Getch( void )
+{
+    int shifted;
+
+    while (!keyIsWaiting) { _idle(); /* pull the pud. */ }
+	keyIsWaiting = false;
+    if (KB_LastScan >= MAXKEYBOARDSCAN)
+        return(0xFF);
+
+    if (KB_KeyDown[sc_LeftShift] || KB_KeyDown[sc_RightShift])
+        return shiftedScancodeToASCII[KB_LastScan];
+
+    return scancodeToASCII[KB_LastScan];
+}
+
+void KB_Addch( char ch )
+{
+	STUBBED("KB_Addch");
+}
+
+void KB_FlushKeyboardQueue( void )
+{
+	_handle_events();
+	keyIsWaiting = false;
+	memset(KB_KeyDown, 0, sizeof(KB_KeyDown));
+	// FIX_00077: Menu goes directly to the "NEW GAME" sub-menu when starting new game (Turrican)
+}
+
+void KB_ClearKeysDown( void )
+{
+    memset((void *) KB_KeyDown, '\0', sizeof (KB_KeyDown));
+	keyIsWaiting = false;
+}
+
+static struct {
+	char* name;
+	int code;
+} keyname2scancode[] = {
+	{ ",",		sc_Comma	},
+	{ ".",		sc_Period	},
+	{ "Enter",	sc_Return	},
+	{ "Escape",	sc_Escape	},
+	{ "Space",	sc_Space	},
+	{ "BakSpc",	sc_BackSpace	},
+	{ "Tab",	sc_Tab		},
+	{ "LAlt",	sc_LeftAlt	},
+	{ "LCtrl",	sc_LeftControl	},
+	{ "CapLck",	sc_CapsLock	},
+	{ "LShift",	sc_LeftShift	},
+	{ "RShift",	sc_RightShift	},
+	{ "F1",		sc_F1		},
+	{ "F2",		sc_F2		},
+	{ "F3",		sc_F3		},
+	{ "F4",		sc_F4		},
+	{ "F5",		sc_F5		},
+	{ "F6",		sc_F6		},
+	{ "F7",		sc_F7		},
+	{ "F8",		sc_F8		},
+	{ "F9",		sc_F9		},
+	{ "F10",	sc_F10		},
+	{ "F11",	sc_F11		},
+	{ "F12",	sc_F12		},
+	{ "Kpad*",	sc_Kpad_Star	},
+	{ "Pause",	sc_Pause	},
+	{ "ScrLck",	sc_ScrollLock	},
+	{ "NumLck",	sc_NumLock	},  // 6 chars Max please.
+	{ "/",		sc_Slash	},
+	{ ";",		sc_SemiColon	},
+	{ "'",		sc_Quote	},
+	{ "`",		sc_Tilde	},
+	{ "\\",		sc_BackSlash	},
+	{ "[",		sc_OpenBracket	},
+	{ "]",		sc_CloseBracket	},
+	{ "1",		sc_1		},
+	{ "2",		sc_2		},
+	{ "3",		sc_3		},
+	{ "4",		sc_4		},
+	{ "5",		sc_5		},
+	{ "6",		sc_6		},
+	{ "7",		sc_7		},
+	{ "8",		sc_8		},
+	{ "9",		sc_9		},
+	{ "0",		sc_0		},
+	{ "-",		sc_Minus	},
+	{ "=",		sc_Equals	},
+	{ "+",		sc_Plus		},
+	{ "Kpad1",	sc_kpad_1	},
+	{ "Kpad2",	sc_kpad_2	},
+	{ "Kpad3",	sc_kpad_3	},
+	{ "Kpad4",	sc_kpad_4	},
+	{ "Kpad5",	sc_kpad_5	},
+	{ "Kpad6",	sc_kpad_6	},
+	{ "Kpad7",	sc_kpad_7	},
+	{ "Kpad8",	sc_kpad_8	},
+	{ "Kpad9",	sc_kpad_9	},
+	{ "Kpad0",	sc_kpad_0	},
+	{ "Kpad-",	sc_kpad_Minus	},
+	{ "Kpad+",	sc_kpad_Plus	},
+	{ "Kpad.",	sc_kpad_Period	},
+	{ "A",		sc_A		},
+	{ "B",		sc_B		},
+	{ "C",		sc_C		},
+	{ "D",		sc_D		},
+	{ "E",		sc_E		},
+	{ "F",		sc_F		},
+	{ "G",		sc_G		},
+	{ "H",		sc_H		},
+	{ "I",		sc_I		},
+	{ "J",		sc_J		},
+	{ "K",		sc_K		},
+	{ "L",		sc_L		},
+	{ "M",		sc_M		},
+	{ "N",		sc_N		},
+	{ "O",		sc_O		},
+	{ "P",		sc_P		},
+	{ "Q",		sc_Q		},
+	{ "R",		sc_R		},
+	{ "S",		sc_S		},
+	{ "T",		sc_T		},
+	{ "U",		sc_U		},
+	{ "V",		sc_V		},
+	{ "W",		sc_W		},
+	{ "X",		sc_X		},
+	{ "Y",		sc_Y		},
+	{ "Z",		sc_Z		},
+	{ "Up",		sc_UpArrow	},
+	{ "Down",	sc_DownArrow	},
+	{ "Left",	sc_LeftArrow	},
+	{ "Right",	sc_RightArrow	},
+	{ "Insert",	sc_Insert	},
+	{ "Delete",	sc_Delete	},
+	{ "Home",	sc_Home		},
+	{ "End",	sc_End		},
+	{ "PgUp",	sc_PgUp		},
+	{ "PgDn",	sc_PgDn		},
+	{ "RAlt",	sc_RightAlt	},
+	{ "RCtrl",	sc_RightControl	},
+	{ "Kpad/",	sc_kpad_Slash	},
+	{ "KpdEnt",	sc_kpad_Enter	},
+	{ "PrtScn",	sc_PrintScreen	},
+	{ NULL,		0		}
+};
+
+char *KB_ScanCodeToString( kb_scancode scancode )
+{
+	int i;
+	for(i = 0; keyname2scancode[i].name != NULL; i++)
+	{
+        if (keyname2scancode[i].code == scancode)
+            return keyname2scancode[i].name;
+	}
+	
+	return NULL;
+}
+
+kb_scancode KB_StringToScanCode( char * string )
+{
+	char* name = NULL;
+	int32 i=0;
+	name = keyname2scancode[i].name;
+	for(;name;++i, name=keyname2scancode[i].name)
+	{
+		if(!stricmp(name,string))
+			break;
+	}
+	
+	return keyname2scancode[i].code;
+}
+
+void KB_TurnKeypadOn( void )
+{
+	STUBBED("KB_TurnKeypadOn");
+}
+
+void KB_TurnKeypadOff( void )
+{
+	STUBBED("KB_TurnKeypadOff");
+}
+
+boolean KB_KeypadActive( void )
+{
+	STUBBED("KB_KeypadActive");
+	return false;
+}
+
+void KB_Startup( void )
+{
+    memset(scancodeToASCII, 0xFF, sizeof (scancodeToASCII));
+
+    // !!! FIXME: incomplete?
+    scancodeToASCII[sc_A] = 'a';
+    scancodeToASCII[sc_B] = 'b';
+    scancodeToASCII[sc_C] = 'c';
+    scancodeToASCII[sc_D] = 'd';
+    scancodeToASCII[sc_E] = 'e';
+    scancodeToASCII[sc_F] = 'f';
+    scancodeToASCII[sc_G] = 'g';
+    scancodeToASCII[sc_H] = 'h';
+    scancodeToASCII[sc_I] = 'i';
+    scancodeToASCII[sc_J] = 'j';
+    scancodeToASCII[sc_K] = 'k';
+    scancodeToASCII[sc_L] = 'l';
+    scancodeToASCII[sc_M] = 'm';
+    scancodeToASCII[sc_N] = 'n';
+    scancodeToASCII[sc_O] = 'o';
+    scancodeToASCII[sc_P] = 'p';
+    scancodeToASCII[sc_Q] = 'q';
+    scancodeToASCII[sc_R] = 'r';
+    scancodeToASCII[sc_S] = 's';
+    scancodeToASCII[sc_T] = 't';
+    scancodeToASCII[sc_U] = 'u';
+    scancodeToASCII[sc_V] = 'v';
+    scancodeToASCII[sc_W] = 'w';
+    scancodeToASCII[sc_X] = 'x';
+    scancodeToASCII[sc_Y] = 'y';
+    scancodeToASCII[sc_Z] = 'z';
+    scancodeToASCII[sc_0] = '0';
+    scancodeToASCII[sc_1] = '1';
+    scancodeToASCII[sc_2] = '2';
+    scancodeToASCII[sc_3] = '3';
+    scancodeToASCII[sc_4] = '4';
+    scancodeToASCII[sc_5] = '5';
+    scancodeToASCII[sc_6] = '6';
+    scancodeToASCII[sc_7] = '7';
+    scancodeToASCII[sc_8] = '8';
+    scancodeToASCII[sc_9] = '9';
+    scancodeToASCII[sc_Escape] = asc_Escape;
+    scancodeToASCII[sc_Tab] = asc_Tab;
+    scancodeToASCII[sc_Space] = asc_Space;
+    scancodeToASCII[sc_Enter] = asc_Enter;
+    scancodeToASCII[sc_BackSpace] = asc_BackSpace;
+    scancodeToASCII[sc_Comma] = ',';
+    scancodeToASCII[sc_Period] = '.';
+    scancodeToASCII[sc_Kpad_Star] = '*';
+    scancodeToASCII[sc_Slash] = '/';
+    scancodeToASCII[sc_SemiColon] = ';';
+    scancodeToASCII[sc_Quote] = '\'';
+    scancodeToASCII[sc_Tilde] = '`';
+    scancodeToASCII[sc_BackSlash] = '\\';
+    scancodeToASCII[sc_OpenBracket] = '[';
+    scancodeToASCII[sc_CloseBracket] = ']';
+    scancodeToASCII[sc_Minus] = '-';
+    scancodeToASCII[sc_Equals] = '=';
+    scancodeToASCII[sc_Plus] = '+';
+    scancodeToASCII[sc_kpad_Minus] = '-';
+    scancodeToASCII[sc_kpad_Period] = '.';
+    scancodeToASCII[sc_kpad_Plus] = '+';
+
+    // !!! FIXME: incomplete?
+    memset(shiftedScancodeToASCII, 0xFF, sizeof (shiftedScancodeToASCII));
+    shiftedScancodeToASCII[sc_A] = 'A';
+    shiftedScancodeToASCII[sc_B] = 'B';
+    shiftedScancodeToASCII[sc_C] = 'C';
+    shiftedScancodeToASCII[sc_D] = 'D';
+    shiftedScancodeToASCII[sc_E] = 'E';
+    shiftedScancodeToASCII[sc_F] = 'F';
+    shiftedScancodeToASCII[sc_G] = 'G';
+    shiftedScancodeToASCII[sc_H] = 'H';
+    shiftedScancodeToASCII[sc_I] = 'I';
+    shiftedScancodeToASCII[sc_J] = 'J';
+    shiftedScancodeToASCII[sc_K] = 'K';
+    shiftedScancodeToASCII[sc_L] = 'L';
+    shiftedScancodeToASCII[sc_M] = 'M';
+    shiftedScancodeToASCII[sc_N] = 'N';
+    shiftedScancodeToASCII[sc_O] = 'O';
+    shiftedScancodeToASCII[sc_P] = 'P';
+    shiftedScancodeToASCII[sc_Q] = 'Q';
+    shiftedScancodeToASCII[sc_R] = 'R';
+    shiftedScancodeToASCII[sc_S] = 'S';
+    shiftedScancodeToASCII[sc_T] = 'T';
+    shiftedScancodeToASCII[sc_U] = 'U';
+    shiftedScancodeToASCII[sc_V] = 'V';
+    shiftedScancodeToASCII[sc_W] = 'W';
+    shiftedScancodeToASCII[sc_X] = 'X';
+    shiftedScancodeToASCII[sc_Y] = 'Y';
+    shiftedScancodeToASCII[sc_Z] = 'Z';
+    shiftedScancodeToASCII[sc_0] = ')';
+    shiftedScancodeToASCII[sc_1] = '!';
+    shiftedScancodeToASCII[sc_2] = '@';
+    shiftedScancodeToASCII[sc_3] = '#';
+    shiftedScancodeToASCII[sc_4] = '$';
+    shiftedScancodeToASCII[sc_5] = '%';
+    shiftedScancodeToASCII[sc_6] = '^';
+    shiftedScancodeToASCII[sc_7] = '&';
+    shiftedScancodeToASCII[sc_8] = '*';
+    shiftedScancodeToASCII[sc_9] = '(';
+    shiftedScancodeToASCII[sc_Comma] = '<';
+    shiftedScancodeToASCII[sc_Period] = '>';
+    shiftedScancodeToASCII[sc_Kpad_Star] = '*';
+    shiftedScancodeToASCII[sc_Slash] = '?';
+    shiftedScancodeToASCII[sc_SemiColon] = ':';
+    shiftedScancodeToASCII[sc_Quote] = '\"';
+    shiftedScancodeToASCII[sc_Tilde] = '~';
+    shiftedScancodeToASCII[sc_BackSlash] = '|';
+    shiftedScancodeToASCII[sc_OpenBracket] = '{';
+    shiftedScancodeToASCII[sc_CloseBracket] = '}';
+    shiftedScancodeToASCII[sc_Minus] = '_';
+    shiftedScancodeToASCII[sc_Equals] = '+';
+    shiftedScancodeToASCII[sc_Plus] = '+';
+    shiftedScancodeToASCII[sc_kpad_Minus] = '-';
+    shiftedScancodeToASCII[sc_kpad_Period] = '.';
+    shiftedScancodeToASCII[sc_kpad_Plus] = '+';
+
+    memset(extscanToSC, '\0', sizeof (extscanToSC));
+    
+    /* map extended keys to their Duke3D equivalents */
+    extscanToSC[0x1C] = sc_kpad_Enter;
+    extscanToSC[0x1D] = sc_RightControl;
+    extscanToSC[0x35] = sc_kpad_Slash;
+    extscanToSC[0x37] = sc_PrintScreen;
+    extscanToSC[0x38] = sc_RightAlt;
+    extscanToSC[0x47] = sc_Home;
+    extscanToSC[0x48] = sc_UpArrow;
+    extscanToSC[0x49] = sc_PgUp;
+    extscanToSC[0x4B] = sc_LeftArrow;
+    extscanToSC[0x4D] = sc_RightArrow;
+    extscanToSC[0x4F] = sc_End;
+    extscanToSC[0x50] = sc_DownArrow;
+    extscanToSC[0x51] = sc_PgDn;
+    extscanToSC[0x52] = sc_Insert;
+    extscanToSC[0x53] = sc_Delete;
+    
+	KB_ClearKeysDown();
+}
+
+void KB_Shutdown( void )
+{
+	KB_ClearKeysDown();
+}
+
--- /dev/null
+++ b/Game/src/keyboard.h
@@ -1,0 +1,224 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef _keyboard_public
+#define _keyboard_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+=============================================================================
+
+                                    DEFINES
+
+=============================================================================
+*/
+
+typedef uint8 kb_scancode;
+
+#define  sc_None         0
+#define  sc_Bad          0xff
+#define  sc_Comma        0x33
+#define  sc_Period       0x34
+#define  sc_Return       0x1c
+#define  sc_Enter        sc_Return
+#define  sc_Escape       0x01
+#define  sc_Space        0x39
+#define  sc_BackSpace    0x0e
+#define  sc_Tab          0x0f
+#define  sc_LeftAlt      0x38
+#define  sc_LeftControl  0x1d
+#define  sc_CapsLock     0x3a
+#define  sc_LeftShift    0x2a
+#define  sc_RightShift   0x36
+#define  sc_F1           0x3b
+#define  sc_F2           0x3c
+#define  sc_F3           0x3d
+#define  sc_F4           0x3e
+#define  sc_F5           0x3f
+#define  sc_F6           0x40
+#define  sc_F7           0x41
+#define  sc_F8           0x42
+#define  sc_F9           0x43
+#define  sc_F10          0x44
+#define  sc_F11          0x57
+#define  sc_F12          0x58
+#define  sc_Kpad_Star    0x37
+#define  sc_Pause        0x59
+#define  sc_ScrollLock   0x46
+#define  sc_NumLock      0x45
+#define  sc_Slash        0x35
+#define  sc_SemiColon    0x27
+#define  sc_Quote        0x28
+#define  sc_Tilde        0x29
+#define  sc_BackSlash    0x2b
+
+#define  sc_OpenBracket  0x1a
+#define  sc_CloseBracket 0x1b
+
+#define  sc_1            0x02
+#define  sc_2            0x03
+#define  sc_3            0x04
+#define  sc_4            0x05
+#define  sc_5            0x06
+#define  sc_6            0x07
+#define  sc_7            0x08
+#define  sc_8            0x09
+#define  sc_9            0x0a
+#define  sc_0            0x0b
+#define  sc_Minus        0x0c
+#define  sc_Equals       0x0d
+#define  sc_Plus         0x0d
+
+#define  sc_kpad_1       0x4f
+#define  sc_kpad_2       0x50
+#define  sc_kpad_3       0x51
+#define  sc_kpad_4       0x4b
+#define  sc_kpad_5       0x4c
+#define  sc_kpad_6       0x4d
+#define  sc_kpad_7       0x47
+#define  sc_kpad_8       0x48
+#define  sc_kpad_9       0x49
+#define  sc_kpad_0       0x52
+#define  sc_kpad_Minus   0x4a
+#define  sc_kpad_Plus    0x4e
+#define  sc_kpad_Period  0x53
+
+#define  sc_A            0x1e
+#define  sc_B            0x30
+#define  sc_C            0x2e
+#define  sc_D            0x20
+#define  sc_E            0x12
+#define  sc_F            0x21
+#define  sc_G            0x22
+#define  sc_H            0x23
+#define  sc_I            0x17
+#define  sc_J            0x24
+#define  sc_K            0x25
+#define  sc_L            0x26
+#define  sc_M            0x32
+#define  sc_N            0x31
+#define  sc_O            0x18
+#define  sc_P            0x19
+#define  sc_Q            0x10
+#define  sc_R            0x13
+#define  sc_S            0x1f
+#define  sc_T            0x14
+#define  sc_U            0x16
+#define  sc_V            0x2f
+#define  sc_W            0x11
+#define  sc_X            0x2d
+#define  sc_Y            0x15
+#define  sc_Z            0x2c
+
+// Extended scan codes
+
+#define  sc_UpArrow      0x5a
+#define  sc_DownArrow    0x6a
+#define  sc_LeftArrow    0x6b
+#define  sc_RightArrow   0x6c
+#define  sc_Insert       0x5e
+#define  sc_Delete       0x5f
+#define  sc_Home         0x61
+#define  sc_End          0x62
+#define  sc_PgUp         0x63
+#define  sc_PgDn         0x64
+#define  sc_RightAlt     0x65
+#define  sc_RightControl 0x66
+#define  sc_kpad_Slash   0x67
+#define  sc_kpad_Enter   0x68
+#define  sc_PrintScreen  0x69
+#define  sc_LastScanCode 0x6e
+
+// Ascii scan codes
+
+#define  asc_Enter       13
+#define  asc_Escape      27
+#define  asc_BackSpace   8
+#define  asc_Tab         9
+#define  asc_Space       32
+
+#define MAXKEYBOARDSCAN  128
+
+
+/*
+=============================================================================
+
+                               GLOBAL VARIABLES
+
+=============================================================================
+*/
+
+extern volatile byte  KB_KeyDown[ MAXKEYBOARDSCAN ];   // Keyboard state array
+extern volatile kb_scancode KB_LastScan;
+
+
+/*
+=============================================================================
+
+                                    MACROS
+
+=============================================================================
+*/
+
+#define KB_GetLastScanCode()    ( KB_LastScan )
+
+#define KB_SetLastScanCode( scancode ) { KB_LastScan = ( scancode ); }
+
+#define KB_ClearLastScanCode() { KB_SetLastScanCode( sc_None ); }
+
+#define KB_KeyPressed( scan )  ( KB_KeyDown[ ( scan ) ] != 0 )
+
+#define KB_ClearKeyDown( scan ) { KB_KeyDown[ ( scan ) ] = false; }
+
+#define KB_SetKeyDown( scan ) { KB_KeyDown[ ( scan ) ] = true; }
+/*
+=============================================================================
+
+                              FUNCTION PROTOTYPES
+
+=============================================================================
+*/
+
+void KB_KeyEvent( int scancode, boolean keypressed );  // Interprets scancodes
+boolean KB_KeyWaiting( void );         // Checks if a character is waiting in the keyboard queue
+char    KB_Getch( void );              // Gets the next keypress
+void    KB_Addch( char ch );           // Adds key to end of queue
+void    KB_FlushKeyboardQueue( void ); // Empties the keyboard queue of all waiting characters.
+void    KB_ClearKeysDown( void );      // Clears all keys down flags.
+char *  KB_ScanCodeToString( kb_scancode scancode ); // convert scancode into a string
+kb_scancode KB_StringToScanCode( char * string );  // convert a string into a scancode
+void    KB_TurnKeypadOn( void );       // turn the keypad on
+void    KB_TurnKeypadOff( void );      // turn the keypad off
+boolean KB_KeypadActive( void );       // check whether keypad is active
+void    KB_Startup( void );
+void    KB_Shutdown( void );
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/menues.c
@@ -1,0 +1,4720 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "mouse.h"
+#include "animlib.h"
+#include "control.h"
+#include "cache1d.h"
+#include "SDL.h"
+
+extern SDL_Surface *surface;
+extern short inputloc;
+extern int recfilep;
+extern char vgacompatible;
+short probey=0,lastprobey=0,last_menu,globalskillsound=-1;
+short sh,onbar,buttonstat,deletespot;
+short last_zero,last_fifty,last_threehundred = 0;
+static char fileselect = 1, menunamecnt, menuname[256][17], curpath[80], menupath[80];
+
+// File tree info
+//
+char szCurrentDirectory[1024] = {'\0'};
+
+#define FILETYPE_DIRECTORY 0
+#define FILETYPE_FILE      1
+
+typedef struct fileentry
+{
+	char filename[16];
+	int type;
+}FILEENTRY;
+
+typedef struct filelist
+{
+	FILEENTRY *files;
+}FILELIST;
+
+FILELIST m_Files;// = NULL;
+
+//
+//
+
+#define MENU_SELECT_EPISODE	100
+#define	MENU_USER_MAP		101
+
+
+// CTW - REMOVED
+/* Error codes */
+/*
+#define eTenBnNotInWindows 3801
+#define eTenBnBadGameIni 3802
+#define eTenBnBadTenIni 3803
+#define eTenBnBrowseCancel 3804
+#define eTenBnBadTenInst 3805
+
+int  tenBnStart(void);
+void tenBnSetBrowseRtn(char *(*rtn)(char *str, int len));
+void tenBnSetExitRtn(void (*rtn)(void));
+void tenBnSetEndRtn(void (*rtn)(void));*/
+// CTW END - REMOVED
+
+void dummyfunc(void)
+{
+}
+
+void dummymess(int i,char *c)
+{
+}
+
+// CTW - REMOVED
+/*
+void TENtext(void)
+{
+    long dacount,dalastcount;
+
+    puts("\nDuke Nukem 3D has been licensed exclusively to TEN (Total");
+    puts("Entertainment Network) for wide-area networked (WAN) multiplayer");
+    puts("games.\n");
+
+    puts("The multiplayer code within Duke Nukem 3D has been highly");
+    puts("customized to run best on TEN, where you'll experience fast and");
+    puts("stable performance, plus other special benefits.\n");
+
+    puts("We do not authorize or recommend the use of Duke Nukem 3D with");
+    puts("gaming services other than TEN.\n");
+
+    puts("Duke Nukem 3D is protected by United States copyright law and");
+    puts("international treaty.\n");
+
+    puts("For the best online multiplayer gaming experience, please call TEN");
+    puts("at 800-8040-TEN, or visit TEN's Web Site at www.ten.net.\n");
+
+    puts("Press any key to continue.\n");
+
+    _bios_timeofday(0,&dacount);
+
+    while( _bios_keybrd(1) == 0 )
+    {
+        _bios_timeofday(0,&dalastcount);
+        if( (dacount+240) < dalastcount ) break;
+    }
+}
+*/
+// CTW END - REMOVED
+
+void cmenu(short cm)
+{
+    current_menu = cm;
+
+    if( (cm >= 1000 && cm <= 1010) )
+        return;
+
+    if( cm == 0 )
+        probey = last_zero;
+    else if(cm == 50)
+        probey = last_fifty;
+    else if(cm >= 300 && cm < 400)
+        probey = last_threehundred;
+    else if(cm == 110)
+        probey = 1;
+    else probey = 0;
+    lastprobey = -1;
+}
+
+
+void savetemp(char *fn,long daptr,long dasiz)
+{
+    int fp;
+
+    fp = open(fn,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+
+    write(fp,(char *)daptr,dasiz);
+
+    close(fp);
+}
+
+void getangplayers(short snum)
+{
+    short i,a;
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+    {
+        if(i != snum)
+        {
+            a = ps[snum].ang+getangle(ps[i].posx-ps[snum].posx,ps[i].posy-ps[snum].posy);
+            a = a-1024;
+            rotatesprite(
+                (320<<15) + (((sintable[(a+512)&2047])>>7)<<15),
+                (320<<15) - (((sintable[a&2047])>>8)<<15),
+                klabs(sintable[((a>>1)+768)&2047]<<2),0,APLAYER,0,ps[i].palookup,0,0,0,xdim-1,ydim-1);
+        }
+    }
+}
+
+static int loadpheader(char spot,int32 *vn,int32 *ln,int32 *psk,int32 *nump)
+{
+
+     long i;
+         char fn[] = "game0.sav";
+         long fil;
+     long bv;
+
+         fn[4] = spot+'0';
+
+     if ((fil = TCkopen4load(fn,0)) == -1) return(-1);
+
+     walock[MAXTILES-3] = 255;
+
+     kdfread(&bv,4,1,fil);
+     if(bv != BYTEVERSION)
+     {
+        FTA(114,&ps[myconnectindex],1);
+        kclose(fil);
+        return 1;
+     }
+
+     kdfread(nump,sizeof(int32),1,fil);
+
+     kdfread(tempbuf,19,1,fil);
+         kdfread(vn,sizeof(int32),1,fil);
+         kdfread(ln,sizeof(int32),1,fil);
+     kdfread(psk,sizeof(int32),1,fil);
+
+     if (waloff[MAXTILES-3] == 0) allocache(&waloff[MAXTILES-3],160*100,&walock[MAXTILES-3]);
+     tilesizx[MAXTILES-3] = 100; tilesizy[MAXTILES-3] = 160;
+     kdfread((char *)waloff[MAXTILES-3],160,100,fil);
+
+         kclose(fil);
+
+         return(0);
+}
+
+
+int loadplayer(signed char spot)
+{
+     short k,music_changed;
+     char fn[] = "game0.sav";
+     char mpfn[] = "gameA_00.sav";
+     char *fnptr, scriptptrs[MAXSCRIPTSIZE];
+     long fil, bv, i, j, x;
+     int32 nump;
+
+     if(spot < 0)
+     {
+        multiflag = 1;
+        multiwhat = 0;
+        multipos = -spot-1;
+        return -1;
+     }
+
+     if( multiflag == 2 && multiwho != myconnectindex )
+     {
+         fnptr = mpfn;
+         mpfn[4] = spot + 'A';
+
+         if(ud.multimode > 9)
+         {
+             mpfn[6] = (multiwho/10) + '0';
+             mpfn[7] = (multiwho%10) + '0';
+         }
+         else mpfn[7] = multiwho + '0';
+     }
+     else
+     {
+        fnptr = fn;
+        fn[4] = spot + '0';
+     }
+
+     if ((fil = TCkopen4load(fnptr,0)) == -1) return(-1);
+
+	 if(ud.recstat != 2)
+		ready2send = 0;
+
+     kdfread(&bv,4,1,fil);
+     if(bv != BYTEVERSION)
+     {
+        FTA(114,&ps[myconnectindex],1);
+        kclose(fil);
+		if(ud.recstat != 2)
+		{	
+			// FIX_00084: Various bugs in the load game (single player) option if ESC is hit or if wrong version 
+			ototalclock = totalclock;
+			ready2send = 1;
+		}
+        return 1;
+     }
+
+     kdfread(&nump,sizeof(nump),1,fil);
+     if(nump != numplayers)
+     {
+        kclose(fil);
+		if(ud.recstat != 2)
+		{
+			// FIX_00084: Various bugs in the load game (single player) option if ESC is hit or if wrong version 
+	        ototalclock = totalclock;
+	        ready2send = 1;
+		}
+        FTA(124,&ps[myconnectindex],1);
+        return 1;
+     }
+
+     if(numplayers > 1)
+     {
+         pub = NUMPAGES;
+         pus = NUMPAGES;
+         vscrn();
+         drawbackground();
+         menutext(160,100,0,0,"LOADING...");
+         nextpage();
+    }
+
+     waitforeverybody();
+
+         FX_StopAllSounds();
+     clearsoundlocks();
+         MUSIC_StopSong();
+
+     if(numplayers > 1)
+         kdfread(&buf,19,1,fil);
+     else
+         kdfread(&ud.savegame[spot][0],19,1,fil);
+
+     music_changed = (music_select != (ud.volume_number*11) + ud.level_number);
+
+         kdfread(&ud.volume_number,sizeof(ud.volume_number),1,fil);
+         kdfread(&ud.level_number,sizeof(ud.level_number),1,fil);
+         kdfread(&ud.player_skill,sizeof(ud.player_skill),1,fil);
+
+         ud.m_level_number = ud.level_number;
+         ud.m_volume_number = ud.volume_number;
+         ud.m_player_skill = ud.player_skill;
+
+                 //Fake read because lseek won't work with compression
+     walock[MAXTILES-3] = 1;
+     if (waloff[MAXTILES-3] == 0) allocache(&waloff[MAXTILES-3],160*100,&walock[MAXTILES-3]);
+     tilesizx[MAXTILES-3] = 100; tilesizy[MAXTILES-3] = 160;
+     kdfread((char *)waloff[MAXTILES-3],160,100,fil);
+
+         kdfread(&numwalls,2,1,fil);
+     kdfread(&wall[0],sizeof(walltype),MAXWALLS,fil);
+         kdfread(&numsectors,2,1,fil);
+     kdfread(&sector[0],sizeof(sectortype),MAXSECTORS,fil);
+         kdfread(&sprite[0],sizeof(spritetype),MAXSPRITES,fil);
+         kdfread(&headspritesect[0],2,MAXSECTORS+1,fil);
+         kdfread(&prevspritesect[0],2,MAXSPRITES,fil);
+         kdfread(&nextspritesect[0],2,MAXSPRITES,fil);
+         kdfread(&headspritestat[0],2,MAXSTATUS+1,fil);
+         kdfread(&prevspritestat[0],2,MAXSPRITES,fil);
+         kdfread(&nextspritestat[0],2,MAXSPRITES,fil);
+         kdfread(&numcyclers,sizeof(numcyclers),1,fil);
+         kdfread(&cyclers[0][0],12,MAXCYCLERS,fil);
+     kdfread(ps,sizeof(ps),1,fil);
+     kdfread(po,sizeof(po),1,fil);
+         kdfread(&numanimwalls,sizeof(numanimwalls),1,fil);
+         kdfread(&animwall,sizeof(animwall),1,fil);
+         kdfread(&msx[0],sizeof(long),sizeof(msx)/sizeof(long),fil);
+         kdfread(&msy[0],sizeof(long),sizeof(msy)/sizeof(long),fil);
+     kdfread((short *)&spriteqloc,sizeof(short),1,fil);
+     kdfread((short *)&spriteqamount,sizeof(short),1,fil);
+     kdfread((short *)&spriteq[0],sizeof(short),spriteqamount,fil);
+         kdfread(&mirrorcnt,sizeof(short),1,fil);
+         kdfread(&mirrorwall[0],sizeof(short),64,fil);
+     kdfread(&mirrorsector[0],sizeof(short),64,fil);
+     kdfread(&show2dsector[0],sizeof(char),MAXSECTORS>>3,fil);
+     kdfread(&actortype[0],sizeof(char),MAXTILES,fil);
+     kdfread(&boardfilename[0],sizeof(boardfilename),1,fil);
+
+     kdfread(&numclouds,sizeof(numclouds),1,fil);
+     kdfread(&clouds[0],sizeof(short)<<7,1,fil);
+     kdfread(&cloudx[0],sizeof(short)<<7,1,fil);
+     kdfread(&cloudy[0],sizeof(short)<<7,1,fil);
+
+     kdfread(&scriptptrs[0],1,MAXSCRIPTSIZE,fil);
+     kdfread(&script[0],4,MAXSCRIPTSIZE,fil);
+     for(i=0;i<MAXSCRIPTSIZE;i++)
+        if( scriptptrs[i] )
+     {
+         j = (long)script[i]+(long)&script[0];
+         script[i] = j;
+     }
+
+     kdfread(&actorscrptr[0],4,MAXTILES,fil);
+     for(i=0;i<MAXTILES;i++)
+         if(actorscrptr[i])
+     {
+        j = (long)actorscrptr[i]+(long)&script[0];
+        actorscrptr[i] = (long *)j;
+     }
+
+     kdfread(&scriptptrs[0],1,MAXSPRITES,fil);
+     kdfread(&hittype[0],sizeof(struct weaponhit),MAXSPRITES,fil);
+
+     for(i=0;i<MAXSPRITES;i++)
+     {
+        j = (long)(&script[0]);
+        if( scriptptrs[i]&1 ) T2 += j;
+        if( scriptptrs[i]&2 ) T5 += j;
+        if( scriptptrs[i]&4 ) T6 += j;
+     }
+
+         kdfread(&lockclock,sizeof(lockclock),1,fil);
+     kdfread(&pskybits,sizeof(pskybits),1,fil);
+     kdfread(&pskyoff[0],sizeof(pskyoff[0]),MAXPSKYTILES,fil);
+
+         kdfread(&animatecnt,sizeof(animatecnt),1,fil);
+         kdfread(&animatesect[0],2,MAXANIMATES,fil);
+         kdfread(&animateptr[0],4,MAXANIMATES,fil);
+     for(i = animatecnt-1;i>=0;i--) animateptr[i] = (long *)((long)animateptr[i]+(long)(&sector[0]));
+         kdfread(&animategoal[0],4,MAXANIMATES,fil);
+         kdfread(&animatevel[0],4,MAXANIMATES,fil);
+
+         kdfread(&earthquaketime,sizeof(earthquaketime),1,fil);
+     kdfread(&ud.from_bonus,sizeof(ud.from_bonus),1,fil);
+     kdfread(&ud.secretlevel,sizeof(ud.secretlevel),1,fil);
+     kdfread(&ud.respawn_monsters,sizeof(ud.respawn_monsters),1,fil);
+     ud.m_respawn_monsters = ud.respawn_monsters;
+     kdfread(&ud.respawn_items,sizeof(ud.respawn_items),1,fil);
+     ud.m_respawn_items = ud.respawn_items;
+     kdfread(&ud.respawn_inventory,sizeof(ud.respawn_inventory),1,fil);
+     ud.m_respawn_inventory = ud.respawn_inventory;
+
+     kdfread(&ud.god,sizeof(ud.god),1,fil);
+     kdfread(&ud.auto_run,sizeof(ud.auto_run),1,fil);
+     kdfread(&ud.crosshair,sizeof(ud.crosshair),1,fil);
+     kdfread(&ud.monsters_off,sizeof(ud.monsters_off),1,fil);
+     ud.m_monsters_off = ud.monsters_off;
+     kdfread(&ud.last_level,sizeof(ud.last_level),1,fil);
+     kdfread(&ud.eog,sizeof(ud.eog),1,fil);
+
+     kdfread(&ud.coop,sizeof(ud.coop),1,fil);
+     ud.m_coop = ud.coop;
+     kdfread(&ud.marker,sizeof(ud.marker),1,fil);
+     ud.m_marker = ud.marker;
+     kdfread(&ud.ffire,sizeof(ud.ffire),1,fil);
+     ud.m_ffire = ud.ffire;
+
+     kdfread(&camsprite,sizeof(camsprite),1,fil);
+     kdfread(&connecthead,sizeof(connecthead),1,fil);
+     kdfread(connectpoint2,sizeof(connectpoint2),1,fil);
+     kdfread(&numplayersprites,sizeof(numplayersprites),1,fil);
+     kdfread((short *)&frags[0][0],sizeof(frags),1,fil);
+
+     kdfread(&randomseed,sizeof(randomseed),1,fil);
+     kdfread(&global_random,sizeof(global_random),1,fil);
+     kdfread(&parallaxyscale,sizeof(parallaxyscale),1,fil);
+
+     kclose(fil);
+
+     if(ps[myconnectindex].over_shoulder_on != 0)
+     {
+         cameradist = 0;
+         cameraclock = 0;
+         ps[myconnectindex].over_shoulder_on = 1;
+     }
+
+     screenpeek = myconnectindex;
+
+     clearbufbyte(gotpic,sizeof(gotpic),0L);
+     clearsoundlocks();
+         cacheit();
+     docacheit();
+
+     if(music_changed == 0)
+        music_select = (ud.volume_number*11) + ud.level_number;
+     playmusic(&music_fn[0][music_select][0]);
+
+     ps[myconnectindex].gm = MODE_GAME;
+         ud.recstat = 0;
+
+     if(ps[myconnectindex].jetpack_on)
+         spritesound(DUKE_JETPACK_IDLE,ps[myconnectindex].i);
+
+     restorepalette = 1;
+     setpal(&ps[myconnectindex]);
+     vscrn();
+
+     FX_SetReverb(0);
+
+     if(ud.lockout == 0)
+     {
+         for(x=0;x<numanimwalls;x++)
+             if( wall[animwall[x].wallnum].extra >= 0 )
+                 wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
+     }
+     else
+     {
+         for(x=0;x<numanimwalls;x++)
+             switch(wall[animwall[x].wallnum].picnum)
+         {
+             case FEMPIC1:
+                 wall[animwall[x].wallnum].picnum = BLANKSCREEN;
+                 break;
+             case FEMPIC2:
+             case FEMPIC3:
+                 wall[animwall[x].wallnum].picnum = SCREENBREAK6;
+                 break;
+         }
+     }
+
+     numinterpolations = 0;
+     startofdynamicinterpolations = 0;
+
+     k = headspritestat[3];
+     while(k >= 0)
+     {
+        switch(sprite[k].lotag)
+        {
+            case 31:
+                setinterpolation(&sector[sprite[k].sectnum].floorz);
+                break;
+            case 32:
+                setinterpolation(&sector[sprite[k].sectnum].ceilingz);
+                break;
+            case 25:
+                setinterpolation(&sector[sprite[k].sectnum].floorz);
+                setinterpolation(&sector[sprite[k].sectnum].ceilingz);
+                break;
+            case 17:
+                setinterpolation(&sector[sprite[k].sectnum].floorz);
+                setinterpolation(&sector[sprite[k].sectnum].ceilingz);
+                break;
+            case 0:
+            case 5:
+            case 6:
+            case 11:
+            case 14:
+            case 15:
+            case 16:
+            case 26:
+            case 30:
+                setsectinterpolate(k);
+                break;
+        }
+
+        k = nextspritestat[k];
+     }
+
+     for(i=numinterpolations-1;i>=0;i--) bakipos[i] = *curipos[i];
+     for(i = animatecnt-1;i>=0;i--)
+         setinterpolation(animateptr[i]);
+
+     show_shareware = 0;
+     everyothertime = 0;
+
+     clearbufbyte(playerquitflag,MAXPLAYERS,0x01010101);
+
+     resetmys();
+
+     ready2send = 1;
+
+     flushpackets();
+     clearfifo();
+     waitforeverybody();
+
+     resettimevars();
+
+     return(0);
+}
+
+int saveplayer(signed char spot)
+{
+     long i, j;
+         char fn[] = "game0.sav";
+     char mpfn[] = "gameA_00.sav";
+     char *fnptr,scriptptrs[MAXSCRIPTSIZE];
+         FILE *fil;
+     long bv = BYTEVERSION;
+	 char fullpathsavefilename[16];
+
+     if(spot < 0)
+     {
+        multiflag = 1;
+        multiwhat = 1;
+        multipos = -spot-1;
+        return -1;
+     }
+
+     waitforeverybody();
+
+     if( multiflag == 2 && multiwho != myconnectindex )
+     {
+         fnptr = mpfn;
+         mpfn[4] = spot + 'A';
+
+         if(ud.multimode > 9)
+         {
+             mpfn[6] = (multiwho/10) + '0';
+             mpfn[7] = multiwho + '0';
+         }
+         else mpfn[7] = multiwho + '0';
+     }
+     else
+     {
+        fnptr = fn;
+        fn[4] = spot + '0';
+     }
+
+
+	// Are we loading a TC?
+	if(game_dir[0] != '\0')
+	{
+		// Yes
+		sprintf(fullpathsavefilename, "%s\\%s", game_dir, fnptr);
+	}
+	else
+	{
+		// No 
+		sprintf(fullpathsavefilename, "%s", fnptr);
+	}
+
+     if ((fil = fopen(fullpathsavefilename,"wb")) == 0) return(-1);
+
+     ready2send = 0;
+
+     dfwrite(&bv,4,1,fil);
+     dfwrite(&ud.multimode,sizeof(ud.multimode),1,fil);
+
+         dfwrite(&ud.savegame[spot][0],19,1,fil);
+         dfwrite(&ud.volume_number,sizeof(ud.volume_number),1,fil);
+     dfwrite(&ud.level_number,sizeof(ud.level_number),1,fil);
+         dfwrite(&ud.player_skill,sizeof(ud.player_skill),1,fil);
+     dfwrite((char *)waloff[MAXTILES-1],160,100,fil);
+
+         dfwrite(&numwalls,2,1,fil);
+     dfwrite(&wall[0],sizeof(walltype),MAXWALLS,fil);
+         dfwrite(&numsectors,2,1,fil);
+     dfwrite(&sector[0],sizeof(sectortype),MAXSECTORS,fil);
+         dfwrite(&sprite[0],sizeof(spritetype),MAXSPRITES,fil);
+         dfwrite(&headspritesect[0],2,MAXSECTORS+1,fil);
+         dfwrite(&prevspritesect[0],2,MAXSPRITES,fil);
+         dfwrite(&nextspritesect[0],2,MAXSPRITES,fil);
+         dfwrite(&headspritestat[0],2,MAXSTATUS+1,fil);
+         dfwrite(&prevspritestat[0],2,MAXSPRITES,fil);
+         dfwrite(&nextspritestat[0],2,MAXSPRITES,fil);
+         dfwrite(&numcyclers,sizeof(numcyclers),1,fil);
+         dfwrite(&cyclers[0][0],12,MAXCYCLERS,fil);
+     dfwrite(ps,sizeof(ps),1,fil);
+     dfwrite(po,sizeof(po),1,fil);
+         dfwrite(&numanimwalls,sizeof(numanimwalls),1,fil);
+         dfwrite(&animwall,sizeof(animwall),1,fil);
+         dfwrite(&msx[0],sizeof(long),sizeof(msx)/sizeof(long),fil);
+         dfwrite(&msy[0],sizeof(long),sizeof(msy)/sizeof(long),fil);
+     dfwrite(&spriteqloc,sizeof(short),1,fil);
+     dfwrite(&spriteqamount,sizeof(short),1,fil);
+     dfwrite(&spriteq[0],sizeof(short),spriteqamount,fil);
+         dfwrite(&mirrorcnt,sizeof(short),1,fil);
+         dfwrite(&mirrorwall[0],sizeof(short),64,fil);
+         dfwrite(&mirrorsector[0],sizeof(short),64,fil);
+     dfwrite(&show2dsector[0],sizeof(char),MAXSECTORS>>3,fil);
+     dfwrite(&actortype[0],sizeof(char),MAXTILES,fil);
+     dfwrite(&boardfilename[0],sizeof(boardfilename),1,fil);
+
+     dfwrite(&numclouds,sizeof(numclouds),1,fil);
+     dfwrite(&clouds[0],sizeof(short)<<7,1,fil);
+     dfwrite(&cloudx[0],sizeof(short)<<7,1,fil);
+     dfwrite(&cloudy[0],sizeof(short)<<7,1,fil);
+
+     for(i=0;i<MAXSCRIPTSIZE;i++)
+     {
+          if( (long)script[i] >= (long)(&script[0]) && (long)script[i] < (long)(&script[MAXSCRIPTSIZE]) )
+          {
+                scriptptrs[i] = 1;
+                j = (long)script[i] - (long)&script[0];
+                script[i] = j;
+          }
+          else scriptptrs[i] = 0;
+     }
+
+     dfwrite(&scriptptrs[0],1,MAXSCRIPTSIZE,fil);
+     dfwrite(&script[0],4,MAXSCRIPTSIZE,fil);
+
+     for(i=0;i<MAXSCRIPTSIZE;i++)
+        if( scriptptrs[i] )
+     {
+        j = script[i]+(long)&script[0];
+        script[i] = j;
+     }
+
+     for(i=0;i<MAXTILES;i++)
+         if(actorscrptr[i])
+     {
+        j = (long)actorscrptr[i]-(long)&script[0];
+        actorscrptr[i] = (long *)j;
+     }
+     dfwrite(&actorscrptr[0],4,MAXTILES,fil);
+     for(i=0;i<MAXTILES;i++)
+         if(actorscrptr[i])
+     {
+         j = (long)actorscrptr[i]+(long)&script[0];
+         actorscrptr[i] = (long *)j;
+     }
+
+     for(i=0;i<MAXSPRITES;i++)
+     {
+        scriptptrs[i] = 0;
+
+        if(actorscrptr[PN] == 0) continue;
+
+        j = (long)&script[0];
+
+        if(T2 >= j && T2 < (long)(&script[MAXSCRIPTSIZE]) )
+        {
+            scriptptrs[i] |= 1;
+            T2 -= j;
+        }
+        if(T5 >= j && T5 < (long)(&script[MAXSCRIPTSIZE]) )
+        {
+            scriptptrs[i] |= 2;
+            T5 -= j;
+        }
+        if(T6 >= j && T6 < (long)(&script[MAXSCRIPTSIZE]) )
+        {
+            scriptptrs[i] |= 4;
+            T6 -= j;
+        }
+    }
+
+    dfwrite(&scriptptrs[0],1,MAXSPRITES,fil);
+    dfwrite(&hittype[0],sizeof(struct weaponhit),MAXSPRITES,fil);
+
+    for(i=0;i<MAXSPRITES;i++)
+    {
+        if(actorscrptr[PN] == 0) continue;
+        j = (long)&script[0];
+
+        if(scriptptrs[i]&1)
+            T2 += j;
+        if(scriptptrs[i]&2)
+            T5 += j;
+        if(scriptptrs[i]&4)
+            T6 += j;
+    }
+
+         dfwrite(&lockclock,sizeof(lockclock),1,fil);
+     dfwrite(&pskybits,sizeof(pskybits),1,fil);
+     dfwrite(&pskyoff[0],sizeof(pskyoff[0]),MAXPSKYTILES,fil);
+         dfwrite(&animatecnt,sizeof(animatecnt),1,fil);
+         dfwrite(&animatesect[0],2,MAXANIMATES,fil);
+         for(i = animatecnt-1;i>=0;i--) animateptr[i] = (long *)((long)animateptr[i]-(long)(&sector[0]));
+         dfwrite(&animateptr[0],4,MAXANIMATES,fil);
+         for(i = animatecnt-1;i>=0;i--) animateptr[i] = (long *)((long)animateptr[i]+(long)(&sector[0]));
+         dfwrite(&animategoal[0],4,MAXANIMATES,fil);
+         dfwrite(&animatevel[0],4,MAXANIMATES,fil);
+
+         dfwrite(&earthquaketime,sizeof(earthquaketime),1,fil);
+         dfwrite(&ud.from_bonus,sizeof(ud.from_bonus),1,fil);
+     dfwrite(&ud.secretlevel,sizeof(ud.secretlevel),1,fil);
+     dfwrite(&ud.respawn_monsters,sizeof(ud.respawn_monsters),1,fil);
+     dfwrite(&ud.respawn_items,sizeof(ud.respawn_items),1,fil);
+     dfwrite(&ud.respawn_inventory,sizeof(ud.respawn_inventory),1,fil);
+     dfwrite(&ud.god,sizeof(ud.god),1,fil);
+     dfwrite(&ud.auto_run,sizeof(ud.auto_run),1,fil);
+     dfwrite(&ud.crosshair,sizeof(ud.crosshair),1,fil);
+     dfwrite(&ud.monsters_off,sizeof(ud.monsters_off),1,fil);
+     dfwrite(&ud.last_level,sizeof(ud.last_level),1,fil);
+     dfwrite(&ud.eog,sizeof(ud.eog),1,fil);
+     dfwrite(&ud.coop,sizeof(ud.coop),1,fil);
+     dfwrite(&ud.marker,sizeof(ud.marker),1,fil);
+     dfwrite(&ud.ffire,sizeof(ud.ffire),1,fil);
+     dfwrite(&camsprite,sizeof(camsprite),1,fil);
+     dfwrite(&connecthead,sizeof(connecthead),1,fil);
+     dfwrite(connectpoint2,sizeof(connectpoint2),1,fil);
+     dfwrite(&numplayersprites,sizeof(numplayersprites),1,fil);
+     dfwrite((short *)&frags[0][0],sizeof(frags),1,fil);
+
+     dfwrite(&randomseed,sizeof(randomseed),1,fil);
+     dfwrite(&global_random,sizeof(global_random),1,fil);
+     dfwrite(&parallaxyscale,sizeof(parallaxyscale),1,fil);
+
+         fclose(fil);
+
+     if(ud.multimode < 2)
+     {
+         strcpy(fta_quotes[122],"GAME SAVED");
+         FTA(122,&ps[myconnectindex],1);
+     }
+
+     ready2send = 1;
+
+     waitforeverybody();
+
+     ototalclock = totalclock;
+
+     return(0);
+}
+
+#define LMB (buttonstat&1)
+#define RMB (buttonstat&2)
+
+// FIX_00036: Mouse wheel can now be used in menu
+#define WHEELUP (buttonstat&8)
+#define WHEELDOWN (buttonstat&16)
+
+ControlInfo minfo;
+
+int probe(int x,int y,int i,int n)
+{
+	return( probeXduke(x, y, i, n, 65536L) );
+}
+
+int probeXduke(int x,int y,int i,int n, long spriteSize)
+{
+    short centre;
+	long mouseY;
+	int32 mouseSens;
+
+	static long delay_counter_up = 0, delay_counter_down = 0, delay_up = 50, delay_down = 50;
+	static long mi = 0;
+
+	// FIX_00075: Bad Sensitivity along Y axis when using mouse in menu (Thanks to Turrican)
+	mouseSens = CONTROL_GetMouseSensitivity_Y();
+	mouseSens = mouseSens ? mouseSens : 1;
+
+    if( ((ControllerType == controltype_keyboardandmouse)||
+		(ControllerType == controltype_joystickandmouse)) )
+		//&& CONTROL_MousePresent )
+    {
+		memset(&minfo, 0, sizeof(ControlInfo));
+
+        CONTROL_GetInput( &minfo );
+		//mouseY = CONTROL_GetMouseY();
+		//mi = mouseY;
+        mi += minfo.dz / mouseSens;
+		mi += minfo.dpitch / mouseSens;
+    }
+
+    else minfo.dz = minfo.dyaw = 0;
+
+    if( x == (320>>1) )
+        centre = 320>>2;
+    else centre = 0;
+
+        if( KB_KeyPressed( sc_UpArrow ) || KB_KeyPressed( sc_PgUp ) || KB_KeyPressed( sc_kpad_8 ) ||
+            (mi < -1024) || WHEELUP)
+        {
+			// FIX_00060: Repeat key function was not working in the menu
+			if(delay_counter_up==0 || (totalclock-delay_counter_up)>delay_up || (mi < -1024) || WHEELUP)
+			{
+				mi = 0;
+				sound(KICK_HIT);
+
+				probey--;
+				if(probey < 0) probey = n-1;
+				minfo.dz = 0;
+				minfo.dpitch = 0;
+				if (delay_counter_up && (totalclock-delay_counter_up)>delay_up)
+					delay_up = 10;
+				delay_counter_up = totalclock;
+			}
+        }
+		else
+		{
+            KB_ClearKeyDown( sc_UpArrow );
+            KB_ClearKeyDown( sc_kpad_8 );
+            KB_ClearKeyDown( sc_PgUp );
+			delay_counter_up = 0;
+			delay_up = 50;
+		}
+
+        if( KB_KeyPressed( sc_DownArrow ) || KB_KeyPressed( sc_PgDn ) || KB_KeyPressed( sc_kpad_2 )
+            || (mi > 1024) || WHEELDOWN )
+        {
+			if(delay_counter_down==0 || (totalclock-delay_counter_down)>delay_down || (mi > 1024) || WHEELDOWN)
+			{
+				mi = 0;
+				sound(KICK_HIT);
+				probey++;
+				minfo.dz = 0;
+				minfo.dpitch = 0;
+				if (delay_counter_down && (totalclock-delay_counter_down)>delay_down)
+					delay_down = 10;
+				delay_counter_down = totalclock;
+			}
+        }
+		else
+		{
+			KB_ClearKeyDown( sc_DownArrow );
+			KB_ClearKeyDown( sc_kpad_2 );
+			KB_ClearKeyDown( sc_PgDn );
+			delay_counter_down = 0;
+			delay_down = 50;
+		}
+
+    if(probey >= n)
+        probey = 0;
+
+    if(centre)
+    {
+//        rotatesprite(((320>>1)+(centre)+54)<<16,(y+(probey*i)-4)<<16,65536L,0,SPINNINGNUKEICON+6-((6+(totalclock>>3))%7),sh,0,10,0,0,xdim-1,ydim-1);
+//        rotatesprite(((320>>1)-(centre)-54)<<16,(y+(probey*i)-4)<<16,65536L,0,SPINNINGNUKEICON+((totalclock>>3)%7),sh,0,10,0,0,xdim-1,ydim-1);
+
+        rotatesprite(((320>>1)+(centre>>1)+70)<<16,(y+(probey*i)-4)<<16,spriteSize,0,SPINNINGNUKEICON+6-((6+(totalclock>>3))%7),sh,0,10,0,0,xdim-1,ydim-1);
+        rotatesprite(((320>>1)-(centre>>1)-70)<<16,(y+(probey*i)-4)<<16,spriteSize,0,SPINNINGNUKEICON+((totalclock>>3)%7),sh,0,10,0,0,xdim-1,ydim-1);
+    }
+    else
+        rotatesprite((x-tilesizx[BIGFNTCURSOR]-4)<<16,(y+(probey*i)-4)<<16,spriteSize,0,SPINNINGNUKEICON+(((totalclock>>3))%7),sh,0,10,0,0,xdim-1,ydim-1);
+
+    if( KB_KeyPressed(sc_Space) || KB_KeyPressed( sc_kpad_Enter ) || KB_KeyPressed( sc_Enter ) || (LMB))// && !onbar) )
+    {
+        if(current_menu != 110)
+            sound(PISTOL_BODYHIT);
+        KB_ClearKeyDown( sc_Enter );
+        KB_ClearKeyDown( sc_Space );
+        KB_ClearKeyDown( sc_kpad_Enter );
+        return(probey);
+    }
+    else if( KB_KeyPressed( sc_Escape ) || (RMB) )
+    {
+        onbar = 0;
+        KB_ClearKeyDown( sc_Escape );
+        sound(EXITMENUSOUND);
+        return(-1);
+    }
+    else
+    {
+        if(onbar == 0) return(-probey-2);
+        if ( KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed( sc_kpad_4 ) || ((buttonstat&1) && minfo.dyaw < -128 ) )
+            return(probey);
+        else if ( KB_KeyPressed( sc_RightArrow ) || KB_KeyPressed( sc_kpad_6 ) || ((buttonstat&1) && minfo.dyaw > 128 ) )
+            return(probey);
+        else return(-probey-2);
+    }
+}
+
+int menutext(int x,int y,short s,short p,char *t)
+{
+    short i, ac, centre;
+
+    y -= 12;
+
+    i = centre = 0;
+
+    if( x == (320>>1) )
+    {
+        while( *(t+i) )
+        {
+            if(*(t+i) == ' ')
+            {
+                centre += 5;
+                i++;
+                continue;
+            }
+            ac = 0;
+            if(*(t+i) >= '0' && *(t+i) <= '9')
+                ac = *(t+i) - '0' + BIGALPHANUM-10;
+            else if(*(t+i) >= 'a' && *(t+i) <= 'z')
+                ac = toupper(*(t+i)) - 'A' + BIGALPHANUM;
+            else if(*(t+i) >= 'A' && *(t+i) <= 'Z')
+                ac = *(t+i) - 'A' + BIGALPHANUM;
+            else switch(*(t+i))
+            {
+                case '-':
+                    ac = BIGALPHANUM-11;
+                    break;
+                case '.':
+                    ac = BIGPERIOD;
+                    break;
+                case '\'':
+                    ac = BIGAPPOS;
+                    break;
+                case ',':
+                    ac = BIGCOMMA;
+                    break;
+                case '!':
+                    ac = BIGX;
+                    break;
+                case '?':
+                    ac = BIGQ;
+                    break;
+                case ';':
+                    ac = BIGSEMI;
+                    break;
+                case ':':
+                    ac = BIGSEMI;
+                    break;
+                default:
+                    centre += 5;
+                    i++;
+                    continue;
+            }
+
+            centre += tilesizx[ac]-1;
+            i++;
+        }
+    }
+
+    if(centre)
+        x = (320-centre-10)>>1;
+
+    while(*t)
+    {
+        if(*t == ' ') {x+=5;t++;continue;}
+        ac = 0;
+        if(*t >= '0' && *t <= '9')
+            ac = *t - '0' + BIGALPHANUM-10;
+        else if(*t >= 'a' && *t <= 'z')
+            ac = toupper(*t) - 'A' + BIGALPHANUM;
+        else if(*t >= 'A' && *t <= 'Z')
+            ac = *t - 'A' + BIGALPHANUM;
+        else switch(*t)
+        {
+            case '-':
+                ac = BIGALPHANUM-11;
+                break;
+            case '.':
+                ac = BIGPERIOD;
+                break;
+            case ',':
+                ac = BIGCOMMA;
+                break;
+            case '!':
+                ac = BIGX;
+                break;
+            case '\'':
+                ac = BIGAPPOS;
+                break;
+            case '?':
+                ac = BIGQ;
+                break;
+            case ';':
+                ac = BIGSEMI;
+                break;
+            case ':':
+                ac = BIGCOLIN;
+                break;
+            default:
+                x += 5;
+                t++;
+                continue;
+        }
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,10+16,0,0,xdim-1,ydim-1);
+
+        x += tilesizx[ac];
+        t++;
+    }
+    return (x);
+}
+
+int menutextc(int x,int y,short s,short p,char *t)
+{
+    short i, ac, centre;
+
+    s += 8;
+    y -= 12;
+
+    i = centre = 0;
+
+//    if( x == (320>>1) )
+    {
+        while( *(t+i) )
+        {
+            if(*(t+i) == ' ')
+            {
+                centre += 5;
+                i++;
+                continue;
+            }
+            ac = 0;
+            if(*(t+i) >= '0' && *(t+i) <= '9')
+                ac = *(t+i) - '0' + BIGALPHANUM+26+26;
+            if(*(t+i) >= 'a' && *(t+i) <= 'z')
+                ac = *(t+i) - 'a' + BIGALPHANUM+26;
+            if(*(t+i) >= 'A' && *(t+i) <= 'Z')
+                ac = *(t+i) - 'A' + BIGALPHANUM;
+
+            else switch(*t)
+            {
+                case '-':
+                    ac = BIGALPHANUM-11;
+                    break;
+                case '.':
+                    ac = BIGPERIOD;
+                    break;
+                case ',':
+                    ac = BIGCOMMA;
+                    break;
+                case '!':
+                    ac = BIGX;
+                    break;
+                case '?':
+                    ac = BIGQ;
+                    break;
+                case ';':
+                    ac = BIGSEMI;
+                    break;
+                case ':':
+                    ac = BIGCOLIN;
+                    break;
+            }
+
+            centre += tilesizx[ac]-1;
+            i++;
+        }
+    }
+
+    x -= centre>>1;
+
+    while(*t)
+    {
+        if(*t == ' ') {x+=5;t++;continue;}
+        ac = 0;
+        if(*t >= '0' && *t <= '9')
+            ac = *t - '0' + BIGALPHANUM+26+26;
+        if(*t >= 'a' && *t <= 'z')
+            ac = *t - 'a' + BIGALPHANUM+26;
+        if(*t >= 'A' && *t <= 'Z')
+            ac = *t - 'A' + BIGALPHANUM;
+        switch(*t)
+        {
+            case '-':
+                ac = BIGALPHANUM-11;
+                break;
+            case '.':
+                ac = BIGPERIOD;
+                break;
+            case ',':
+                ac = BIGCOMMA;
+                break;
+            case '!':
+                ac = BIGX;
+                break;
+            case '?':
+                ac = BIGQ;
+                break;
+            case ';':
+                ac = BIGSEMI;
+                break;
+            case ':':
+                ac = BIGCOLIN;
+                break;
+        }
+
+        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,10+16,0,0,xdim-1,ydim-1);
+
+        x += tilesizx[ac];
+        t++;
+    }
+    return (x);
+}
+
+
+void bar(int x,int y,short *p,short dainc,char damodify,short s, short pa)
+{
+    short xloc;
+    char rev;
+
+    if(dainc < 0) { dainc = -dainc; rev = 1; }
+    else rev = 0;
+    y-=2;
+
+    if(damodify)
+    {
+        if(rev == 0)
+        {
+            if( KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed( sc_kpad_4 ) || ((buttonstat&1) && minfo.dyaw < -256 ) ) // && onbar) )
+            {
+                KB_ClearKeyDown( sc_LeftArrow );
+                KB_ClearKeyDown( sc_kpad_4 );
+
+                *p -= dainc;
+                if(*p < 0)
+                    *p = 0;
+                sound(KICK_HIT);
+            }
+            if( KB_KeyPressed( sc_RightArrow ) || KB_KeyPressed( sc_kpad_6 ) || ((buttonstat&1) && minfo.dyaw > 256 ) )//&& onbar) )
+            {
+                KB_ClearKeyDown( sc_RightArrow );
+                KB_ClearKeyDown( sc_kpad_6 );
+
+                *p += dainc;
+                if(*p > 63)
+                    *p = 63;
+                sound(KICK_HIT);
+            }
+        }
+        else
+        {
+            if( KB_KeyPressed( sc_RightArrow ) || KB_KeyPressed( sc_kpad_6 ) || ((buttonstat&1) && minfo.dyaw > 256 ))//&& onbar ))
+            {
+                KB_ClearKeyDown( sc_RightArrow );
+                KB_ClearKeyDown( sc_kpad_6 );
+
+                *p -= dainc;
+                if(*p < 0)
+                    *p = 0;
+                sound(KICK_HIT);
+            }
+            if( KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed( sc_kpad_4 ) || ((buttonstat&1) && minfo.dyaw < -256 ))// && onbar) )
+            {
+                KB_ClearKeyDown( sc_LeftArrow );
+                KB_ClearKeyDown( sc_kpad_4 );
+
+                *p += dainc;
+                if(*p > 64)
+                    *p = 64;
+                sound(KICK_HIT);
+            }
+        }
+    }
+
+    xloc = *p;
+
+    rotatesprite( (x+22)<<16,(y-3)<<16,65536L,0,SLIDEBAR,s,pa,10,0,0,xdim-1,ydim-1);
+    if(rev == 0)
+        rotatesprite( (x+xloc+1)<<16,(y+1)<<16,65536L,0,SLIDEBAR+1,s,pa,10,0,0,xdim-1,ydim-1);
+    else
+        rotatesprite( (x+(65-xloc) )<<16,(y+1)<<16,65536L,0,SLIDEBAR+1,s,pa,10,0,0,xdim-1,ydim-1);
+}
+
+#define SHX(X) 0
+// ((x==X)*(-sh))
+#define PHX(X) 0
+// ((x==X)?1:2)
+#define MWIN(X) rotatesprite( 320<<15,200<<15,X,0,MENUSCREEN,-16,0,10+64,0,0,xdim-1,ydim-1)
+#define MWINXY(X,OX,OY) rotatesprite( ( 320+(OX) )<<15, ( 200+(OY) )<<15,X,0,MENUSCREEN,-16,0,10+64,0,0,xdim-1,ydim-1)
+
+
+int32 volnum,levnum,plrskl,numplr;
+short lastsavedpos = -1;
+
+void dispnames(void)
+{
+    short x, c = 160;
+
+    c += 64;
+    for(x = 0;x <= 108;x += 12)
+    rotatesprite((c+91-64)<<16,(x+56)<<16,65536L,0,TEXTBOX,24,0,10,0,0,xdim-1,ydim-1);
+
+    rotatesprite(22<<16,97<<16,65536L,0,WINDOWBORDER2,24,0,10,0,0,xdim-1,ydim-1);
+    rotatesprite(180<<16,97<<16,65536L,1024,WINDOWBORDER2,24,0,10,0,0,xdim-1,ydim-1);
+    rotatesprite(99<<16,50<<16,65536L,512,WINDOWBORDER1,24,0,10,0,0,xdim-1,ydim-1);
+    rotatesprite(103<<16,144<<16,65536L,1024+512,WINDOWBORDER1,24,0,10,0,0,xdim-1,ydim-1);
+
+    minitext(c,48,ud.savegame[0],2,10+16);
+    minitext(c,48+12,ud.savegame[1],2,10+16);
+    minitext(c,48+12+12,ud.savegame[2],2,10+16);
+    minitext(c,48+12+12+12,ud.savegame[3],2,10+16);
+    minitext(c,48+12+12+12+12,ud.savegame[4],2,10+16);
+    minitext(c,48+12+12+12+12+12,ud.savegame[5],2,10+16);
+    minitext(c,48+12+12+12+12+12+12,ud.savegame[6],2,10+16);
+    minitext(c,48+12+12+12+12+12+12+12,ud.savegame[7],2,10+16);
+    minitext(c,48+12+12+12+12+12+12+12+12,ud.savegame[8],2,10+16);
+    minitext(c,48+12+12+12+12+12+12+12+12+12,ud.savegame[9],2,10+16);
+
+}
+
+
+// This is the same thing as was in build.c ...
+//  We DO have a _dos_findfirst implementation now...maybe use that instead?
+//     --ryan.
+#if ORIGINAL_DUKE3D_GETFILENAMES
+int getfilenames(char kind[6])
+{
+        short type;
+        struct find_t fileinfo;
+
+        if (strcmp(kind,"SUBD") == 0)
+        {
+                strcpy(kind,"*.*");
+                if (_dos_findfirst(kind,_A_SUBDIR,&fileinfo) != 0)
+                        return(-1);
+                type = 1;
+        }
+        else
+        {
+                if (_dos_findfirst(kind,_A_NORMAL,&fileinfo) != 0)
+                        return(-1);
+                type = 0;
+        }
+        do
+        {
+                if ((type == 0) || ((fileinfo.attrib&16) > 0))
+                        if ((fileinfo.name[0] != '.') || (fileinfo.name[1] != 0))
+                        {
+                                strcpy(menuname[menunamecnt],fileinfo.name);
+                                menuname[menunamecnt][16] = type;
+                                menunamecnt++;
+                        }
+        }
+        while (_dos_findnext(&fileinfo) == 0);
+
+        return(0);
+}
+
+#else
+
+int getfilenames(char kind[6])
+{
+/* !!! FIXME: Visual C? */
+#if (defined __WATCOMC__)
+	short type;
+	struct find_t fileinfo;
+
+	if (strcmp(kind,"SUBD") == 0)
+	{
+		strcpy(kind,"*.*");
+		if (_dos_findfirst(kind,_A_SUBDIR,&fileinfo) != 0)
+			return(-1);
+		type = 1;
+	}
+	else
+	{
+		if (_dos_findfirst(kind,_A_NORMAL,&fileinfo) != 0)
+			return(-1);
+		type = 0;
+	}
+	do
+	{
+		if ((type == 0) || ((fileinfo.attrib&16) > 0))
+			if ((fileinfo.name[0] != '.') || (fileinfo.name[1] != 0))
+			{
+				strcpy(menuname[menunamecnt],fileinfo.name);
+				menuname[menunamecnt][16] = type;
+				menunamecnt++;
+			}
+	}
+	while (_dos_findnext(&fileinfo) == 0);
+
+#elif (defined PLATFORM_UNIX)
+
+    DIR *dir;
+    struct dirent *dent;
+    struct stat statbuf;
+    int add_this;
+    char *ptr = NULL;
+    int len = 0;
+    int subdirs = 0;
+
+	if (strcmp(kind,"SUBD") == 0)
+        subdirs = 1;
+
+    dir = opendir(".");
+    if (dir == NULL)
+        return(-1);
+
+    do
+    {
+        add_this = 0;
+        dent = readdir(dir);
+        if (dent != NULL)
+        {
+            if (stat(dent->d_name, &statbuf) == 0)
+            {
+                if (subdirs)
+                {
+                    if (S_ISDIR(statbuf.st_mode))
+                    add_this = 1;
+                } /* if */
+                else
+                {
+                    /* need to expand support if this assertion ever fails. */
+                    assert(stricmp(kind, "*.MAP") == 0);
+                    len = strlen(dent->d_name);
+                    if (len >= 5)
+                    {
+                        ptr = ((char *) dent->d_name) + len;
+                        ptr += strlen(ptr) - 4;
+                        if (stricmp(ptr, ".MAP") == 0)
+                    add_this = 1;
+                    } /* if */
+                } /* else */
+
+                if (add_this)
+    			{
+	    			strcpy(menuname[menunamecnt],dent->d_name);
+		    		menuname[menunamecnt][16] = subdirs;
+			    	menunamecnt++;
+                } /* if */
+            } /* if */
+        } /* if */
+    } while (dent != NULL);
+
+    closedir(dir);
+
+#endif
+	return(0);
+}
+
+#endif
+
+
+void sortfilenames()
+{
+        char sortbuffer[17];
+        long i, j, k;
+
+        for(i=1;i<menunamecnt;i++)
+                for(j=0;j<i;j++)
+                {
+                         k = 0;
+                         while ((menuname[i][k] == menuname[j][k]) && (menuname[i][k] != 0) && (menuname[j][k] != 0))
+                                 k++;
+                        if (menuname[i][k] < menuname[j][k])
+                        {
+                                memcpy(&sortbuffer[0],&menuname[i][0],sizeof(menuname[0]));
+                                memcpy(&menuname[i][0],&menuname[j][0],sizeof(menuname[0]));
+                                memcpy(&menuname[j][0],&sortbuffer[0],sizeof(menuname[0]));
+                        }
+                }
+}
+
+long quittimer = 0;
+
+void gameexitanycase(void)
+{
+    KB_FlushKeyboardQueue();
+
+    if( gamequit == 0 && ( numplayers > 1 ) )
+    {
+        if(ps[myconnectindex].gm&MODE_GAME)
+        {
+            gamequit = 1;
+            quittimer = totalclock+120;
+			sound(SHORT_CIRCUIT);
+        }
+        else
+        {
+            sendlogoff();
+            gameexit(" ");
+			sound(NITEVISION_ONOFF);
+        }
+    }
+    else if( numplayers < 2 )
+        gameexit(" ");
+
+    if( ( totalclock > quittimer ) && ( gamequit == 1) )
+	{
+        gameexit("Timed out.");
+	}
+}
+
+
+void menus(void)
+{
+    short c,x;
+    volatile long l;
+	int i,j;
+
+	static int lastkeysetup = 0;
+	static int waiting4key = false;
+	static int current_resolution = 0;
+
+    getpackets();
+
+    if(((ControllerType == controltype_keyboardandmouse)||
+		(ControllerType == controltype_joystickandmouse)) 
+		//&& CONTROL_MousePresent
+		)
+    {
+		
+        if(buttonstat != 0 && !onbar) // anti-repeat
+        {
+            x = MOUSE_GetButtons()<<3;
+            if( x ) 
+			{
+				buttonstat = x<<3;
+			}
+            else 
+			{
+				buttonstat = 0;
+			}
+        }
+        else
+			
+            buttonstat = MOUSE_GetButtons();
+    }
+    else buttonstat = 0;
+
+    if( (ps[myconnectindex].gm&MODE_MENU) == 0 )
+    {
+        walock[MAXTILES-3] = 1;
+        return;
+    }
+
+    ps[myconnectindex].gm &= (0xff-MODE_TYPE);
+    ps[myconnectindex].fta = 0;
+
+    x = 0;
+
+    sh = 4-(sintable[(totalclock<<4)&2047]>>11);
+
+    if(!(current_menu >= 1000 && current_menu <= 2999 && current_menu >= 300 && current_menu <= 369))
+        vscrn();
+
+	// printf("Current menu=%d, game mode=%d, last menu =%d\n", current_menu, ps[myconnectindex].gm, last_menu);
+
+    switch(current_menu)
+    {
+        case 25000:
+            gametext(160,90,"SELECT A SAVE SPOT BEFORE",0,2+8+16);
+            gametext(160,90+9,"YOU QUICK RESTORE.",0,2+8+16);
+
+            x = probe(186,124,0,0);
+            if(x >= -1)
+            {
+                if(ud.multimode < 2 && ud.recstat != 2)
+                {
+                    ready2send = 1;
+                    totalclock = ototalclock;
+                }
+                ps[myconnectindex].gm &= ~MODE_MENU;
+            }
+            break;
+
+        case 20000:
+            x = probe(326,190,0,0);
+            gametext(160,50-8,"YOU ARE PLAYING THE SHAREWARE",0,2+8+16);
+            gametext(160,59-8,"VERSION OF DUKE NUKEM 3D.  WHILE",0,2+8+16);
+            gametext(160,68-8,"THIS VERSION IS REALLY COOL, YOU",0,2+8+16);
+            gametext(160,77-8,"ARE MISSING OVER 75% OF THE TOTAL",0,2+8+16);
+            gametext(160,86-8,"GAME, ALONG WITH OTHER GREAT EXTRAS",0,2+8+16);
+            gametext(160,95-8,"AND GAMES, WHICH YOU'LL GET WHEN",0,2+8+16);
+            gametext(160,104-8,"YOU ORDER THE COMPLETE VERSION AND",0,2+8+16);
+            gametext(160,113-8,"GET THE FINAL TWO EPISODES.",0,2+8+16);
+
+            gametext(160,113+8,"PLEASE READ THE 'HOW TO ORDER' ITEM",0,2+8+16);
+            gametext(160,122+8,"ON THE MAIN MENU IF YOU WISH TO",0,2+8+16);
+            gametext(160,131+8,"UPGRADE TO THE FULL REGISTERED",0,2+8+16);
+            gametext(160,140+8,"VERSION OF DUKE NUKEM 3D.",0,2+8+16);
+            gametext(160,149+16,"PRESS ANY KEY...",0,2+8+16);
+
+            if( x >= -1 ) cmenu(100);
+            break;
+// CTW - REMOVED
+/*      case 20001:
+            x = probe(188,80+32+32,0,0);
+            gametext(160,86-8,"You must be in Windows 95 to",0,2+8+16);
+            gametext(160,86,"play on TEN",0,2+8+16);
+            gametext(160,86+32,"PRESS ANY KEY...",0,2+8+16);
+            if(x >= -1) cmenu(0);
+            break;
+
+        case 20002:
+            x = probe(188,80+32+32+32,0,0);
+            gametext(160,86-8,"MISSING FILE: TENGAME.INI.  PLEASE",0,2+8+16);
+            gametext(160,86,"CONNECT TO TEN BY LAUNCHING THE",0,2+8+16);
+            gametext(160,86+8,"CONNECT TO TEN SHORTCUT OR CONTACT",0,2+8+16);
+            gametext(160,86+8+8,"CUSTOMER SUPPORT AT 1-800-8040-TEN.",0,2+8+16);
+            gametext(160,86+8+8+32,"PRESS ANY KEY...",0,2+8+16);
+            if(x >= -1) cmenu(0);
+            break;
+        case 20003:
+            x = probe(188,80+32+32,0,0);
+            gametext(160,86-8,"BAD TEN INSTALL:  PLEASE RE-INSTALL",0,2+8+16);
+            gametext(160,86,"BAD TEN INSTALL:  PLEASE RE-INSTALL TEN",0,2+8+16);
+            gametext(160,86+32,"PRESS ANY KEY...",0,2+8+16);
+            if(x >= -1) cmenu(0);
+            break;
+        case 20005:
+            x = probe(188,80+32+32,0,0);
+            gametext(160,86-8,"GET THE LATEST TEN SOFTWARE AT",0,2+8+16);
+            gametext(160,86,"HTTP://WWW.TEN.NET",0,2+8+16);
+            gametext(160,86+32,"PRESS ANY KEY...",0,2+8+16);
+            if(x >= -1) cmenu(0);
+            break;*/
+// CTW END - REMOVED
+
+        case 15001:
+        case 15000:
+
+            gametext(160,90,"LOAD last game:",0,2+8+16);
+
+            sprintf(tempbuf,"\"%s\"",ud.savegame[lastsavedpos]);
+            gametext(160,99,tempbuf,0,2+8+16);
+
+            gametext(160,99+9,"(Y/N)",0,2+8+16);
+
+			_handle_events();
+            if( KB_KeyPressed(sc_Escape) || KB_KeyPressed(sc_N) || RMB)
+            {
+                if(sprite[ps[myconnectindex].i].extra <= 0)
+                {
+                    enterlevel(MODE_GAME);
+                    return;
+                }
+
+                KB_ClearKeyDown(sc_N);
+                KB_ClearKeyDown(sc_Escape);
+
+                ps[myconnectindex].gm &= ~MODE_MENU;
+                if(ud.multimode < 2 && ud.recstat != 2)
+                {
+                    ready2send = 1;
+                    totalclock = ototalclock;
+                }
+            }
+
+            if(  KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+                KB_FlushKeyboardQueue();
+                FX_StopAllSounds();
+
+                if(ud.multimode > 1)
+                {
+                    loadplayer(-1-lastsavedpos);
+                    ps[myconnectindex].gm = MODE_GAME;
+                }
+                else
+                {
+                    c = loadplayer(lastsavedpos);
+                    if(c == 0)
+                        ps[myconnectindex].gm = MODE_GAME;
+                }
+            }
+
+            probe(186,124+9,0,0);
+
+            break;
+
+        case 10000:  // parental
+        case 10001:
+
+            c = (320>>1)-120;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"ADULT MODE");
+
+            x = probe(c+6,43,16,2);
+
+            if(x == -1) 
+			{ 
+				cmenu(702); 
+				probey = 6;			
+				break; 
+			}
+
+            menutext(c,43,SHX(-2),PHX(-2),"ADULT MODE");
+			menutext(c+160+40,43,0,0,ud.lockout?"OFF":"ON");
+			
+			menutext(c,43+16,SHX(-3),PHX(-3),"ENTER PASSWORD");
+
+            if(current_menu == 10001)
+            {
+                gametext(160,43+16+16+16-12,"ENTER PASSWORD",0,2+8+16);
+                x = strget((320>>1),43+16+16+16,buf,19, 998);
+
+                if(x == 1) // user hit enter key
+                {
+                    if(ud.pwlockout[0] == 0 || ud.lockout == 0 ) // if already unlocked then we set pwd or reset pwd is there is already one
+                        strcpy(&ud.pwlockout[0],buf);
+                    else if( strcmp(buf,&ud.pwlockout[0]) == 0 ) // if (pwd was up AND lockout is true (locked) AND pwd is good)
+                    {
+                        ud.lockout = 0;
+                        buf[0] = 0;
+
+                        for(x=0;x<numanimwalls;x++)
+                            if( wall[animwall[x].wallnum].picnum != W_SCREENBREAK &&
+                                wall[animwall[x].wallnum].picnum != W_SCREENBREAK+1 &&
+                                wall[animwall[x].wallnum].picnum != W_SCREENBREAK+2 )
+                                    if( wall[animwall[x].wallnum].extra >= 0 )
+                                        wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
+
+                    }
+                    current_menu = 10000;
+                    KB_ClearKeyDown(sc_Enter);
+                    KB_ClearKeyDown(sc_kpad_Enter);
+                    KB_FlushKeyboardQueue();
+                }
+				else if(x==-1) // pressed esc while typing. We discard the text.
+				{
+					*buf = 0; 
+					current_menu = 10000;
+					KB_ClearKeyDown(sc_Escape);
+				}
+			}
+            else
+            {
+                if(x == 0)
+                {
+                    if( ud.lockout == 1 )
+                    {
+                        if(ud.pwlockout[0] == 0)
+                        {
+                            ud.lockout = 0;
+                            for(x=0;x<numanimwalls;x++)
+                            if( wall[animwall[x].wallnum].picnum != W_SCREENBREAK &&
+                                wall[animwall[x].wallnum].picnum != W_SCREENBREAK+1 &&
+                                wall[animwall[x].wallnum].picnum != W_SCREENBREAK+2 )
+                                    if( wall[animwall[x].wallnum].extra >= 0 )
+                                        wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
+                        }
+                        else
+                        {
+                            buf[0] = 0;
+                            current_menu = 10001;
+                            inputloc = 0;
+                            KB_FlushKeyboardQueue();
+                        }
+                    }
+                    else
+                    {
+                        ud.lockout = 1;
+
+                        for(x=0;x<numanimwalls;x++)
+                            switch(wall[animwall[x].wallnum].picnum)
+                            {
+                                case FEMPIC1:
+                                    wall[animwall[x].wallnum].picnum = BLANKSCREEN;
+                                    break;
+                                case FEMPIC2:
+                                case FEMPIC3:
+                                    wall[animwall[x].wallnum].picnum = SCREENBREAK6;
+                                    break;
+                            }
+                    }
+                }
+
+                else if(x == 1)
+                {
+                    current_menu = 10001; // ask for password change
+                    inputloc = 0;
+					*buf = 0;
+                    KB_FlushKeyboardQueue();
+                }
+            }
+
+            break;
+
+        case 1000:
+        case 1001:
+        case 1002:
+        case 1003:
+        case 1004:
+        case 1005:
+        case 1006:
+        case 1007:
+        case 1008:
+        case 1009:
+
+            rotatesprite(160<<16,200<<15,65536L,0,MENUSCREEN,16,0,10+64,0,0,xdim-1,ydim-1);
+            rotatesprite(160<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(160,24,0,0,"LOAD GAME");
+            rotatesprite(101<<16,97<<16,65536,512,MAXTILES-3,-32,0,4+10+64,0,0,xdim-1,ydim-1);
+
+            dispnames();
+
+            sprintf(tempbuf,"PLAYERS: %-2d                      ",numplr);
+            gametext(160,158,tempbuf,0,2+8+16);
+
+            sprintf(tempbuf,"EPISODE: %-2d / LEVEL: %-2d / SKILL: %-2d",1+volnum,1+levnum,plrskl);
+            gametext(160,170,tempbuf,0,2+8+16);
+
+            gametext(160,90,"LOAD game:",0,2+8+16);
+            sprintf(tempbuf,"\"%s\"",ud.savegame[current_menu-1000]);
+            gametext(160,99,tempbuf,0,2+8+16);
+            gametext(160,99+9,"(Y/N)",0,2+8+16);
+
+			_handle_events();
+            if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+                lastsavedpos = current_menu-1000;
+
+                KB_FlushKeyboardQueue();
+                //if(ud.multimode < 2 && ud.recstat != 2)
+                //{
+                //    ready2send = 1;
+                //    totalclock = ototalclock;
+                //}
+
+                if(ud.multimode > 1)
+                {
+                    if( ps[myconnectindex].gm&MODE_GAME )
+                    {
+                        loadplayer(-1-lastsavedpos);
+                        ps[myconnectindex].gm = MODE_GAME;
+                    }
+                    else
+                    {
+                        tempbuf[0] = 126;
+                        tempbuf[1] = lastsavedpos;
+                        for(x=connecthead;x>=0;x=connectpoint2[x])
+                            if(x != myconnectindex)
+                                sendpacket(x,tempbuf,2);
+
+                        getpackets();
+
+                        loadplayer(lastsavedpos);
+
+                        multiflag = 0;
+                    }
+                }
+                else
+                {
+					// FIX_00084: Various bugs in the load game (single player) option if ESC is hit or if wrong version 
+
+                    c = loadplayer(lastsavedpos);
+                    if(c == 0)
+					{
+                        ps[myconnectindex].gm = MODE_GAME;
+						if (ud.recstat != 2) // if not playing a demo then ..
+							totalclock = ototalclock;
+					}
+					else
+						cmenu(1010); // failed loading game
+                }
+
+                break;
+            }
+
+            if( KB_KeyPressed(sc_N) || KB_KeyPressed(sc_Escape) || RMB)
+            {
+                KB_ClearKeyDown(sc_N);
+                KB_ClearKeyDown(sc_Escape);
+                sound(EXITMENUSOUND);
+				cmenu(300);
+				// FIX_00084: Various bugs in the load game (single player) option if ESC is hit or if wrong version 
+				// simply get back w/o changing anything.
+
+            }
+
+            break;
+
+		case 1010: //loading a saved game failed
+
+			gametext(160,99,"YOU CAN'T LOAD THIS GAME",0,2+8+16);
+			gametext(160,99+9,"EITHER A WONG VERSION",0,2+8+16);
+			gametext(160,99+9+9,"OR BAD # OF PLAYERS OR...",0,2+8+16);
+
+			_handle_events();
+			if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Escape) || KB_KeyPressed(sc_Enter)
+				|| RMB)            {
+                KB_ClearKeyDown(sc_Space);
+                KB_ClearKeyDown(sc_Escape);
+				KB_ClearKeyDown(sc_Enter);
+                sound(EXITMENUSOUND);
+				cmenu(300);
+			}
+
+			break;
+
+        case 1500:
+			_handle_events();
+            if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+                KB_FlushKeyboardQueue();
+                cmenu(100);
+            }
+            if( KB_KeyPressed(sc_N) || KB_KeyPressed(sc_Escape) || RMB)
+            {
+                KB_ClearKeyDown(sc_N);
+                KB_ClearKeyDown(sc_Escape);
+                if(ud.multimode < 2 && ud.recstat != 2)
+                {
+                    ready2send = 1;
+                    totalclock = ototalclock;
+                }
+                ps[myconnectindex].gm &= ~MODE_MENU;
+                sound(EXITMENUSOUND);
+                break;
+            }
+            probe(186,124,0,0);
+            gametext(160,90,"ABORT this game?",0,2+8+16);
+            gametext(160,90+9,"(Y/N)",0,2+8+16);
+
+            break;
+
+        case 2000:
+        case 2001:
+        case 2002:
+        case 2003:
+        case 2004:
+        case 2005:
+        case 2006:
+        case 2007:
+        case 2008:
+        case 2009:
+
+            rotatesprite(160<<16,200<<15,65536L,0,MENUSCREEN,16,0,10+64,0,0,xdim-1,ydim-1);
+            rotatesprite(160<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(160,24,0,0,"SAVE GAME");
+
+            rotatesprite(101<<16,97<<16,65536L,512,MAXTILES-3,-32,0,4+10+64,0,0,xdim-1,ydim-1);
+            sprintf(tempbuf,"PLAYERS: %-2d                      ",ud.multimode);
+            gametext(160,158,tempbuf,0,2+8+16);
+
+            sprintf(tempbuf,"EPISODE: %-2d / LEVEL: %-2d / SKILL: %-2d",1+ud.volume_number,1+ud.level_number,ud.player_skill);
+            gametext(160,170,tempbuf,0,2+8+16);
+
+            dispnames();
+
+            gametext(160,90,"OVERWRITE previous SAVED game?",0,2+8+16);
+            gametext(160,90+9,"(Y/N)",0,2+8+16);
+
+			_handle_events();
+            if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+                KB_FlushKeyboardQueue();
+                inputloc = strlen(&ud.savegame[current_menu-2000][0]);
+
+                cmenu(current_menu-2000+360);
+
+                KB_FlushKeyboardQueue();
+                break;
+            }
+            if( KB_KeyPressed(sc_N) || KB_KeyPressed(sc_Escape) || RMB)
+            {
+                KB_ClearKeyDown(sc_N);
+                KB_ClearKeyDown(sc_Escape);
+                cmenu(351);
+                sound(EXITMENUSOUND);
+            }
+
+            probe(186,124,0,0);
+
+            break;
+
+        case 990:  // credits
+        case 991:
+        case 992:
+        case 993:
+        case 994:
+        case 995:
+        case 996:
+        case 997:
+			c = 160;
+			if (!PLUTOPAK) {
+            	//rotatesprite(c<<16,200<<15,65536L,0,MENUSCREEN,16,0,10+64,0,0,xdim-1,ydim-1);
+            	rotatesprite(c<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            	menutext(c,24,0,0,"CREDITS");
+
+				l = 7;
+			} else {
+				l = 2;
+			}
+
+            if(KB_KeyPressed(sc_Escape)) { cmenu(0); break; }
+
+            if( KB_KeyPressed( sc_LeftArrow ) ||
+                KB_KeyPressed( sc_kpad_4 ) ||
+                KB_KeyPressed( sc_UpArrow ) ||
+                KB_KeyPressed( sc_PgUp ) ||
+                KB_KeyPressed( sc_kpad_8 ) )
+            {
+                KB_ClearKeyDown(sc_LeftArrow);
+                KB_ClearKeyDown(sc_kpad_4);
+                KB_ClearKeyDown(sc_UpArrow);
+                KB_ClearKeyDown(sc_PgUp);
+                KB_ClearKeyDown(sc_kpad_8);
+
+                sound(KICK_HIT);
+                current_menu--;
+                if(current_menu < 990) current_menu = 990+l;
+            }
+            else if(
+                KB_KeyPressed( sc_PgDn ) ||
+                KB_KeyPressed( sc_Enter ) ||
+                KB_KeyPressed( sc_Space ) ||
+                KB_KeyPressed( sc_kpad_Enter ) ||
+                KB_KeyPressed( sc_RightArrow ) ||
+                KB_KeyPressed( sc_DownArrow ) ||
+                KB_KeyPressed( sc_kpad_2 ) ||
+                KB_KeyPressed( sc_kpad_9 ) ||
+                KB_KeyPressed( sc_kpad_6 ) )
+            {
+                KB_ClearKeyDown(sc_PgDn);
+                KB_ClearKeyDown(sc_Enter);
+                KB_ClearKeyDown(sc_RightArrow);
+                KB_ClearKeyDown(sc_kpad_Enter);
+                KB_ClearKeyDown(sc_kpad_6);
+                KB_ClearKeyDown(sc_kpad_9);
+                KB_ClearKeyDown(sc_kpad_2);
+                KB_ClearKeyDown(sc_DownArrow);
+                KB_ClearKeyDown(sc_Space);
+                sound(KICK_HIT);
+                current_menu++;
+                if(current_menu > 990+l) current_menu = 990;
+            }
+
+			if (!PLUTOPAK) { // This is Jonathon Fowler code. Text respects the true 1.3/1.3d --mk
+			switch (current_menu) {
+				case 990:
+					gametext(c,40,                      "ORIGINAL CONCEPT",0,2+8+16);
+					gametext(c,40+9,                    "TODD REPLOGLE",0,2+8+16);
+					gametext(c,40+9+9,                  "ALLEN H. BLUM III",0,2+8+16);
+						
+					gametext(c,40+9+9+9+9,              "PRODUCED & DIRECTED BY",0,2+8+16);
+					gametext(c,40+9+9+9+9+9,            "GREG MALONE",0,2+8+16);
+
+					gametext(c,40+9+9+9+9+9+9+9,        "EXECUTIVE PRODUCER",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9,      "GEORGE BROUSSARD",0,2+8+16);
+
+					gametext(c,40+9+9+9+9+9+9+9+9+9+9,  "BUILD ENGINE",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9+9+9+9,"KEN SILVERMAN",0,2+8+16);
+					break;
+				case 991:
+					gametext(c,40,                      "GAME PROGRAMMING",0,2+8+16);
+					gametext(c,40+9,                    "TODD REPLOGLE",0,2+8+16);
+						
+					gametext(c,40+9+9+9,                "3D ENGINE/TOOLS/NET",0,2+8+16);
+					gametext(c,40+9+9+9+9,              "KEN SILVERMAN",0,2+8+16);
+
+					gametext(c,40+9+9+9+9+9+9,          "NETWORK LAYER/SETUP PROGRAM",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9,        "MARK DOCHTERMANN",0,2+8+16);
+					break;
+				case 992:
+					gametext(c,40,                      "MAP DESIGN",0,2+8+16);
+					gametext(c,40+9,                    "ALLEN H BLUM III",0,2+8+16);
+					gametext(c,40+9+9,                  "RICHARD GRAY",0,2+8+16);
+						
+					gametext(c,40+9+9+9+9,              "3D MODELING",0,2+8+16);
+					gametext(c,40+9+9+9+9+9,            "CHUCK JONES",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9,          "SAPPHIRE CORPORATION",0,2+8+16);
+
+					gametext(c,40+9+9+9+9+9+9+9+9,      "ARTWORK",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9+9,    "DIRK JONES, STEPHEN HORNBACK",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9+9+9,  "JAMES STOREY, DAVID DEMARET",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9+9+9+9,"DOUGLAS R WOOD",0,2+8+16);
+					break;
+				case 993:
+					gametext(c,40,                      "SOUND ENGINE",0,2+8+16);
+					gametext(c,40+9,                    "JIM DOSE",0,2+8+16);
+						
+					gametext(c,40+9+9+9,                "SOUND & MUSIC DEVELOPMENT",0,2+8+16);
+					gametext(c,40+9+9+9+9,              "ROBERT PRINCE",0,2+8+16);
+					gametext(c,40+9+9+9+9+9,            "LEE JACKSON",0,2+8+16);
+
+					gametext(c,40+9+9+9+9+9+9+9,        "VOICE TALENT",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9,      "LANI MINELLA - VOICE PRODUCER",0,2+8+16);
+					gametext(c,40+9+9+9+9+9+9+9+9+9,    "JON ST. JOHN AS \"DUKE NUKEM\"",0,2+8+16);
+					break;
+				case 994:
+					gametext(c,60,                      "GRAPHIC DESIGN",0,2+8+16);
+					gametext(c,60+9,                    "PACKAGING, MANUAL, ADS",0,2+8+16);
+					gametext(c,60+9+9,                  "ROBERT M. ATKINS",0,2+8+16);
+					gametext(c,60+9+9+9,                "MICHAEL HADWIN",0,2+8+16);
+					
+					gametext(c,60+9+9+9+9+9,            "SPECIAL THANKS TO",0,2+8+16);
+					gametext(c,60+9+9+9+9+9+9,          "STEVEN BLACKBURN, TOM HALL",0,2+8+16);
+					gametext(c,60+9+9+9+9+9+9+9,        "SCOTT MILLER, JOE SIEGLER",0,2+8+16);
+					gametext(c,60+9+9+9+9+9+9+9+9,      "TERRY NAGY, COLLEEN COMPTON",0,2+8+16);
+					gametext(c,60+9+9+9+9+9+9+9+9+9,    "HASH INC., FORMGEN, INC.",0,2+8+16);
+					break;
+				case 995:
+					gametext(c,49,                      "THE 3D REALMS BETA TESTERS",0,2+8+16);
+
+					gametext(c,49+9+9,                  "NATHAN ANDERSON, WAYNE BENNER",0,2+8+16);
+					gametext(c,49+9+9+9,                "GLENN BRENSINGER, ROB BROWN",0,2+8+16);
+					gametext(c,49+9+9+9+9,              "ERIK HARRIS, KEN HECKBERT",0,2+8+16);
+					gametext(c,49+9+9+9+9+9,            "TERRY HERRIN, GREG HIVELY",0,2+8+16);
+					gametext(c,49+9+9+9+9+9+9,          "HANK LEUKART, ERIC BAKER",0,2+8+16);
+					gametext(c,49+9+9+9+9+9+9+9,        "JEFF RAUSCH, KELLY ROGERS",0,2+8+16);
+					gametext(c,49+9+9+9+9+9+9+9+9,      "MIKE DUNCAN, DOUG HOWELL",0,2+8+16);
+					gametext(c,49+9+9+9+9+9+9+9+9+9,    "BILL BLAIR",0,2+8+16);
+					break;
+				case 996:
+					gametext(c,32,                      "COMPANY PRODUCT SUPPORT",0,2+8+16);
+
+					gametext(c,32+9+9,                  "THE FOLLOWING COMPANIES WERE COOL",0,2+8+16);
+					gametext(c,32+9+9+9,                "ENOUGH TO GIVE US LOTS OF STUFF",0,2+8+16);
+					gametext(c,32+9+9+9+9,              "DURING THE MAKING OF DUKE NUKEM 3D.",0,2+8+16);
+
+					gametext(c,32+9+9+9+9+9+9,          "ALTEC LANSING MULTIMEDIA",0,2+8+16);
+					gametext(c,32+9+9+9+9+9+9+9,        "FOR TONS OF SPEAKERS AND THE",0,2+8+16);
+					gametext(c,32+9+9+9+9+9+9+9+9,      "THX-LICENSED SOUND SYSTEM",0,2+8+16);
+					gametext(c,32+9+9+9+9+9+9+9+9+9,    "FOR INFO CALL 1-800-548-0620",0,2+8+16);
+					
+					gametext(c,32+9+9+9+9+9+9+9+9+9+9+9,"CREATIVE LABS, INC.",0,2+8+16);
+
+					gametext(c,32+9+9+9+9+9+9+9+9+9+9+9+9+9,"THANKS FOR THE HARDWARE, GUYS.",0,2+8+16);
+					break;
+				case 997:
+					gametext(c,50,                      "DUKE NUKEM IS A TRADEMARK OF",0,2+8+16);
+					gametext(c,50+9,                    "3D REALMS ENTERTAINMENT",0,2+8+16);
+						
+					gametext(c,50+9+9+9,                "DUKE NUKEM",0,2+8+16);
+					gametext(c,50+9+9+9+9,              "(C) 1996 3D REALMS ENTERTAINMENT",0,2+8+16);
+
+					if (VOLUMEONE) {
+					gametext(c,106,                     "PLEASE READ LICENSE.DOC FOR SHAREWARE",0,2+8+16);
+					gametext(c,106+9,                   "DISTRIBUTION GRANTS AND RESTRICTIONS",0,2+8+16);
+					}
+
+					gametext(c,VOLUMEONE?134:115,       "MADE IN DALLAS, TEXAS USA",0,2+8+16);
+					break;
+			}
+			}
+
+            switch(current_menu)
+            {
+                case 990:
+                case 991:
+                case 992:
+                   rotatesprite(160<<16,200<<15,65536L,0,2504+current_menu-990,0,0,10+64,0,0,xdim-1,ydim-1);
+
+                   break;
+
+            }
+            break;
+
+        case 0: // main menu
+            c = (320>>1);
+            rotatesprite(c<<16,28<<16,65536L,0,INGAMEDUKETHREEDEE,0,0,10,0,0,xdim-1,ydim-1);
+            rotatesprite((c+100)<<16,36<<16,65536L,0,PLUTOPAKSPRITE+2,(sintable[(totalclock<<4)&2047]>>11),0,2+8,0,0,xdim-1,ydim-1);
+            
+			x = probe(c,67,16,6);
+
+            if(x >= 0)
+            {
+				if( ud.multimode > 1 && x == 0 && ud.recstat != 2)
+                {
+                    if( movesperpacket == 4 && myconnectindex != connecthead )
+                        break;
+
+                    last_zero = 0;
+                    cmenu( 600 );
+                }
+                else
+                {
+                    last_zero = x;
+                    switch(x)
+                    {
+                        case 0:
+                            cmenu(100);
+                            break;
+                        case 1: cmenu(200);break;
+                        case 2:
+                            if(movesperpacket == 4 && connecthead != myconnectindex)
+                                break;
+                            cmenu(300);
+                            break;
+                        case 3: KB_FlushKeyboardQueue();cmenu(400);break;  // help
+                        case 4: cmenu(990);break;  // credit
+                        case 5: cmenu(501);break;  // quit
+
+                    }
+                }
+            }
+
+            if(KB_KeyPressed(sc_Q)) cmenu(501);
+
+            if(x == -1)
+            {
+				// FIX_00069: Hitting Esc at the menu screen shows an empty green screen
+				if(ud.recstat == 2) // playing demo
+					ps[myconnectindex].gm &= ~MODE_MENU;
+            }
+
+            if(movesperpacket == 4)
+            {
+                if( myconnectindex == connecthead )
+                    menutext(c,67,SHX(-2),PHX(-2),"NEW GAME");
+                else
+                    menutext(c,67,SHX(-2),1,"NEW GAME");
+            }
+            else
+                menutext(c,67,SHX(-2),PHX(-2),"NEW GAME");
+
+            menutext(c,67+16,SHX(-3),PHX(-3),"OPTIONS");
+
+            if(movesperpacket == 4 && connecthead != myconnectindex)
+                menutext(c,67+16+16,SHX(-4),1,"LOAD GAME");
+            else menutext(c,67+16+16,SHX(-4),PHX(-4),"LOAD GAME");
+
+if(VOLUMEONE)
+            menutext(c,67+16+16+16,SHX(-5),PHX(-5),"HOW TO ORDER");
+else
+            menutext(c,67+16+16+16,SHX(-5),PHX(-5),"HELP");
+
+            menutext(c,67+16+16+16+16,SHX(-6),PHX(-6),"CREDITS");
+
+            menutext(c,67+16+16+16+16+16,SHX(-7),PHX(-7),"QUIT");
+
+            break;
+// CTW END - MODIFICATION
+
+        case 50: // general menu as cmenu(0) but for multiplayer games
+            c = (320>>1);
+            rotatesprite(c<<16,32<<16,65536L,0,INGAMEDUKETHREEDEE,0,0,10,0,0,xdim-1,ydim-1);
+            rotatesprite((c+100)<<16,36<<16,65536L,0,PLUTOPAKSPRITE+2,(sintable[(totalclock<<4)&2047]>>11),0,2+8,0,0,xdim-1,ydim-1);
+            x = probe(c,67,16,7);
+            switch(x)
+            {
+                case 0:
+                    if(movesperpacket == 4 && myconnectindex != connecthead)
+                        break;
+                    if(ud.multimode < 2 || ud.recstat == 2)
+                        cmenu(1500);
+                    else
+                    {
+                        cmenu(600);
+                        last_fifty = 0;
+                    }
+                    break;
+                case 1:
+                    if(movesperpacket == 4 && connecthead != myconnectindex)
+                        break;
+                    if(ud.recstat != 2)
+                    {
+                        last_fifty = 1;
+                        cmenu(350);
+                        setview(0,0,xdim-1,ydim-1);
+                    }
+                    break;
+                case 2:
+                    if(movesperpacket == 4 && connecthead != myconnectindex)
+                        break;
+                    last_fifty = 2;
+                    cmenu(300);
+                    break;
+                case 3:
+                    last_fifty = 3;
+                    cmenu(200);
+                    break;
+                case 4:
+                    last_fifty = 4;
+                    KB_FlushKeyboardQueue();
+                    cmenu(400);
+                    break;
+                case 5:
+                    if(numplayers < 2)
+                    {
+                        last_fifty = 5;
+                        cmenu(503);
+                    }
+                    break;
+                case 6:
+                    last_fifty = 6;
+                    cmenu(502);
+                    break;
+                case -1:
+                    ps[myconnectindex].gm &= ~MODE_MENU;
+                    if(ud.multimode < 2 && ud.recstat != 2)
+                    {
+                        ready2send = 1;
+                        totalclock = ototalclock;
+                    }
+                    break;
+            }
+
+            if( KB_KeyPressed(sc_Q) )
+                cmenu(502);
+
+            if(movesperpacket == 4 && connecthead != myconnectindex)
+            {
+                menutext(c,67+16*0 ,SHX(-2),1,"NEW GAME");
+                menutext(c,67+16*1 ,SHX(-3),1,"SAVE GAME");
+                menutext(c,67+16*2 ,SHX(-4),1,"LOAD GAME");
+            }
+            else
+            {
+                menutext(c,67+16*0 ,SHX(-2),PHX(-2),"NEW GAME");
+                menutext(c,67+16*1 ,SHX(-3),PHX(-3),"SAVE GAME");
+                menutext(c,67+16*2 ,SHX(-4),PHX(-4),"LOAD GAME");
+            }
+
+            menutext(c,67+16*3 ,SHX(-5),PHX(-5),"OPTIONS");
+if(VOLUMEONE)
+            menutext(c,67+16*4 ,SHX(-6),PHX(-6),"HOW TO ORDER");
+else
+            menutext(c,67+16*4 ,SHX(-6),PHX(-6)," HELP");
+
+            if(numplayers > 1)
+                menutext(c,67+16*5 ,SHX(-7),1,"QUIT TO TITLE");
+            else menutext(c,67+16*5 ,SHX(-7),PHX(-7),"QUIT TO TITLE");
+            menutext(c,67+16*6,SHX(-8),PHX(-8),"QUIT GAME");
+
+            break;
+
+        case 100: // Title menu
+            rotatesprite(160<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(160,24,0,0,"SELECT AN EPISODE");
+if(PLUTOPAK)
+{            //////if(boardfilename[0])
+            //
+			// uncomment this for user map
+			//x = probe(160,60,20,5);
+			x = probe(160,60,20,4);
+
+            //////else x = probe(160,60,20,4);
+}
+else
+{
+            if(boardfilename[0])
+                x = probe(160,60,20,4);
+            else x = probe(160,60,20,3);
+}
+            if(x >= 0)
+            {
+
+				if (VOLUMEONE)
+				{
+					if(x > 0)
+						cmenu(20000);
+					else
+					{
+						ud.m_volume_number = x;
+						ud.m_level_number = 0;
+						cmenu(110);
+				    }
+				}
+
+				else
+				{
+
+					if((x == 3 && boardfilename[0])&&!PLUTOPAK)
+					{
+					    ud.m_volume_number = 0;
+					    ud.m_level_number = 7;
+					}
+					else
+
+					/*
+					if(x == 4 && boardfilename[0])
+					{
+						ud.m_volume_number = 0;
+						ud.m_level_number = 7;
+					}
+					*/
+
+					// USER MAP
+					if((x == 4)&&PLUTOPAK)
+					{
+						//CONSOLE_Printf("MENU_USER_MAP");
+						//
+#pragma message ("[Todo: generate file list starting from .\\maps]")
+
+						cmenu(MENU_USER_MAP); // cmenu(101)
+						break;
+					}
+					else
+					{
+						ud.m_volume_number = x;
+						ud.m_level_number = 0;
+					}
+					cmenu(110);
+				}
+            }
+            else if(x == -1)
+            {
+                if(ps[myconnectindex].gm&MODE_GAME) cmenu(50);
+                else cmenu(0);
+            }
+
+            menutext(160,60,SHX(-2),PHX(-2),volume_names[0]);
+
+            c = 80;
+			if (VOLUMEONE)
+			{
+				menutext(160,60+20,SHX(-3),1,volume_names[1]);
+	            menutext(160,60+20+20,SHX(-4),1,volume_names[2]);
+				if(PLUTOPAK)
+					menutext(160,60+20+20,SHX(-5),1,volume_names[3]);
+			}
+			else
+			{
+				menutext(160,60+20,SHX(-3),PHX(-3),volume_names[1]);
+				menutext(160,60+20+20,SHX(-4),PHX(-4),volume_names[2]);
+				if(PLUTOPAK)
+				{            
+					menutext(160,60+20+20+20,SHX(-5),PHX(-5),volume_names[3]);
+					//if(boardfilename[0])
+					//{
+
+					// uncomment this for usermap
+					//menutext(160,60+20+20+20+20,SHX(-6),PHX(-6),"USER MAP");
+		           
+					//gametextpal(160,60+20+20+20+20+3,boardfilename,16+(sintable[(totalclock<<4)&2047]>>11),2);
+					//}
+				}
+				else
+				{	/*
+		            if(boardfilename[0])
+				    {
+						menutext(160,60+20+20+20,SHX(-6),PHX(-6),"USER MAP");
+						gametext(160,60+20+20+20+6,boardfilename,2,2+8+16);
+					}
+					*/
+				}
+			}
+            break;
+
+		case 101: // MENU_USER_MAP
+	            c = (320>>1);
+				rotatesprite(c<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+				menutext(c,24,0,0,"USER MAP");
+
+				// Draw USER MAP background
+				{
+					int x1, x2, y, y1, y2; 
+					long sx, sy;
+					long xPos, xPos2;
+					long yPos, yPos2;
+
+					xPos =  ( xdim *32) / 320;
+					yPos =  ( ydim *30) / 200;
+
+					xPos2 =  ( xdim *282) / 320;
+					yPos2 =  ( ydim *130) / 200;
+
+					for(y=yPos; y < (ydim - (yPos*2)); y+=128)
+					{
+						for(x1=xPos; x1 < xPos2; x1+=128)
+						{
+							rotatesprite(x1<<16,y<<16,65536L,0,BIGHOLE,8,0,1+8+16+64+128,0,0, xdim - xPos, ydim - (yPos*2));
+						}
+					}
+				}	
+
+				c = (320>>1)-120;
+				x = probe(c,70,19,4);
+				
+				if(x == -1)
+				{
+					cmenu(MENU_SELECT_EPISODE);
+				}
+			break;
+
+        case 110:
+            c = (320>>1);
+            rotatesprite(c<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(c,24,0,0,"SELECT SKILL");
+            x = probe(c,70,19,4);
+            if(x >= 0)
+            {
+                switch(x)
+                {
+                    case 0: globalskillsound = JIBBED_ACTOR6;break;
+                    case 1: globalskillsound = BONUS_SPEECH1;break;
+                    case 2: globalskillsound = DUKE_GETWEAPON2;break;
+                    case 3: globalskillsound = JIBBED_ACTOR5;break;
+                }
+
+                sound(globalskillsound);
+
+                ud.m_player_skill = x+1;
+                if(x == 3) ud.m_respawn_monsters = 1;
+                else ud.m_respawn_monsters = 0;
+
+                ud.m_monsters_off = ud.monsters_off = 0;
+
+                ud.m_respawn_items = 0;
+                ud.m_respawn_inventory = 0;
+
+                ud.multimode = 1;
+
+				// if (ud.showcinematics) 
+                //if(ud.m_volume_number == 3) // not needed to play cinematics. Black screen not nice
+                //{
+                //    flushperms();
+                //    setview(0,0,xdim-1,ydim-1);
+                //    clearview(0L);
+                //    nextpage();
+                //}
+				
+                newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill);
+                enterlevel(MODE_GAME);
+            }
+            else if(x == -1)
+            {
+                cmenu(100);
+                KB_FlushKeyboardQueue();
+            }
+
+            menutext(c,70,SHX(-2),PHX(-2),skill_names[0]);
+            menutext(c,70+19,SHX(-3),PHX(-3),skill_names[1]);
+            menutext(c,70+19+19,SHX(-4),PHX(-4),skill_names[2]);
+            menutext(c,70+19+19+19,SHX(-5),PHX(-5),skill_names[3]);
+            break;
+
+        case 200:
+
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"OPTIONS");
+
+            c = (320>>1)-120;
+
+            x = probe(c+6,43,16,6);
+
+            if(x == -1)
+                { if(ps[myconnectindex].gm&MODE_GAME) cmenu(50);else cmenu(0); }
+
+            switch(x)
+            {
+				case 0:
+					cmenu(702); // game options
+					break;
+
+                case 1:
+                    cmenu(703); // keybaord setup
+					probey = 7;
+                    break;
+
+                case 2:
+                    cmenu(701); // mouse setup
+                    break;
+
+				case 3:
+                    cmenu(700);  // sound setup
+                    break;
+
+				case 4:  
+					cmenu(706); // Video setup
+					lastkeysetup = 0;
+					current_resolution = 0; // in case we don't find it
+					for(i=0; i<validmodecnt; i++)
+					{
+						if(validmodexdim[i] == xdim && validmodeydim[i] == ydim)
+							current_resolution = i;
+					}
+					break;
+
+                case 5: // record on/off
+                    if( (ps[myconnectindex].gm&MODE_GAME) )
+                    {
+                        closedemowrite();
+                        break;
+                    }
+                    ud.m_recstat = !ud.m_recstat;
+                    break;
+
+				//case -7:
+				//	gametext(320>>1,43+16*6,"*** DISABLED. WILL BE FIXED SOON***",0,2+8+16); // center-i
+				//	break;
+
+			}
+
+			menutext(c,43,SHX(-6),PHX(-6),"GAME OPTIONS");
+
+			menutext(c,43+16,SHX(-6),PHX(-6),"SETUP KEYBOARD");
+
+			menutext(c,43+16+16,SHX(-6),PHX(-6),"SETUP MOUSE");
+
+            menutext(c,43+16+16+16,SHX(-8),PHX(-8),"SETUP SOUND");
+
+            menutext(c,43+16+16+16+16,SHX(-8),PHX(-8),"SETUP VIDEO");
+
+            if( (ps[myconnectindex].gm&MODE_GAME) && ud.m_recstat != 1 )
+            {
+                menutext(c,43+16+16+16+16+16,SHX(-10),1,"RECORD");
+                menutext(c+160+40,43+16+16+16+16+16,SHX(-10),1,"OFF");
+            }
+            else
+            {
+                menutext(c,43+16+16+16+16+16,SHX(-10),PHX(-10),"RECORD");
+
+                if(ud.m_recstat == 1)
+                    menutext(c+160+40,43+16+16+16+16+16,SHX(-10),PHX(-10),"ON");
+                else menutext(c+160+40,43+16+16+16+16+16,SHX(-10),PHX(-10),"OFF");
+            }
+
+            break;
+
+        case 700:
+
+            c = (320>>1)-120;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"SETUP SOUNDS");
+            onbar = ( (probey == 2)&&SoundToggle || (probey == 3)&&MusicToggle );
+
+            x = probe(c+6,43,16,8);
+
+            switch(x)
+            {
+                case -1:
+					cmenu(200);
+					probey = 3;
+                    break;
+
+                case 0:
+                    if (FXDevice != NumSoundCards)
+                    {
+                        SoundToggle = 1-SoundToggle;
+                        if( SoundToggle == 0 )
+                        {
+                            FX_StopAllSounds();
+                            clearsoundlocks();
+                        }
+                    }
+                    break;
+
+				case 1:
+
+                    if(eightytwofifty == 0 || numplayers < 2)
+                        if(MusicDevice != NumSoundCards)
+                    {
+                        MusicToggle = 1-MusicToggle;
+                        if( MusicToggle == 0 ) 
+							MUSIC_StopSong();
+                        else
+                        {
+                            if(ud.recstat != 2 && ps[myconnectindex].gm&MODE_GAME)
+                                playmusic(&music_fn[0][music_select][0]);
+                            else playmusic(&env_music_fn[0][0]);
+
+                            MUSIC_Continue();
+                        }
+                    }
+                    break;
+
+                case 4:
+                    if(SoundToggle && (FXDevice != NumSoundCards)) VoiceToggle = 1-VoiceToggle;
+                    break;
+
+				case 5:
+                    if(SoundToggle && (FXDevice != NumSoundCards)) AmbienceToggle = 1-AmbienceToggle;
+                    break;
+
+				case 6:
+                    if(SoundToggle && (FXDevice != NumSoundCards))
+                    {
+                        ReverseStereo = 1-ReverseStereo;
+                        FX_SetReverseStereo(ReverseStereo);
+					}
+                    break;
+
+				case 7: // xduke: 1.3d sound style - hear opponent
+					if(SoundToggle && (FXDevice != NumSoundCards))
+                    {
+                        OpponentSoundToggle = 1-OpponentSoundToggle;
+                    }
+					break;
+
+                default:
+                    break;
+            }
+
+            if(SoundToggle && FXDevice != NumSoundCards) menutext(c+160+40,43,0,(FXDevice == NumSoundCards),"ON");
+            else menutext(c+160+40,43,0,(FXDevice == NumSoundCards),"OFF");
+
+            if(MusicToggle && (MusicDevice != NumSoundCards) && (!eightytwofifty||numplayers<2))
+                menutext(c+160+40,43+16,0,(MusicDevice == NumSoundCards),"ON");
+            else menutext(c+160+40,43+16,0,(MusicDevice == NumSoundCards),"OFF");
+
+            menutext(c,43,SHX(-2),(FXDevice == NumSoundCards),"SOUND");
+            menutext(c,43+16+16,SHX(-4),(FXDevice==NumSoundCards)||SoundToggle==0,"SOUND VOLUME");
+            {
+                l = FXVolume;
+                FXVolume >>= 2;
+                bar(c+167+40,43+16+16,(short *)&FXVolume,4,(FXDevice!=NumSoundCards)&&x==2,SHX(-4),SoundToggle==0||(FXDevice==NumSoundCards));
+                if(l != FXVolume)
+                    FXVolume <<= 2;
+                if(l != FXVolume)
+                    FX_SetVolume( (short) FXVolume );
+            }
+            menutext(c,43+16,SHX(-3),(MusicDevice==NumSoundCards),"MUSIC");
+            menutext(c,43+16+16+16,SHX(-5),(MusicDevice==NumSoundCards)||(numplayers > 1 && eightytwofifty)||MusicToggle==0,"MUSIC VOLUME");
+            {
+                l = MusicVolume;
+                MusicVolume >>= 2;
+                bar(c+167+40,43+16+16+16,
+                    (short *)&MusicVolume,4,
+                    (eightytwofifty==0||numplayers < 2) && (MusicDevice!=NumSoundCards) && x==3,SHX(-5),
+                    (numplayers > 1 && eightytwofifty)||MusicToggle==0||(MusicDevice==NumSoundCards));
+                MusicVolume <<= 2;
+                if(l != MusicVolume)
+                {
+                    STUBBED("Check this");
+                    // !!! FIXME: Used to be Music_ not MUSIC_.  --ryan.
+                    MUSIC_SetVolume( (short) MusicVolume );
+                }
+            }
+            menutext(c,43+16+16+16+16,SHX(-6),(FXDevice==NumSoundCards)||SoundToggle==0,"DUKE TALK");
+            menutext(c,43+16+16+16+16+16,SHX(-7),(FXDevice==NumSoundCards)||SoundToggle==0,"AMBIENCE");
+
+            menutext(c,43+16+16+16+16+16+16,SHX(-8),(FXDevice==NumSoundCards)||SoundToggle==0,"FLIP STEREO");
+
+            if(VoiceToggle) menutext(c+160+40,43+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"ON");
+            else menutext(c+160+40,43+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"OFF");
+
+            if(AmbienceToggle) menutext(c+160+40,43+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"ON");
+            else menutext(c+160+40,43+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"OFF");
+
+            if(ReverseStereo) menutext(c+160+40,43+16+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"ON");
+            else menutext(c+160+40,43+16+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"OFF");
+
+            menutext(c,43+16+16+16+16+16+16+16,SHX(-9),(FXDevice==NumSoundCards)||SoundToggle==0,"OPPONENT SOUND");
+			if(OpponentSoundToggle) menutext(c+160+40,43+16+16+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"ON");
+			else menutext(c+160+40,43+16+16+16+16+16+16+16,0,(FXDevice==NumSoundCards)||SoundToggle==0,"OFF");
+
+            break;
+
+        case 701:
+            c = (320>>1)-120;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"SETUP MOUSE");
+            onbar = ( probey == 0 || probey == 1);
+
+            x = probe(c+6,43,16,8);
+            switch(x)
+            {
+				case -7:
+					gametext(320>>1,43+16*7+3,"*** SHORTCUT: ALT-M ***",0,2+8+16); // center-i
+					break;
+
+                case -1:
+					cmenu(200);
+					probey = 2;
+                    break;
+
+                case 0:
+				case 1:
+					break;
+				case 2:
+
+ 					if ( ((ControllerType == controltype_keyboardandmouse)||
+						(ControllerType == controltype_joystickandmouse)) )
+					{
+						MouseAiming = !MouseAiming;
+						if(MouseAiming)
+							myaimmode = 0;
+
+					}
+                    break;
+
+				case 3:
+
+ 					if ( ((ControllerType == controltype_keyboardandmouse)||
+						(ControllerType == controltype_joystickandmouse)) )
+					{
+						if(!MouseAiming) // means we are in toggle mode
+							myaimmode = !myaimmode;
+					}
+                    break;
+
+                case 4:
+
+ 					if ( ((ControllerType == controltype_keyboardandmouse)||
+						(ControllerType == controltype_joystickandmouse)) )
+					{
+						ud.mouseflip = 1-ud.mouseflip;
+					}
+                    break;
+
+				case 5:
+
+					if (SDL_WM_GrabInput(SDL_GRAB_QUERY)==SDL_GRAB_ON) 
+					{
+						SDL_WM_GrabInput(SDL_GRAB_OFF);
+						SDL_ShowCursor(1);
+					}
+					else
+					{
+						SDL_WM_GrabInput(SDL_GRAB_ON);
+						SDL_ShowCursor(0);
+					}
+					break;
+
+				case 6:
+					cmenu(704); // Button setup
+                    break;
+
+				case 7:
+					cmenu(705); // Digital axes setup
+                    break;
+
+                default:
+                    break;
+            }
+
+			{            
+			short sense;
+
+			sense = CONTROL_GetMouseSensitivity_X();
+			menutext(c,43+16*0,SHX(-7),PHX(-7),"X SENSITIVITY");
+			bar(c+167+40,43+16*0,&sense,1,x==0,SHX(-7),PHX(-7));
+			CONTROL_SetMouseSensitivity_X( sense );
+
+			sense = CONTROL_GetMouseSensitivity_Y();
+			menutext(c,43+16*1,SHX(-7),PHX(-7),"Y SENSITIVITY");
+			bar(c+167+40,43+16*1,&sense,1,x==1,SHX(-7),PHX(-7));
+			CONTROL_SetMouseSensitivity_Y( sense );
+
+			menutext(c,43+16*2,SHX(-7),PHX(-7),"MOUSE AIM TYPE");
+			if(MouseAiming) menutext(c+160+40,43+16*2,SHX(-7),PHX(-7),"HELD");
+			else menutext(c+160+40,43+16*2,SHX(-7),PHX(-7),"TOGGLE");
+
+			menutext(c,43+16*3,SHX(-7),MouseAiming,"MOUSE AIMING");
+			if(myaimmode) menutext(c+160+40,43+16*3,SHX(-7),MouseAiming,"ON");
+			else menutext(c+160+40,43+16*3,SHX(-7),MouseAiming,"OFF");
+
+			menutext(c,43+16*4,SHX(-7),PHX(-7),"MOUSE AIMING FLIP");
+			if(ud.mouseflip) menutext(c+160+40,43+16*4,SHX(-7),PHX(-7),"ON");
+			else menutext(c+160+40,43+16*4,SHX(-7),PHX(-7),"OFF");
+
+
+			menutext(c,43+16*5,SHX(-7),PHX(-7),"MOUSE CURSOR");
+			if(SDL_WM_GrabInput(SDL_GRAB_QUERY)==SDL_GRAB_ON)
+				menutext(c+160+40,43+16*5,SHX(-7),PHX(-7),"TAKEN");
+			else
+				menutext(c+160+40,43+16*5,SHX(-7),PHX(-7),"FREE'D");
+
+			menutext(c,43+16*6,SHX(-7),PHX(-7),"BUTTON SETUP...");
+		
+			menutext(c,43+16*7,SHX(-7),PHX(-7),"DIGITAL AXES SETUP...");	
+			}
+
+            break;
+
+        case 702:
+            c = (320>>1)-120;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"GAME OPTIONS");
+
+            onbar = 0;
+
+			x = probe(c+6,43,16,7);
+
+            switch(x)
+            {
+
+                case -1:
+					cmenu(200); 
+                    break;
+
+                case 0:
+                    ud.shadows = 1-ud.shadows;
+                    break;
+                case 1:
+                    ud.screen_tilting = 1-ud.screen_tilting;
+                    break;
+				case 2:
+					ud.showcinematics = !ud.showcinematics;
+					break;
+				case 3:
+					ud.hideweapon = !ud.hideweapon;
+					vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+					break;
+				case 4:
+					ud.weaponautoswitch = !ud.weaponautoswitch;
+					break;
+				case 5:
+					// FIX_00045: Autoaim mode can now be toggled on/off from menu
+					if( nHostForceDisableAutoaim == 0)
+					{
+						ud.auto_aim++;
+						ud.auto_aim = ((ud.auto_aim-1)%2)+1; // 2 = normal = full; 1 = bullet only
+					}					
+					break;
+                case 6: // parental
+#ifndef AUSTRALIA
+                    cmenu(10000); 
+#endif
+                    break;
+
+			}
+
+
+			menutext(c,43+16*0,SHX(-3),PHX(-3),"SHADOWS");
+            if(ud.shadows) menutext(c+160+40,43+16*0,0,0,"ON");
+            else menutext(c+160+40,43+16*0,0,0,"OFF");
+
+			menutext(c,43+16*1,SHX(-4),PHX(-4),"SCREEN TILTING");
+            switch(ud.screen_tilting)
+            {
+                case 0: menutext(c+160+40,43+16*1,0,0,"OFF");break;
+                case 1: menutext(c+160+40,43+16*1,0,0,"ON");break;
+                case 2: menutext(c+160+40,43+16*1,0,0,"FULL");break;
+            }
+
+			menutext(c,43+16*2,SHX(-3),PHX(-3),"CINEMATICS");
+            if(ud.showcinematics) menutext(c+160+40,43+16*2,0,0,"ON");
+            else menutext(c+160+40,43+16*2,0,0,"OFF");
+
+			menutext(c,43+16*3,SHX(-3),PHX(-3),"WEAPON MODEL");
+			if(ud.hideweapon) menutext(c+160+40,43+16*3,0,0,"OFF");
+            else menutext(c+160+40,43+16*3,0,0,"ON");
+
+			menutext(c,43+16*4,SHX(-3),PHX(-3),"WEAPON SWITCH");
+			if(ud.weaponautoswitch) menutext(c+160+40,43+16*4,0,0,"OFF");
+            else menutext(c+160+40,43+16*4,0,0,"ON");
+
+			switch(ud.auto_aim)
+            {
+                case 0: menutext(c,43+16*5,0,nHostForceDisableAutoaim,"AUTOAIM DISABLED BY HOST"); 
+						break;
+				case 1: menutext(c,43+16*5,0,0,"AUTOAIM BULLET ONLY");
+						break;
+                case 2: menutext(c,43+16*5,0,0,"AUTOAIM REGULAR FULL");
+						break;
+            }
+
+#ifndef AUSTRALIA
+            menutext(c,43+16*6,SHX(-9),PHX(-9),"PARENTAL LOCK");
+#else
+            menutext(c,43+16*6,SHX(-9),1,"PARENTAL LOCK");
+#endif
+
+
+			break;
+
+        case 703:
+
+			// black translucent background underneath lists
+			rotatesprite(0<<16, 0<<16, 65536l<<5, 0, BLANK, 8, 0, 1+2+8+16+64,
+			    scale(0,xdim,320),scale(0,ydim,200),
+			    scale(320-0,xdim,320)-1,scale(200-34,ydim,200)-1);
+
+			c = (320>>1)-120-25;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"SETUP KEYBOARD");
+
+			onbar = 0;
+			x = probeXduke(c+210+lastkeysetup*62,46+16+16+16,0,NUMGAMEFUNCTIONS,20000);
+			if(waiting4key)
+			{
+				probey = waiting4key-1; // force it to stay at the same location
+				x=-(waiting4key-1)-2;
+			}
+
+			if (( KB_KeyPressed( sc_RightArrow ) || KB_KeyPressed( sc_LeftArrow ) ||
+				 KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_kpad_6 )) &&
+				!waiting4key) // set left or right column flag
+			{
+				lastkeysetup = !lastkeysetup;
+				KB_ClearKeyDown( sc_RightArrow ); KB_ClearKeyDown( sc_LeftArrow );
+				KB_ClearKeyDown( sc_kpad_4 ); KB_ClearKeyDown( sc_kpad_6 );
+				sound(KICK_HIT);
+			}
+
+			if (KB_KeyPressed(sc_Delete) && -2>=x && x>=(-NUMGAMEFUNCTIONS-1) && !waiting4key) // clear a key
+			{
+				if(lastkeysetup)
+					CONTROL_MapKey(-x-2, KeyMapping[-x-2].key1, 0);
+				else
+					CONTROL_MapKey(-x-2, 0, KeyMapping[-x-2].key2);
+
+				KB_ClearKeyDown( sc_Delete ); // Avoid repeating delete
+				sound(EXITMENUSOUND);
+			}
+
+			if (0<=x && x<NUMGAMEFUNCTIONS || waiting4key) // set a key
+			{
+				if(!waiting4key)
+				{ 
+					waiting4key = x+1; // so it's always true
+					KB_ClearLastScanCode(); // clear the enter hit that was just hit
+				}
+
+				if(KB_GetLastScanCode())
+				{
+					if(KB_GetLastScanCode() != sc_Escape)  // ESC is reserved for menu. Using it for controls could discard it completely, eg: AutoRun = "Escape"
+					{
+						if(lastkeysetup)		
+							CONTROL_MapKey(waiting4key-1, KeyMapping[waiting4key-1].key1, KB_GetLastScanCode());
+						else
+							CONTROL_MapKey(waiting4key-1, KB_GetLastScanCode(), KeyMapping[waiting4key-1].key2);
+
+						sound(KICK_HIT);
+					}
+
+					KB_ClearLastScanCode();
+					KB_FlushKeyboardQueue();
+					KB_ClearKeysDown();
+					waiting4key = false;
+				}					
+			}
+
+			if(!waiting4key) 
+				switch(x)
+				{
+					case -1:
+						cmenu(200);
+						probey = 1; // back to setup keyboard
+						break;
+				}
+
+			// display and scroll the whole keyboard list
+			j = 7; // -j .. 0 .. j lines => 2*j+1 lines
+			if(-2>=x && x>=(-NUMGAMEFUNCTIONS-1)) // -2 to -54 (53 values -> 0->52)
+			{
+				for(i=-j; i<=+j; i++)
+					if(NUMGAMEFUNCTIONS > (-x-2+i) && (-x-2+i) >= 0)
+					{
+						gametext(c-10,47-16+8*(i+j),gamefunctions[-x-2+i],i?0:0,2+8+16); // disp name
+						if (i ||  lastkeysetup || !waiting4key || (totalclock%128 < 64)) // blink 1st key
+							gametext(c+185,47-16+8*(i+j),KB_ScanCodeToString( KeyMapping[-x-2+i].key1 )?KB_ScanCodeToString( KeyMapping[-x-2+i].key1 ):"...",i?0:0,2+8+16); // center-i
+						if (i || !lastkeysetup || !waiting4key || (totalclock%128 < 64)) // blink 2nd key
+							gametext(c+247,47-16+8*(i+j),KB_ScanCodeToString( KeyMapping[-x-2+i].key2 )?KB_ScanCodeToString( KeyMapping[-x-2+i].key2 ):"...",i?0:0,2+8+16); // center-i
+					}
+			}
+
+			if(waiting4key)
+				gametext(320>>1,47-16+8*(2*j+2)-4,"*** HIT A NEW KEY ***",0,2+8+16); // center-i
+			else
+				gametext(320>>1,47-16+8*(2*j+2)-4,"*** HIT ENTER OR DEL KEY ***",0,2+8+16); // center-i
+
+			break;
+
+        case 704: // mouse button setup, frm menu 701
+            c = (320>>1)-120-25;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"SETUP MOUSE");
+
+			// black translucent background underneath lists
+			rotatesprite(0<<16, 0<<16, 65536l<<5, 0, BLANK, 8, 0, 1+2+8+16+64,
+			    scale(0,xdim,320),scale(40,ydim,200),
+			    scale(320-0,xdim,320)-1,scale(200-75,ydim,200)-1);
+
+			onbar = 0;
+			x = probeXduke(c+146,46+8,8,MAXMOUSEBUTTONS,20000);
+			lastkeysetup = 0;
+			
+			if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed (sc_BackSpace) )
+			{
+				lastkeysetup = 1; // reversed;
+				KB_ClearKeyDown( sc_kpad_4 );
+				KB_ClearKeyDown( sc_LeftArrow );
+				KB_ClearKeyDown(sc_BackSpace);
+				sound(KICK_HIT);
+				x = -x-2;
+			}
+			else if ( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+			{
+				KB_ClearKeyDown( sc_kpad_6 );
+				KB_ClearKeyDown( sc_RightArrow );
+				sound(KICK_HIT);
+				x = -x-2;
+			}
+
+			if (KB_KeyPressed(sc_Delete) && -2>=x && x>=(-MAXMOUSEBUTTONS-1)) // clear a key
+			{
+				MouseMapping[-x-2] = -1;
+				KB_ClearKeyDown( sc_Delete ); // Avoid repeating delete
+				sound(EXITMENUSOUND);
+			}
+
+			if (0<=x && x<MAXMOUSEBUTTONS) // set a function
+			{	
+				if (lastkeysetup) // going up
+				{
+					MouseMapping[x]--;
+					if(MouseMapping[x]==-2)
+						MouseMapping[x] = NUMGAMEFUNCTIONS-1;
+
+				}
+				else
+				{
+					MouseMapping[x]++;
+					if(MouseMapping[x]==NUMGAMEFUNCTIONS)
+						MouseMapping[x] = -1; // Discard
+				}
+			}
+
+			switch(x)
+			{
+				case -1:
+					cmenu(701);
+					probey = 6; // back to the general mouse setup menu
+					break;
+			}
+
+			// display the button list
+			for(i=0; i<MAXMOUSEBUTTONS; i++)
+			{
+				sprintf(tempbuf, "#%d",i);
+				switch(i)
+				{
+					case 0:
+						strcat(tempbuf, " Left");
+						break;
+					case 1:
+						strcat(tempbuf, " Right");
+						break;
+					case 2:
+						strcat(tempbuf, " Middle");
+						break;
+					case 3:
+						strcat(tempbuf, " Wheel up");
+						break;
+					case 4:
+						strcat(tempbuf, " Wheel down");
+						break;
+					default:
+						strcat(tempbuf, " (Extra)");
+						break;
+				}
+				
+				gametext(c,47+i*8,tempbuf,0,2+8+16);
+				gametext(c+121,47+i*8,(MouseMapping[i]!=-1)?CONFIG_FunctionNumToName(MouseMapping[i]):"...",0,2+8+16);
+			}
+
+			gametext(320>>1,47+(MAXMOUSEBUTTONS+1)*8,"*** USE ARROWS OR DEL KEY ***",0,2+8+16);
+
+			break;
+
+        case 705: // digital axes setup, from menu 701
+            c = (320>>1)-120-25;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"DIGITAL AXES");
+
+			// black translucent background underneath lists
+			rotatesprite(0<<16, 0<<16, 65536l<<5, 0, BLANK, 8, 0, 1+2+8+16+64,
+			    scale(0,xdim,320),scale(40,ydim,200),
+			    scale(320-0,xdim,320)-1,scale(200-100,ydim,200)-1);
+
+			onbar = 0;
+			x = probeXduke(c+146,46+8,8,MAXMOUSEAXES*2,20000);
+			lastkeysetup = 0;
+			
+			if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed (sc_BackSpace) )
+			{
+				lastkeysetup = 1; // reversed;
+				KB_ClearKeyDown( sc_kpad_4 );
+				KB_ClearKeyDown( sc_LeftArrow );
+				KB_ClearKeyDown(sc_BackSpace);
+				sound(KICK_HIT);
+				x = -x-2;
+			}
+			else if ( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+			{
+				KB_ClearKeyDown( sc_kpad_6 );
+				KB_ClearKeyDown( sc_RightArrow );
+				sound(KICK_HIT);
+				x = -x-2;
+			}
+
+			if (KB_KeyPressed(sc_Delete) && -2>=x && x>=(-(MAXMOUSEAXES*2)-1)) // clear a key
+			{
+				MouseDigitalAxeMapping[(-x-2)>>1][(-x-2)&1] = -1;
+				KB_ClearKeyDown( sc_Delete ); // Avoid repeating delete
+				sound(EXITMENUSOUND);
+			}
+
+			if (0<=x && x<(MAXMOUSEAXES*2)) // set a function
+			{	
+				if (lastkeysetup) // going up
+				{
+					MouseDigitalAxeMapping[x>>1][x&1]--;
+					if(MouseDigitalAxeMapping[x>>1][x&1]==-2)
+						MouseDigitalAxeMapping[x>>1][x&1] = NUMGAMEFUNCTIONS-1;
+				}
+				else
+				{
+					MouseDigitalAxeMapping[x>>1][x&1]++;
+					if(MouseDigitalAxeMapping[x>>1][x&1]==NUMGAMEFUNCTIONS)
+						MouseDigitalAxeMapping[x>>1][x&1] = -1; // Discard
+				}
+			}
+
+			switch(x)
+			{
+				case -1:
+					cmenu(701);
+					probey = 7; // back to the general mouse setup menu
+					break;
+			}
+
+			// display the button list
+			for(i=0; i<(MAXMOUSEAXES*2); i++)
+			{
+				*tempbuf = 0;
+				switch(i)
+				{
+					case 0:
+						strcat(tempbuf, "X left");
+						break;
+					case 1:
+						strcat(tempbuf, "X right");
+						break;
+					case 2:
+						strcat(tempbuf, "Y up");
+						break;
+					case 3:
+						strcat(tempbuf, "Y down");
+						break;
+					default:
+						break;
+				}
+				
+				gametext(c,47+i*8,tempbuf,0,2+8+16);
+				gametext(c+121,47+i*8,(MouseDigitalAxeMapping[i>>1][i&1]!=-1)?CONFIG_FunctionNumToName(MouseDigitalAxeMapping[i>>1][i&1]):"...",0,2+8+16);
+			}
+
+			gametext(320>>1,47+(4+1)*8,"*** USE ARROWS OR DEL KEY ***",0,2+8+16);
+
+			break;
+
+        case 706: // Video setup
+			// FIX_00042: Build in Video setup.
+			c = (320>>1)-120;
+            rotatesprite(320<<15,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"VIDEO SETUP");
+
+			onbar = (probey == 3 || probey == 4);
+			x = probe(c+6,43,16,6);
+			
+			switch(x)
+			{
+				case -7: // cursor idle on the FPS option (5)
+					gametext(320>>1,43+16*7,"*** SHORTCUT: TYPE DNRATE ***",0,2+8+16); // center-i
+					
+					break;
+
+				case -3: // cursor idle on the togglefullscreen option (1)
+					gametext(320>>1,43+16*7,"*** SHORTCUT: ALT-ENTER ***",0,2+8+16); // center-i
+
+					break;
+
+				case -2: // cursor idle on the resolution option (0)
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) )
+					{
+						current_resolution--;
+						if(current_resolution == -1) 
+							current_resolution = 0;
+						lastkeysetup = 1; // indicates we changed
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						sound(PISTOL_BODYHIT);
+					}
+					else if ( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+					{
+						current_resolution++; // reversed;
+						if(current_resolution == validmodecnt) 
+							current_resolution = validmodecnt-1;
+						lastkeysetup = 1; // indicates we changed
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					}
+
+					if(lastkeysetup)
+						gametext(320>>1,43+16*7,"*** HIT ENTER TO VALIDATE ***",0,2+8+16); // center-i
+					else
+						gametext(320>>1,43+16*7,"*** LEFT/RIGHT ARROW TO SELECT ***",0,2+8+16); // center-i
+
+					break;
+
+				case -1:
+					cmenu(200);
+					probey = 4; // back to the general option menu
+					break;
+
+				case 0:
+					if(lastkeysetup)
+						setgamemode(ScreenMode,validmodexdim[current_resolution],validmodeydim[current_resolution]);
+					lastkeysetup = 0; // indicating changes are done
+					break;
+
+				case 1:
+					BFullScreen = !BFullScreen;
+					SDL_QuitSubSystem(SDL_INIT_VIDEO);
+					_platform_init(0, NULL, "Duke Nukem 3D", "Duke3D");
+					_setgamemode(ScreenMode,validmodexdim[current_resolution],validmodeydim[current_resolution]);
+					break;
+
+                case 2:
+                    ud.detail = 1-ud.detail;
+                    break;
+
+				case 5:
+					ud.tickrate ^= 1;
+					vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+					break;
+			}
+				
+			menutext(c,43,0,0,"RESOLUTION");
+			sprintf(tempbuf, "%d x %d", validmodexdim[current_resolution],validmodeydim[current_resolution]);
+			if (lastkeysetup == 0 || (totalclock%64 < 32)) // blink color after change
+				menutext(c+150,43,0,0,tempbuf);
+			else
+				menutext(c+150,43,0,1,tempbuf);
+
+			menutext(c,43+16*1,SHX(-3),PHX(-3),"FULLSCREEN");
+			menutext(c+160+40,43+16*1,0,0,BFullScreen?"ON":"OFF");
+			
+			menutext(c,43+16*2,SHX(-2),PHX(-2),"DETAIL");
+			menutext(c+160+40,43+16*2,0,0,ud.detail?"HIGH":"LOW");
+			{
+				int32 screen_size = ud.screen_size;
+
+				// FIX_00027: Added an extra small statusbar (HUD)
+	            menutext(c,43+16*3,SHX(-5),PHX(-5),"SCREEN SIZE");
+	            bar(c+167+40,43+16*3,(short *)&screen_size,-4,x==3,SHX(-5),PHX(-5));
+				if(ud.screen_size==4)
+				{
+					if(screen_size==0)
+					{
+						ud.extended_screen_size++;
+						if(ud.extended_screen_size==2)
+						{
+							ud.extended_screen_size = 1;
+							ud.screen_size -= 4;
+						}
+					}
+					else if(screen_size==8)
+					{
+						ud.extended_screen_size--;
+						if(ud.extended_screen_size<0)
+						{
+							ud.extended_screen_size=0;
+							ud.screen_size += 4;
+						}
+					}
+				}
+				else
+					ud.screen_size = screen_size;
+			}
+
+            menutext(c,43+16*4,SHX(-6),PHX(-6),"BRIGHTNESS");
+            bar(c+167+40,43+16*4,(short *)&ud.brightness,8,x==4,SHX(-6),PHX(-6));
+            if(x==4) setbrightness(ud.brightness>>2,&ps[myconnectindex].palette[0]);
+
+			menutext(c,43+16*5,SHX(-2),PHX(-2),"SHOW FPS");
+			menutext(c+160+40,43+16*5,0,0,(ud.tickrate&1)?"ON":"OFF");
+
+			break;
+
+        case 350:
+            cmenu(351);
+            screencapt = 1;
+            displayrooms(myconnectindex,65536);
+            savetemp("duke3d.tmp",waloff[MAXTILES-1],160*100);
+            screencapt = 0;
+            break;
+
+        case 360:
+        case 361:
+        case 362:
+        case 363:
+        case 364:
+        case 365:
+        case 366:
+        case 367:
+        case 368:
+        case 369:
+        case 351:
+        case 300:
+
+            c = 320>>1;
+            rotatesprite(c<<16,200<<15,65536L,0,MENUSCREEN,16,0,10+64,0,0,xdim-1,ydim-1);
+            rotatesprite(c<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+
+            if(current_menu == 300) menutext(c,24,0,0,"LOAD GAME");
+            else menutext(c,24,0,0,"SAVE GAME");
+
+            if(current_menu >= 360 && current_menu <= 369 )
+            {
+                sprintf(tempbuf,"PLAYERS: %-2d                      ",ud.multimode);
+                gametext(160,158,tempbuf,0,2+8+16);
+                sprintf(tempbuf,"EPISODE: %-2d / LEVEL: %-2d / SKILL: %-2d",1+ud.volume_number,1+ud.level_number,ud.player_skill);
+                gametext(160,170,tempbuf,0,2+8+16);
+
+                x = strget((320>>1),184,&ud.savegame[current_menu-360][0],19, 999 );
+
+                if(x == -1)
+                {
+            //        readsavenames();
+                    ps[myconnectindex].gm = MODE_GAME;
+                    if(ud.multimode < 2  && ud.recstat != 2)
+                    {
+                        ready2send = 1;
+                        totalclock = ototalclock;
+                    }
+                    goto DISPLAYNAMES;
+                }
+
+                if( x == 1 )
+                {
+                    if( ud.savegame[current_menu-360][0] == 0 )
+                    {
+                        KB_FlushKeyboardQueue();
+                        cmenu(351);
+                    }
+                    else
+                    {
+                        if(ud.multimode > 1)
+                            saveplayer(-1-(current_menu-360));
+                        else saveplayer(current_menu-360);
+                        lastsavedpos = current_menu-360;
+                        ps[myconnectindex].gm = MODE_GAME;
+
+                        if(ud.multimode < 2  && ud.recstat != 2)
+                        {
+                            ready2send = 1;
+                            totalclock = ototalclock;
+                        }
+                        KB_ClearKeyDown(sc_Escape);
+                        sound(EXITMENUSOUND);
+                    }
+                }
+
+                rotatesprite(101<<16,97<<16,65536,512,MAXTILES-1,-32,0,2+4+8+64,0,0,xdim-1,ydim-1);
+                dispnames();
+                rotatesprite((c+67+strlen(&ud.savegame[current_menu-360][0])*4)<<16,(50+12*probey)<<16,32768L-10240,0,SPINNINGNUKEICON+(((totalclock)>>3)%7),0,0,10,0,0,xdim-1,ydim-1);
+                break;
+            }
+
+           last_threehundred = probey;
+
+            x = probe(c+68,54,12,10);
+
+          if(current_menu == 300)
+          {
+              if( ud.savegame[probey][0] )
+              {
+                  if( lastprobey != probey )
+                  {
+                     loadpheader(probey,&volnum,&levnum,&plrskl,&numplr);
+                     lastprobey = probey;
+                  }
+
+                  rotatesprite(101<<16,97<<16,65536L,512,MAXTILES-3,-32,0,4+10+64,0,0,xdim-1,ydim-1);
+                  sprintf(tempbuf,"PLAYERS: %-2d                      ",numplr);
+                  gametext(160,158,tempbuf,0,2+8+16);
+                  sprintf(tempbuf,"EPISODE: %-2d / LEVEL: %-2d / SKILL: %-2d",1+volnum,1+levnum,plrskl);
+                  gametext(160,170,tempbuf,0,2+8+16);
+              }
+              else menutext(69,70,0,0,"EMPTY");
+          }
+          else
+          {
+              if( ud.savegame[probey][0] )
+              {
+                  if(lastprobey != probey)
+                      loadpheader(probey,&volnum,&levnum,&plrskl,&numplr);
+                  lastprobey = probey;
+                  rotatesprite(101<<16,97<<16,65536L,512,MAXTILES-3,-32,0,4+10+64,0,0,xdim-1,ydim-1);
+              }
+              else menutext(69,70,0,0,"EMPTY");
+              sprintf(tempbuf,"PLAYERS: %-2d                      ",ud.multimode);
+              gametext(160,158,tempbuf,0,2+8+16);
+              sprintf(tempbuf,"EPISODE: %-2d / LEVEL: %-2d / SKILL: %-2d",1+ud.volume_number,1+ud.level_number,ud.player_skill);
+              gametext(160,170,tempbuf,0,2+8+16);
+          }
+
+            switch( x )
+            {
+                case -1:
+                    if(current_menu == 300) //load game
+                    {
+                        if( (ps[myconnectindex].gm&MODE_GAME) != MODE_GAME)
+                        {
+                            cmenu(0);
+                            break;
+                        }
+                        else
+                            ps[myconnectindex].gm &= ~MODE_MENU;
+                    }
+                    else // save game
+                        ps[myconnectindex].gm = MODE_GAME;
+
+                    if(ud.multimode < 2 && ud.recstat != 2)
+                    {
+                        ready2send = 1;
+                        totalclock = ototalclock;
+                    }
+
+                    break;
+                case 0:
+                case 1:
+                case 2:
+                case 3:
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                case 8:
+                case 9:
+                    if( current_menu == 300)
+                    {
+                        if( ud.savegame[x][0] )
+                            current_menu = (1000+x);
+                    }
+                    else
+                    {
+                        if( ud.savegame[x][0] != 0)
+                            current_menu = 2000+x;
+                        else
+                        {
+                            KB_FlushKeyboardQueue();
+                            current_menu = (360+x);
+                            ud.savegame[x][0] = 0;
+                            inputloc = 0;
+                        }
+                    }
+                    break;
+            }
+
+            DISPLAYNAMES:
+            dispnames();
+            break;
+
+        case 400: // help
+        case 401:
+		if(!VOLUMEONE)
+		{
+			c = 320>>1;
+
+            if( KB_KeyPressed( sc_LeftArrow ) ||
+                KB_KeyPressed( sc_kpad_4 ) ||
+                KB_KeyPressed( sc_UpArrow ) ||
+                KB_KeyPressed( sc_PgUp ) ||
+                KB_KeyPressed( sc_kpad_8 ) )
+            {
+                KB_ClearKeyDown(sc_LeftArrow);
+                KB_ClearKeyDown(sc_kpad_4);
+                KB_ClearKeyDown(sc_UpArrow);
+                KB_ClearKeyDown(sc_PgUp);
+                KB_ClearKeyDown(sc_kpad_8);
+
+                sound(KICK_HIT);
+                current_menu--;
+                if(current_menu < 400) current_menu = 401;
+            }
+            else if(
+                KB_KeyPressed( sc_PgDn ) ||
+                KB_KeyPressed( sc_Enter ) ||
+                KB_KeyPressed( sc_kpad_Enter ) ||
+                KB_KeyPressed( sc_RightArrow ) ||
+                KB_KeyPressed( sc_DownArrow ) ||
+                KB_KeyPressed( sc_kpad_2 ) ||
+                KB_KeyPressed( sc_kpad_9 ) ||
+                KB_KeyPressed( sc_Space ) ||
+                KB_KeyPressed( sc_kpad_6 ) )
+            {
+                KB_ClearKeyDown(sc_PgDn);
+                KB_ClearKeyDown(sc_Enter);
+                KB_ClearKeyDown(sc_RightArrow);
+                KB_ClearKeyDown(sc_kpad_Enter);
+                KB_ClearKeyDown(sc_kpad_6);
+                KB_ClearKeyDown(sc_kpad_9);
+                KB_ClearKeyDown(sc_kpad_2);
+                KB_ClearKeyDown(sc_DownArrow);
+                KB_ClearKeyDown(sc_Space);
+                sound(KICK_HIT);
+                current_menu++;
+                if(current_menu > 401) current_menu = 400;
+            }
+
+            if( KB_KeyPressed(sc_Escape) )
+            {
+				KB_ClearKeyDown(sc_Escape); // or else ESC will be activated in cmenu(0)
+                if(ps[myconnectindex].gm&MODE_GAME)
+                    cmenu(50);
+                else cmenu(0);
+                return;
+            }
+
+            flushperms();
+            switch(current_menu)
+            {
+                case 400:
+                    rotatesprite(0,0,65536L,0,TEXTSTORY,0,0,10+16+64, 0,0,xdim-1,ydim-1);
+                    break;
+                case 401:
+                    rotatesprite(0,0,65536L,0,F1HELP,0,0,10+16+64, 0,0,xdim-1,ydim-1);
+                    break;
+            }
+		break;
+		}
+		
+		case 402:
+        case 403:
+		if(VOLUMEONE)
+		{
+            c = 320>>1;
+
+            if( KB_KeyPressed( sc_LeftArrow ) ||
+                KB_KeyPressed( sc_kpad_4 ) ||
+                KB_KeyPressed( sc_UpArrow ) ||
+                KB_KeyPressed( sc_PgUp ) ||
+                KB_KeyPressed( sc_kpad_8 ) )
+            {
+                KB_ClearKeyDown(sc_LeftArrow);
+                KB_ClearKeyDown(sc_kpad_4);
+                KB_ClearKeyDown(sc_UpArrow);
+                KB_ClearKeyDown(sc_PgUp);
+                KB_ClearKeyDown(sc_kpad_8);
+
+                sound(KICK_HIT);
+                current_menu--;
+                if(current_menu < 400) current_menu = 403;
+            }
+            else if(
+                KB_KeyPressed( sc_PgDn ) ||
+                KB_KeyPressed( sc_Enter ) ||
+                KB_KeyPressed( sc_kpad_Enter ) ||
+                KB_KeyPressed( sc_RightArrow ) ||
+                KB_KeyPressed( sc_DownArrow ) ||
+                KB_KeyPressed( sc_kpad_2 ) ||
+                KB_KeyPressed( sc_kpad_9 ) ||
+                KB_KeyPressed( sc_Space ) ||
+                KB_KeyPressed( sc_kpad_6 ) )
+            {
+                KB_ClearKeyDown(sc_PgDn);
+                KB_ClearKeyDown(sc_Enter);
+                KB_ClearKeyDown(sc_RightArrow);
+                KB_ClearKeyDown(sc_kpad_Enter);
+                KB_ClearKeyDown(sc_kpad_6);
+                KB_ClearKeyDown(sc_kpad_9);
+                KB_ClearKeyDown(sc_kpad_2);
+                KB_ClearKeyDown(sc_DownArrow);
+                KB_ClearKeyDown(sc_Space);
+                sound(KICK_HIT);
+                current_menu++;
+                if(current_menu > 403) current_menu = 400;
+            }
+
+            if( KB_KeyPressed(sc_Escape) )
+            {
+                if(ps[myconnectindex].gm&MODE_GAME)
+                    cmenu(50);
+                else cmenu(0);
+                return;
+            }
+
+            flushperms();
+            rotatesprite(0,0,65536L,0,ORDERING+current_menu-400,0,0,10+16+64,0,0,xdim-1,ydim-1);
+		}
+		break;
+
+        case 500: // from f10
+		case 501: // from menu 0
+		case 502: // from menu 50
+
+            c = 320>>1;
+
+            gametext(c,90,"Are you sure you want to quit?",0,2+8+16);
+            gametext(c,99,"(Y/N)",0,2+8+16);
+
+			_handle_events();
+            if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+				gameexitanycase();
+            }
+
+            x = probe(186,124,0,0);
+            if(x == -1 || KB_KeyPressed(sc_N) || RMB)
+            {
+                KB_ClearKeyDown(sc_N);
+                quittimer = 0;
+				// FIX_00073: menu off messed up. While in game hit Esc -> select quit -> press esc => stuck in menu
+				if (current_menu==500)
+					ps[myconnectindex].gm &= ~MODE_MENU;
+				else if(current_menu==501)
+					cmenu(0);
+				else
+					cmenu(50);
+            }
+            break;
+
+		case 503:
+            c = 320>>1;
+            gametext(c,90,"Quit to Title?",0,2+8+16);
+            gametext(c,99,"(Y/N)",0,2+8+16);
+
+			_handle_events();
+            if( KB_KeyPressed(sc_Space) || KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || KB_KeyPressed(sc_Y) || LMB )
+            {
+                KB_FlushKeyboardQueue();
+                ps[myconnectindex].gm = MODE_DEMO;
+                if(ud.recstat == 1)
+                    closedemowrite();
+				if( ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1)
+					dobonus(1);
+				ud.last_level = -1;
+                cmenu(0);
+            }
+
+            x = probe(186,124,0,0);
+
+            if(x == -1 || KB_KeyPressed(sc_N) || RMB)
+            {
+                cmenu(50);
+                if(ud.multimode < 2  && ud.recstat != 2)
+                {
+                    ready2send = 1;
+                    totalclock = ototalclock;
+                }
+            }
+
+            break;
+
+        case 601:
+            displayfragbar();
+            rotatesprite(160<<16,29<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,34,0,0,&ud.user_name[myconnectindex][0]);
+
+            sprintf(tempbuf,"Waiting for master");
+            gametext(160,50,tempbuf,0,2+8+16);
+            gametext(160,59,"to select level",0,2+8+16);
+
+            if( KB_KeyPressed(sc_Escape) )
+            {
+                KB_ClearKeyDown(sc_Escape);
+                sound(EXITMENUSOUND);
+                cmenu(0);
+            }
+            break;
+
+        case 602:
+            if(menunamecnt == 0)
+            {
+        //        getfilenames("SUBD");
+                getfilenames("*.MAP");
+                sortfilenames();
+                if (menunamecnt == 0)
+                    cmenu(600);
+            }
+        case 603:
+            c = (320>>1) - 120;
+            displayfragbar();
+            rotatesprite(320>>1<<16,19<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(320>>1,24,0,0,"USER MAPS");
+            for(x=0;x<menunamecnt;x++)
+            {
+                if(x == fileselect)
+                    minitext(15 + (x/15)*54,32 + (x%15)*8,menuname[x],0,26);
+                else minitext(15 + (x/15)*54,32 + (x%15)*8,menuname[x],16,26);
+            }
+
+            fileselect = probey;
+            if( KB_KeyPressed( sc_LeftArrow ) || KB_KeyPressed( sc_kpad_4 ) || ((buttonstat&1) && minfo.dyaw < -256 ) )
+            {
+                KB_ClearKeyDown( sc_LeftArrow );
+                KB_ClearKeyDown( sc_kpad_4 );
+                probey -= 15;
+                if(probey < 0) probey += 15;
+                else sound(KICK_HIT);
+            }
+            if( KB_KeyPressed( sc_RightArrow ) || KB_KeyPressed( sc_kpad_6 ) || ((buttonstat&1) && minfo.dyaw > 256 ) )
+            {
+                KB_ClearKeyDown( sc_RightArrow );
+                KB_ClearKeyDown( sc_kpad_6 );
+                probey += 15;
+                if(probey >= menunamecnt)
+                    probey -= 15;
+                else sound(KICK_HIT);
+            }
+
+            onbar = 0;
+            x = probe(0,0,0,menunamecnt);
+
+            if(x == -1) cmenu(600);
+            else if(x >= 0)
+            {
+                tempbuf[0] = 8;
+                tempbuf[1] = ud.m_level_number = 6;
+                tempbuf[2] = ud.m_volume_number = 0;
+                tempbuf[3] = ud.m_player_skill+1;
+
+                if(ud.player_skill == 3)
+                    ud.m_respawn_monsters = 1;
+                else ud.m_respawn_monsters = 0;
+
+                if(ud.m_coop == 0) ud.m_respawn_items = 1;
+                else ud.m_respawn_items = 0;
+
+                ud.m_respawn_inventory = 1;
+
+                tempbuf[4] = ud.m_monsters_off;
+                tempbuf[5] = ud.m_respawn_monsters;
+                tempbuf[6] = ud.m_respawn_items;
+                tempbuf[7] = ud.m_respawn_inventory;
+                tempbuf[8] = ud.m_coop;
+                tempbuf[9] = ud.m_marker;
+
+                x = strlen(menuname[probey]);
+
+                copybufbyte(menuname[probey],tempbuf+10,x);
+                copybufbyte(menuname[probey],boardfilename,x+1);
+
+                for(c=connecthead;c>=0;c=connectpoint2[c])
+                    if(c != myconnectindex)
+                        sendpacket(c,tempbuf,x+10);
+
+                newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill+1);
+                enterlevel(MODE_GAME);
+            }
+            break;
+
+        case 600: // multiplayer and bot menu
+            c = (320>>1) - 120;
+            if((ps[myconnectindex].gm&MODE_GAME) != MODE_GAME)
+                displayfragbar();
+            rotatesprite(160<<16,26<<16,65536L,0,MENUBAR,16,0,10,0,0,xdim-1,ydim-1);
+            menutext(160,31,0,0,&ud.user_name[myconnectindex][0]);
+
+            x = probe(c,57-8,16,8);
+
+            switch(x)
+            {
+				// FIX_00068: menu "New Game" in multiplayer mode now allowing left/right arrow for selection
+				case -7: // idle on case 5
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) ||
+						 KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ))
+					{
+						if(ud.m_coop == 1)
+							ud.m_ffire = !ud.m_ffire;
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					} 
+                    break;
+
+				case -6: // idle on case 4
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) ||
+						 KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ))
+					{
+						if(ud.m_coop == 0)
+							ud.m_marker = !ud.m_marker;
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					} 
+                    break;
+
+				case -5: // idle on case 3
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) )
+					{
+						if(ud.m_monsters_off == 1 && ud.m_player_skill > 0)
+							ud.m_monsters_off = 0;
+
+						if(ud.m_monsters_off == 1)
+						{
+							ud.m_monsters_off = 0;
+							ud.m_player_skill = 3;
+						}
+						else
+						{
+							ud.m_player_skill--;
+							if(ud.m_player_skill < 0)
+							{
+								ud.m_player_skill = 0;
+								ud.m_monsters_off = 1;
+							}
+						}
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						sound(PISTOL_BODYHIT);
+					} 
+					else if( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+					{	
+						if(ud.m_monsters_off == 1 && ud.m_player_skill > 0)
+							ud.m_monsters_off = 0;
+
+						if(ud.m_monsters_off == 0)
+						{
+							ud.m_player_skill++;
+							if(ud.m_player_skill > 3)
+							{
+								ud.m_player_skill = 0;
+								ud.m_monsters_off = 1;
+							}
+						}
+						else
+						{
+							ud.m_monsters_off = 0;
+							ud.m_player_skill = 0;
+						}
+
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					}
+                    break;
+
+#ifndef ONELEVELDEMO
+
+				case -4: // idle on case 2
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) )
+					{
+						ud.m_level_number--;
+if (!VOLUMEONE)
+{
+						if(ud.m_volume_number == 0 && ud.m_level_number < 0)
+							ud.m_level_number = 6;
+}
+else
+{
+						if(ud.m_volume_number == 0 && ud.m_level_number < 0)
+							ud.m_level_number = 5;
+}
+						if(ud.m_level_number < 0) ud.m_level_number = 10;
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						sound(PISTOL_BODYHIT);
+					}
+					else if ( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+					{
+						ud.m_level_number++;
+if (!VOLUMEONE)
+{
+						if(ud.m_volume_number == 0 && ud.m_level_number > 6)
+							ud.m_level_number = 0;
+}
+else
+{
+						if(ud.m_volume_number == 0 && ud.m_level_number > 5)
+							ud.m_level_number = 0;
+}
+						if(ud.m_level_number > 10) ud.m_level_number = 0;
+
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					}
+					break;
+#endif
+
+				case -3: // Idle on case 1
+if (!VOLUMEONE)
+{
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) )
+					{
+						ud.m_volume_number--;
+if(PLUTOPAK)
+						{	if(ud.m_volume_number < 0 ) ud.m_volume_number = 3; }
+else
+						{	if(ud.m_volume_number < 0) ud.m_volume_number = 2; }
+
+						if(ud.m_volume_number == 0 && ud.m_level_number > 6)
+							ud.m_level_number = 0;
+						if(ud.m_level_number > 10) ud.m_level_number = 0;
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						sound(PISTOL_BODYHIT);
+					}
+					else if( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+					{
+						ud.m_volume_number++;
+if(PLUTOPAK)
+						{	if(ud.m_volume_number > 3) ud.m_volume_number = 0; }
+else
+						{	if(ud.m_volume_number > 2) ud.m_volume_number = 0; }
+
+						if(ud.m_volume_number == 0 && ud.m_level_number > 6)
+							ud.m_level_number = 0;
+						if(ud.m_level_number > 10) ud.m_level_number = 0;
+
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					}
+}
+					break;
+
+				case -2: // Idle on case 0
+					if ( KB_KeyPressed( sc_kpad_4 ) || KB_KeyPressed( sc_LeftArrow ) )
+					{
+						ud.m_coop--;
+						if(ud.m_coop == -1) ud.m_coop = 2;
+
+						KB_ClearKeyDown( sc_kpad_4 );
+						KB_ClearKeyDown( sc_LeftArrow );
+						sound(PISTOL_BODYHIT);
+					} 
+					else if( KB_KeyPressed( sc_kpad_6 ) || KB_KeyPressed( sc_RightArrow ) )
+					{	
+						ud.m_coop++;
+						if(ud.m_coop == 3) ud.m_coop = 0;
+
+						KB_ClearKeyDown( sc_kpad_6 );
+						KB_ClearKeyDown( sc_RightArrow );
+						sound(PISTOL_BODYHIT);
+					}
+                    break;
+
+                case -1:
+                    ud.m_recstat = 0;
+                    if(ps[myconnectindex].gm&MODE_GAME) cmenu(50);
+                    else cmenu(0);
+                    break;
+                case 0:
+                    ud.m_coop++;
+                    if(ud.m_coop == 3) ud.m_coop = 0;
+                    break;
+                case 1:
+if (!VOLUMEONE)
+{                    ud.m_volume_number++;
+if(PLUTOPAK)
+					{	if(ud.m_volume_number > 3) ud.m_volume_number = 0; }
+else
+					{	if(ud.m_volume_number > 2) ud.m_volume_number = 0; }
+
+                    if(ud.m_volume_number == 0 && ud.m_level_number > 6)
+                        ud.m_level_number = 0;
+                    if(ud.m_level_number > 10) ud.m_level_number = 0;
+}
+                    break;
+                case 2:
+#ifndef ONELEVELDEMO
+                    ud.m_level_number++;
+if (!VOLUMEONE)
+{
+                    if(ud.m_volume_number == 0 && ud.m_level_number > 6)
+                        ud.m_level_number = 0;
+}
+else
+{
+                    if(ud.m_volume_number == 0 && ud.m_level_number > 5)
+                        ud.m_level_number = 0;
+}
+                    if(ud.m_level_number > 10) ud.m_level_number = 0;
+#endif
+                    break;
+                case 3:
+                    if(ud.m_monsters_off == 1 && ud.m_player_skill > 0)
+                        ud.m_monsters_off = 0;
+
+                    if(ud.m_monsters_off == 0)
+                    {
+                        ud.m_player_skill++;
+                        if(ud.m_player_skill > 3)
+                        {
+                            ud.m_player_skill = 0;
+                            ud.m_monsters_off = 1;
+                        }
+                    }
+                    else 
+					{
+						ud.m_monsters_off = 0;
+						ud.m_player_skill = 0;
+					}
+
+                    break;
+
+                case 4:
+                    if(ud.m_coop == 0)
+                        ud.m_marker = !ud.m_marker;
+                    break;
+
+                case 5:
+                    if(ud.m_coop == 1)
+                        ud.m_ffire = !ud.m_ffire;
+                    break;
+
+                case 6: // select user map
+if(!VOLUMEONE)
+{                    if(boardfilename[0] == 0) break;
+
+                    tempbuf[0] = 5;
+                    tempbuf[1] = ud.m_level_number = 7;
+                    tempbuf[2] = ud.m_volume_number = 0;
+                    tempbuf[3] = ud.m_player_skill+1;
+
+                    ud.level_number = ud.m_level_number;
+                    ud.volume_number = ud.m_volume_number;
+
+                    if( ud.m_player_skill == 3 ) ud.m_respawn_monsters = 1;
+                    else ud.m_respawn_monsters = 0;
+
+                    if(ud.m_coop == 0) ud.m_respawn_items = 1;
+                    else ud.m_respawn_items = 0;
+
+                    ud.m_respawn_inventory = 1;
+
+                    tempbuf[4] = ud.m_monsters_off;
+                    tempbuf[5] = ud.m_respawn_monsters;
+                    tempbuf[6] = ud.m_respawn_items;
+                    tempbuf[7] = ud.m_respawn_inventory;
+                    tempbuf[8] = ud.m_coop;
+                    tempbuf[9] = ud.m_marker;
+                    tempbuf[10] = ud.m_ffire;
+
+                    for(c=connecthead;c>=0;c=connectpoint2[c])
+                    {
+                        resetweapons(c);
+                        resetinventory(c);
+
+                        if(c != myconnectindex)
+                            sendpacket(c,tempbuf,11);
+                    }
+
+                    newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill+1);
+                    enterlevel(MODE_GAME);
+
+                    return;
+}
+                case 7: // start game
+
+                    tempbuf[0] = 5;
+                    tempbuf[1] = ud.m_level_number;
+                    tempbuf[2] = ud.m_volume_number;
+                    tempbuf[3] = ud.m_player_skill+1;
+
+                    if( ud.m_player_skill == 3 ) ud.m_respawn_monsters = 1;
+                    else ud.m_respawn_monsters = 0;
+
+                    if(ud.m_coop == 0) ud.m_respawn_items = 1;
+                    else ud.m_respawn_items = 0;
+
+                    ud.m_respawn_inventory = 1;
+
+                    tempbuf[4] = ud.m_monsters_off;
+                    tempbuf[5] = ud.m_respawn_monsters;
+                    tempbuf[6] = ud.m_respawn_items;
+                    tempbuf[7] = ud.m_respawn_inventory;
+                    tempbuf[8] = ud.m_coop;
+                    tempbuf[9] = ud.m_marker;
+                    tempbuf[10] = ud.m_ffire;
+
+                    for(c=connecthead;c>=0;c=connectpoint2[c])
+                    {
+                        resetweapons(c);
+                        resetinventory(c);
+
+                        if(c != myconnectindex)
+                            sendpacket(c,tempbuf,11);
+                    }
+
+                    newgame(ud.m_volume_number,ud.m_level_number,ud.m_player_skill+1);
+                    enterlevel(MODE_GAME);
+
+                    return;
+
+            }
+
+            c += 40;
+
+            if(ud.m_coop==1) gametext(c+70,57-7-9,"COOPERATIVE PLAY",0,2+8+16);
+            else if(ud.m_coop==2) gametext(c+70,57-7-9,"DUKEMATCH (NO SPAWN)",0,2+8+16);
+            else gametext(c+70,57-7-9,"DUKEMATCH (SPAWN)",0,2+8+16);
+
+ if(VOLUMEONE)
+            gametext(c+70,57+16-7-9,volume_names[ud.m_volume_number],0,2+8+16);
+ else
+            gametext(c+70,57+16-7-9,volume_names[ud.m_volume_number],0,2+8+16);
+ 
+            gametext(c+70,57+16+16-7-9,&level_names[11*ud.m_volume_number+ud.m_level_number][0],0,2+8+16);
+
+            if(ud.m_monsters_off == 0 || ud.m_player_skill > 0)
+                gametext(c+70,57+16+16+16-7-9,skill_names[ud.m_player_skill],0,2+8+16);
+            else gametext(c+70,57+16+16+16-7-9,"NONE",0,2+8+16);
+
+            if(ud.m_coop == 0)
+            {
+                if(ud.m_marker)
+                    gametext(c+70,57+16+16+16+16-7-9,"ON",0,2+8+16);
+                else gametext(c+70,57+16+16+16+16-7-9,"OFF",0,2+8+16);
+            }
+
+            if(ud.m_coop == 1)
+            {
+                if(ud.m_ffire)
+                    gametext(c+70,57+16+16+16+16+16-7-9,"ON",0,2+8+16);
+                else gametext(c+70,57+16+16+16+16+16-7-9,"OFF",0,2+8+16);
+            }
+
+            c -= 44;
+
+            menutext(c,57-9,SHX(-2),PHX(-2),"GAME TYPE");
+
+if (VOLUMEONE)
+{            sprintf(tempbuf,"EPISODE %ld",ud.m_volume_number+1);
+            menutext(c,57+16-9,SHX(-3),1,tempbuf);
+} 
+else 
+{
+            sprintf(tempbuf,"EPISODE %ld",ud.m_volume_number+1);
+            menutext(c,57+16-9,SHX(-3),PHX(-3),tempbuf);
+}
+
+#ifndef ONELEVELDEMO
+            sprintf(tempbuf,"LEVEL %ld",ud.m_level_number+1);
+            menutext(c,57+16+16-9,SHX(-4),PHX(-4),tempbuf);
+#else
+            sprintf(tempbuf,"LEVEL %ld",ud.m_level_number+1);
+            menutext(c,57+16+16-9,SHX(-4),1,tempbuf);
+#endif
+            menutext(c,57+16+16+16-9,SHX(-5),PHX(-5),"MONSTERS");
+
+            if(ud.m_coop == 0)
+                menutext(c,57+16+16+16+16-9,SHX(-6),PHX(-6),"MARKERS");
+            else
+                menutext(c,57+16+16+16+16-9,SHX(-6),1,"MARKERS");
+
+            if(ud.m_coop == 1)
+                menutext(c,57+16+16+16+16+16-9,SHX(-6),PHX(-6),"FR. FIRE");
+            else menutext(c,57+16+16+16+16+16-9,SHX(-6),1,"FR. FIRE");
+
+if(!VOLUMEONE)
+{            menutext(c,57+16+16+16+16+16+16-9,SHX(-7),boardfilename[0] == 0,"USER MAP");
+            if( boardfilename[0] != 0 )
+                gametext(c+70+44,57+16+16+16+16+16,boardfilename,0,2+8+16);
+}
+else
+{
+			menutext(c,57+16+16+16+16+16+16-9,SHX(-7),1,"USER MAP");
+}
+
+            menutext(c,57+16+16+16+16+16+16+16-9,SHX(-8),PHX(-8),"START GAME");
+
+            break;
+    }
+
+    if( (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU)
+    {
+        vscrn();
+        cameraclock = totalclock;
+        cameradist = 65536L;
+    }
+}
+
+void palto(char r,char g,char b,long e)
+{
+    int i;
+    char temparray[768];
+
+    for(i=0;i<768;i+=3)
+    {
+        temparray[i  ] =
+            ps[myconnectindex].palette[i+0]+((((long)r-(long)ps[myconnectindex].palette[i+0])*(long)(e&127))>>6);
+        temparray[i+1] =
+            ps[myconnectindex].palette[i+1]+((((long)g-(long)ps[myconnectindex].palette[i+1])*(long)(e&127))>>6);
+        temparray[i+2] =
+            ps[myconnectindex].palette[i+2]+((((long)b-(long)ps[myconnectindex].palette[i+2])*(long)(e&127))>>6);
+    }
+
+// CTW - MODIFICATION
+/*  if( (e&128) == 0 )
+        if ((vidoption != 1) || (vgacompatible == 1)) limitrate();*/
+    if( (e&128) == 0 )
+        if ((ScreenMode != 1) || (vgacompatible == 1)) limitrate();
+// CTW END - MODIFICATION
+
+    setbrightness(ud.brightness>>2,temparray);
+}
+
+
+void drawoverheadmap(long cposx, long cposy, long czoom, short cang)
+{
+        long i, j, k, l, x1, y1, x2, y2, x3, y3, x4, y4, ox, oy, xoff, yoff;
+        long dax, day, cosang, sinang, xspan, yspan, sprx, spry;
+        long xrepeat, yrepeat, z1, z2, startwall, endwall, tilenum, daang;
+        long xvect, yvect, xvect2, yvect2;
+        short p;
+        char col;
+        walltype *wal, *wal2;
+        spritetype *spr;
+
+        xvect = sintable[(-cang)&2047] * czoom;
+        yvect = sintable[(1536-cang)&2047] * czoom;
+        xvect2 = mulscale16(xvect,yxaspect);
+        yvect2 = mulscale16(yvect,yxaspect);
+
+                //Draw red lines
+        for(i=0;i<numsectors;i++)
+        {
+                if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;
+
+                startwall = sector[i].wallptr;
+                endwall = sector[i].wallptr + sector[i].wallnum;
+
+                z1 = sector[i].ceilingz; z2 = sector[i].floorz;
+
+                for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
+                {
+                        k = wal->nextwall; if (k < 0) continue;
+
+                        //if ((show2dwall[j>>3]&(1<<(j&7))) == 0) continue;
+                        //if ((k > j) && ((show2dwall[k>>3]&(1<<(k&7))) > 0)) continue;
+
+                        if (sector[wal->nextsector].ceilingz == z1)
+                                if (sector[wal->nextsector].floorz == z2)
+                                        if (((wal->cstat|wall[wal->nextwall].cstat)&(16+32)) == 0) continue;
+
+                        col = 139; //red
+                        if ((wal->cstat|wall[wal->nextwall].cstat)&1) col = 234; //magenta
+
+                        if (!(show2dsector[wal->nextsector>>3]&(1<<(wal->nextsector&7))))
+                                col = 24;
+            else continue;
+
+                        ox = wal->x-cposx; oy = wal->y-cposy;
+                        x1 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
+                        y1 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);
+
+                        wal2 = &wall[wal->point2];
+                        ox = wal2->x-cposx; oy = wal2->y-cposy;
+                        x2 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
+                        y2 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);
+
+                        drawline256(x1,y1,x2,y2,col);
+                }
+        }
+
+                //Draw sprites
+        k = ps[screenpeek].i;
+        for(i=0;i<numsectors;i++)
+        {
+                if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;
+                for(j=headspritesect[i];j>=0;j=nextspritesect[j])
+                        //if ((show2dsprite[j>>3]&(1<<(j&7))) > 0)
+                        {
+                spr = &sprite[j];
+
+                if (j == k || (spr->cstat&0x8000) || spr->cstat == 257 || spr->xrepeat == 0) continue;
+
+                                col = 71; //cyan
+                                if (spr->cstat&1) col = 234; //magenta
+
+                                sprx = spr->x;
+                                spry = spr->y;
+
+                if( (spr->cstat&257) != 0) switch (spr->cstat&48)
+                                {
+                    case 0: break;
+                                                ox = sprx-cposx; oy = spry-cposy;
+                                                x1 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y1 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                                                ox = (sintable[(spr->ang+512)&2047]>>7);
+                                                oy = (sintable[(spr->ang)&2047]>>7);
+                                                x2 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y2 = dmulscale16(oy,xvect,ox,yvect);
+
+                                                x3 = mulscale16(x2,yxaspect);
+                                                y3 = mulscale16(y2,yxaspect);
+
+                                                drawline256(x1-x2+(xdim<<11),y1-y3+(ydim<<11),
+                                                                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
+                                                drawline256(x1-y2+(xdim<<11),y1+x3+(ydim<<11),
+                                                                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
+                                                drawline256(x1+y2+(xdim<<11),y1-x3+(ydim<<11),
+                                                                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
+                        break;
+
+                                        case 16:
+                        if( spr->picnum == LASERLINE )
+                        {
+                            x1 = sprx; y1 = spry;
+                            tilenum = spr->picnum;
+                            xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+                            if ((spr->cstat&4) > 0) xoff = -xoff;
+                            k = spr->ang; l = spr->xrepeat;
+                            dax = sintable[k&2047]*l; day = sintable[(k+1536)&2047]*l;
+                            l = tilesizx[tilenum]; k = (l>>1)+xoff;
+                            x1 -= mulscale16(dax,k); x2 = x1+mulscale16(dax,l);
+                            y1 -= mulscale16(day,k); y2 = y1+mulscale16(day,l);
+
+                            ox = x1-cposx; oy = y1-cposy;
+                            x1 = dmulscale16(ox,xvect,-oy,yvect);
+                            y1 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                            ox = x2-cposx; oy = y2-cposy;
+                            x2 = dmulscale16(ox,xvect,-oy,yvect);
+                            y2 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                            drawline256(x1+(xdim<<11),y1+(ydim<<11),
+                                                                                x2+(xdim<<11),y2+(ydim<<11),col);
+                        }
+
+                        break;
+
+                    case 32:
+
+                                                tilenum = spr->picnum;
+                                                xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
+                                                yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
+                                                if ((spr->cstat&4) > 0) xoff = -xoff;
+                                                if ((spr->cstat&8) > 0) yoff = -yoff;
+
+                                                k = spr->ang;
+                                                cosang = sintable[(k+512)&2047]; sinang = sintable[k];
+                                                xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
+                                                yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;
+
+                                                dax = ((xspan>>1)+xoff)*xrepeat; day = ((yspan>>1)+yoff)*yrepeat;
+                                                x1 = sprx + dmulscale16(sinang,dax,cosang,day);
+                                                y1 = spry + dmulscale16(sinang,day,-cosang,dax);
+                                                l = xspan*xrepeat;
+                                                x2 = x1 - mulscale16(sinang,l);
+                                                y2 = y1 + mulscale16(cosang,l);
+                                                l = yspan*yrepeat;
+                                                k = -mulscale16(cosang,l); x3 = x2+k; x4 = x1+k;
+                                                k = -mulscale16(sinang,l); y3 = y2+k; y4 = y1+k;
+
+                                                ox = x1-cposx; oy = y1-cposy;
+                                                x1 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y1 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                                                ox = x2-cposx; oy = y2-cposy;
+                                                x2 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y2 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                                                ox = x3-cposx; oy = y3-cposy;
+                                                x3 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y3 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                                                ox = x4-cposx; oy = y4-cposy;
+                                                x4 = dmulscale16(ox,xvect,-oy,yvect);
+                                                y4 = dmulscale16(oy,xvect2,ox,yvect2);
+
+                                                drawline256(x1+(xdim<<11),y1+(ydim<<11),
+                                                                                x2+(xdim<<11),y2+(ydim<<11),col);
+
+                                                drawline256(x2+(xdim<<11),y2+(ydim<<11),
+                                                                                x3+(xdim<<11),y3+(ydim<<11),col);
+
+                                                drawline256(x3+(xdim<<11),y3+(ydim<<11),
+                                                                                x4+(xdim<<11),y4+(ydim<<11),col);
+
+                                                drawline256(x4+(xdim<<11),y4+(ydim<<11),
+                                                                                x1+(xdim<<11),y1+(ydim<<11),col);
+
+                                                break;
+                                }
+                        }
+        }
+
+                //Draw white lines
+        for(i=0;i<numsectors;i++)
+        {
+                if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;
+
+                startwall = sector[i].wallptr;
+                endwall = sector[i].wallptr + sector[i].wallnum;
+
+                k = -1;
+                for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
+                {
+                        if (wal->nextwall >= 0) continue;
+
+                        //if ((show2dwall[j>>3]&(1<<(j&7))) == 0) continue;
+
+                        if (tilesizx[wal->picnum] == 0) continue;
+                        if (tilesizy[wal->picnum] == 0) continue;
+
+                        if (j == k)
+                                { x1 = x2; y1 = y2; }
+                        else
+                        {
+                                ox = wal->x-cposx; oy = wal->y-cposy;
+                                x1 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
+                                y1 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);
+                        }
+
+                        k = wal->point2; wal2 = &wall[k];
+                        ox = wal2->x-cposx; oy = wal2->y-cposy;
+                        x2 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
+                        y2 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);
+
+                        drawline256(x1,y1,x2,y2,24);
+                }
+        }
+
+         for(p=connecthead;p >= 0;p=connectpoint2[p])
+         {
+          if(ud.scrollmode && p == screenpeek) continue;
+
+          ox = sprite[ps[p].i].x-cposx; oy = sprite[ps[p].i].y-cposy;
+                  daang = (sprite[ps[p].i].ang-cang)&2047;
+                  if (p == screenpeek) { ox = 0; oy = 0; daang = 0; }
+                  x1 = mulscale(ox,xvect,16) - mulscale(oy,yvect,16);
+                  y1 = mulscale(oy,xvect2,16) + mulscale(ox,yvect2,16);
+
+          if(p == screenpeek || ud.coop == 1 )
+          {
+                if(sprite[ps[p].i].xvel > 16 && ps[p].on_ground)
+                    i = APLAYERTOP+((totalclock>>4)&3);
+                else
+                    i = APLAYERTOP;
+
+                j = klabs(ps[p].truefz-ps[p].posz)>>8;
+                j = mulscale(czoom*(sprite[ps[p].i].yrepeat+j),yxaspect,16);
+
+                if(j < 22000) j = 22000;
+                else if(j > (65536<<1)) j = (65536<<1);
+
+                rotatesprite((x1<<4)+(xdim<<15),(y1<<4)+(ydim<<15),j,
+                    daang,i,sprite[ps[p].i].shade,sprite[ps[p].i].pal,
+                    (sprite[ps[p].i].cstat&2)>>1,windowx1,windowy1,windowx2,windowy2);
+          }
+         }	        
+}
+
+
+
+void endanimsounds(long fr)
+{
+    switch(ud.volume_number)
+    {
+        case 0:break;
+        case 1:
+            switch(fr)
+            {
+                case 1:
+                    sound(WIND_AMBIENCE);
+                    break;
+                case 26:
+                    sound(ENDSEQVOL2SND1);
+                    break;
+                case 36:
+                    sound(ENDSEQVOL2SND2);
+                    break;
+                case 54:
+                    sound(THUD);
+                    break;
+                case 62:
+                    sound(ENDSEQVOL2SND3);
+                    break;
+                case 75:
+                    sound(ENDSEQVOL2SND4);
+                    break;
+                case 81:
+                    sound(ENDSEQVOL2SND5);
+                    break;
+                case 115:
+                    sound(ENDSEQVOL2SND6);
+                    break;
+                case 124:
+                    sound(ENDSEQVOL2SND7);
+                    break;
+            }
+            break;
+        case 2:
+            switch(fr)
+            {
+                case 1:
+                    sound(WIND_REPEAT);
+                    break;
+                case 98:
+                    sound(DUKE_GRUNT);
+                    break;
+                case 82+20:
+                    sound(THUD);
+                    sound(SQUISHED);
+                    break;
+                case 104+20:
+                    sound(ENDSEQVOL3SND3);
+                    break;
+                case 114+20:
+                    sound(ENDSEQVOL3SND2);
+                    break;
+                case 158:
+                    sound(PIPEBOMB_EXPLODE);
+                    break;
+            }
+            break;
+    }
+}
+
+void logoanimsounds(long fr)
+{
+    switch(fr)
+    {
+        case 1:
+            sound(FLY_BY);
+            break;
+        case 19:
+            sound(PIPEBOMB_EXPLODE);
+            break;
+    }
+}
+
+void intro4animsounds(long fr)
+{
+    switch(fr)
+    {
+        case 1:
+            sound(INTRO4_B);
+            break;
+        case 12:
+        case 34:
+            sound(SHORT_CIRCUIT);
+            break;
+        case 18:
+            sound(INTRO4_5);
+            break;
+    }
+}
+
+void first4animsounds(long fr)
+{
+    switch(fr)
+    {
+        case 1:
+            sound(INTRO4_1);
+            break;
+        case 12:
+            sound(INTRO4_2);
+            break;
+        case 7:
+            sound(INTRO4_3);
+            break;
+        case 26:
+            sound(INTRO4_4);
+            break;
+    }
+}
+
+void intro42animsounds(long fr)
+{
+    switch(fr)
+    {
+        case 10:
+            sound(INTRO4_6);
+            break;
+    }
+}
+
+
+
+
+void endanimvol41(long fr)
+{
+    switch(fr)
+    {
+        case 3:
+            sound(DUKE_UNDERWATER);
+            break;
+        case 35:
+            sound(VOL4ENDSND1);
+            break;
+    }
+}
+
+void endanimvol42(long fr)
+{
+    switch(fr)
+    {
+        case 11:
+            sound(DUKE_UNDERWATER);
+            break;
+        case 20:
+            sound(VOL4ENDSND1);
+            break;
+        case 39:
+            sound(VOL4ENDSND2);
+            break;
+        case 50:
+            FX_StopAllSounds();
+            break;
+    }
+}
+
+void endanimvol43(long fr)
+{
+    switch(fr)
+    {
+        case 1:
+            sound(BOSS4_DEADSPEECH);
+            break;
+        case 40:
+            sound(VOL4ENDSND1);
+            sound(DUKE_UNDERWATER);
+            break;
+        case 50:
+            sound(BIGBANG);
+            break;
+    }
+}
+
+
+long lastanimhack=0;
+void playanm(char *fn,char t)
+{
+        char *animbuf, *palptr;
+    long i, j, k, length=0, numframes=0;
+    int32 handle=-1;
+
+//    return;
+
+    if(t != 7 && t != 9 && t != 10 && t != 11)
+        KB_FlushKeyboardQueue();
+
+    if( KB_KeyWaiting() )
+    {
+        FX_StopAllSounds();
+        goto ENDOFANIMLOOP;
+    }
+
+        handle = TCkopen4load(fn,0);
+        if(handle == -1) return;
+        length = kfilelength(handle);
+
+    walock[MAXTILES-3-t] = 219+t;
+
+    if(anim == 0 || lastanimhack != (MAXTILES-3-t))
+        allocache((long *)&anim,length+sizeof(anim_t),&walock[MAXTILES-3-t]);
+
+    animbuf = (char *)(FP_OFF(anim)+sizeof(anim_t));
+
+    lastanimhack = (MAXTILES-3-t);
+
+    tilesizx[MAXTILES-3-t] = 200;
+    tilesizy[MAXTILES-3-t] = 320;
+
+        kread(handle,animbuf,length);
+        kclose(handle);
+
+        ANIM_LoadAnim (animbuf);
+        numframes = ANIM_NumFrames();
+
+        palptr = ANIM_GetPalette();
+        for(i=0;i<256;i++)
+        {
+                j = (i<<2); k = j-i;
+                tempbuf[j+0] = (palptr[k+2]>>2);
+                tempbuf[j+1] = (palptr[k+1]>>2);
+                tempbuf[j+2] = (palptr[k+0]>>2);
+                tempbuf[j+3] = 0;
+        }
+
+        VBE_setPalette(0L,256L,tempbuf);
+
+    ototalclock = totalclock + 10;
+
+        for(i=1;i<numframes;i++)
+        {
+       while(totalclock < ototalclock)
+       {
+          if( KB_KeyWaiting() )
+              goto ENDOFANIMLOOP;
+          getpackets();
+       }
+
+       if(t == 10) ototalclock += 14;
+       else if(t == 9) ototalclock += 10;
+       else if(t == 7) ototalclock += 18;
+       else if(t == 6) ototalclock += 14;
+       else if(t == 5) ototalclock += 9;
+       else if(ud.volume_number == 3) ototalclock += 10;
+       else if(ud.volume_number == 2) ototalclock += 10;
+       else if(ud.volume_number == 1) ototalclock += 18;
+       else                           ototalclock += 10;
+
+       waloff[MAXTILES-3-t] = FP_OFF(ANIM_DrawFrame(i));
+       rotatesprite(0<<16,0<<16,65536L,512,MAXTILES-3-t,0,0,2+4+8+16+64, 0,0,xdim-1,ydim-1);
+       nextpage();
+
+       if(t == 8) endanimvol41(i);
+       else if(t == 10) endanimvol42(i);
+       else if(t == 11) endanimvol43(i);
+       else if(t == 9) intro42animsounds(i);
+       else if(t == 7) intro4animsounds(i);
+       else if(t == 6) first4animsounds(i);
+       else if(t == 5) logoanimsounds(i);
+       else if(t < 4) endanimsounds(i);
+        }
+
+    ENDOFANIMLOOP:
+
+    ANIM_FreeAnim ();
+    walock[MAXTILES-3-t] = 1;
+}
+
--- /dev/null
+++ b/Game/src/midi/databuf.h
@@ -1,0 +1,221 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DATA_H
+#define DATA_H
+
+#ifndef ALPHA_LINUX_CXX
+#  include <cstdio>
+#  include <cstring>
+#endif
+#ifdef MACOS
+#  include <cassert>
+#endif
+#include <fstream>
+#include <iomanip>
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned long uint32;
+
+typedef signed char sint8;
+typedef signed short sint16;
+typedef signed long sint32;
+
+class DataSource
+{
+protected:
+	union uint_float {
+		uint32	i;
+		float	f;
+	};
+
+public:
+	DataSource() {};
+	virtual ~DataSource() {};
+	
+	virtual unsigned int read1() =0;
+	virtual uint16 read2() =0;
+	virtual uint16 read2high() =0;
+	virtual uint32 read4() =0;
+	virtual uint32 read4high() =0;
+	virtual float readf() =0;
+	virtual void read(char *, int) =0;
+	
+	virtual void write1(unsigned int) =0;
+	virtual void write2(uint16) =0;
+	virtual void write2high(uint16) =0;
+	virtual void write4(uint32) =0;
+	virtual void write4high(uint32) =0;
+	virtual void writef(float) =0;
+	virtual void write(char *, int) =0;
+	
+	virtual void seek(unsigned int) =0;
+	virtual void skip(int) =0;
+	virtual unsigned int getSize() =0;
+	virtual unsigned int getPos() =0;
+};
+
+class BufferDataSource: public DataSource
+{
+protected:
+	/* const solely so that no-one accidentally modifies it.
+		data is being passed 'non-const' anyway */
+	const unsigned char *buf;
+	unsigned char *buf_ptr;
+	sint32 size;
+public:
+	BufferDataSource(char *data, unsigned int len)
+	{
+		// data can be NULL if len is also 0
+//		assert(data!=0 || len==0);
+		buf = buf_ptr = reinterpret_cast<unsigned char*>(data);
+		size = len;
+	};
+	
+	void load(char *data, unsigned int len)
+	{
+		// data can be NULL if len is also 0
+		//assert(data!=0 || len==0);
+		buf = buf_ptr = reinterpret_cast<unsigned char*>(data);
+		size = len;
+	};
+	
+	virtual ~BufferDataSource() {};
+	
+	virtual unsigned int read1() 
+	{ 
+		unsigned char b0;
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		return (b0);
+	};
+	
+	virtual uint16 read2()
+	{
+		unsigned char b0, b1;
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		b1 = static_cast<unsigned char>(*buf_ptr++);
+		return (b0 | (b1 << 8));
+	};
+	
+	virtual uint16 read2high()
+	{
+		unsigned char b0, b1;
+		b1 = static_cast<unsigned char>(*buf_ptr++);
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		return (b0 | (b1 << 8));
+	};
+	
+	virtual uint32 read4()
+	{
+		unsigned char b0, b1, b2, b3;
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		b1 = static_cast<unsigned char>(*buf_ptr++);
+		b2 = static_cast<unsigned char>(*buf_ptr++);
+		b3 = static_cast<unsigned char>(*buf_ptr++);
+		return (b0 | (b1<<8) | (b2<<16) | (b3<<24));
+	};
+	
+	virtual uint32 read4high()
+	{
+		unsigned char b0, b1, b2, b3;
+		b3 = static_cast<unsigned char>(*buf_ptr++);
+		b2 = static_cast<unsigned char>(*buf_ptr++);
+		b1 = static_cast<unsigned char>(*buf_ptr++);
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		return (b0 | (b1<<8) | (b2<<16) | (b3<<24));
+	};
+	
+	virtual float readf()
+	{
+		unsigned char b0, b1, b2, b3;
+		b0 = static_cast<unsigned char>(*buf_ptr++);
+		b1 = static_cast<unsigned char>(*buf_ptr++);
+		b2 = static_cast<unsigned char>(*buf_ptr++);
+		b3 = static_cast<unsigned char>(*buf_ptr++);
+		uint_float	uif;
+		uif.i = (b0 | (b1<<8) | (b2<<16) | (b3<<24));
+		return uif.f;
+	};
+	
+	void read(char *b, int len) {
+		memcpy(b, buf_ptr, len);
+		buf_ptr += len;
+	};
+	
+	virtual void write1(unsigned int val)
+	{
+		*buf_ptr++ = val & 0xff;
+	};
+	
+	virtual void write2(uint16 val)
+	{
+		*buf_ptr++ = val & 0xff;
+		*buf_ptr++ = (val>>8) & 0xff;
+	};
+
+	virtual void write2high(uint16 val)
+	{
+		*buf_ptr++ = (val>>8) & 0xff;
+		*buf_ptr++ = val & 0xff;
+	};
+
+	
+	virtual void write4(uint32 val)
+	{
+		*buf_ptr++ = val & 0xff;
+		*buf_ptr++ = (val>>8) & 0xff;
+		*buf_ptr++ = (val>>16)&0xff;
+		*buf_ptr++ = (val>>24)&0xff;
+	};
+	
+	virtual void write4high(uint32 val)
+	{
+		*buf_ptr++ = (val>>24)&0xff;
+		*buf_ptr++ = (val>>16)&0xff;
+		*buf_ptr++ = (val>>8) & 0xff;
+		*buf_ptr++ = val & 0xff;
+	};
+
+	virtual void writef(float val)
+	{
+		uint_float	uif;
+		uif.f = val;
+		*buf_ptr++ = uif.i & 0xff;
+		*buf_ptr++ = (uif.i>>8) & 0xff;
+		*buf_ptr++ = (uif.i>>16)&0xff;
+		*buf_ptr++ = (uif.i>>24)&0xff;
+	};
+	
+	virtual void write(char *b, int len)
+	{
+		memcpy(buf_ptr, b, len);
+		buf_ptr += len;
+	};
+	
+	virtual void seek(unsigned int pos) { buf_ptr = const_cast<unsigned char *>(buf)+pos; };
+	
+	virtual void skip(int pos) { buf_ptr += pos; };
+	
+	virtual unsigned int getSize() { return size; };
+	
+	virtual unsigned int getPos() { return (buf_ptr-buf); };
+	
+	unsigned char *getPtr() { return buf_ptr; };
+};
+
+
+#endif
--- /dev/null
+++ b/Game/src/midi/win_midiout.cpp
@@ -1,0 +1,1295 @@
+/*
+Copyright (C) 2000, 2001, 2002  Ryan Nunn
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#if (__GNUG__ >= 2) && (!defined WIN32)
+#  pragma interface
+#endif
+
+//Windows-specific code
+#ifdef WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+// These will prevent inclusion of mmsystem sections
+#define MMNODRV         // Installable driver support
+#define MMNOSOUND       // Sound support
+#define MMNOWAVE        // Waveform support
+#define MMNOAUX         // Auxiliary audio support
+#define MMNOMIXER       // Mixer support
+#define MMNOTIMER       // Timer support
+#define MMNOJOY         // Joystick support
+#define MMNOMCI         // MCI support
+#define MMNOMMIO        // Multimedia file I/O support
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <winbase.h>
+
+#include <string>
+#include <iostream>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdarg>
+#include <cstring>
+#include <cassert>
+
+#include "win_midiout.h"
+#include "SDL_mixer.h"
+
+#define W32MO_THREAD_COM_READY			0
+#define W32MO_THREAD_COM_PLAY			1
+#define W32MO_THREAD_COM_STOP			2
+#define W32MO_THREAD_COM_INIT			3
+#define W32MO_THREAD_COM_INIT_FAILED	4
+#define W32MO_THREAD_COM_EXIT			-1
+
+const unsigned short Windows_MidiOut::centre_value = 0x2000;
+const unsigned char Windows_MidiOut::fine_value = centre_value & 127;
+const unsigned char Windows_MidiOut::coarse_value = centre_value >> 7;
+const unsigned short Windows_MidiOut::combined_value = (coarse_value << 8) | fine_value;
+
+#define MUSIC_STATUS_IDLE    0
+#define MUSIC_STATUS_PLAYING 1
+
+unsigned char nMusicState = MUSIC_STATUS_IDLE;
+
+Mix_Music *music;
+
+//#define DO_SMP_TEST
+
+#ifdef DO_SMP_TEST
+#define giveinfo() std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr.flush();
+#else
+#define giveinfo()
+#endif
+
+using std::string;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+
+Windows_MidiOut::Windows_MidiOut() : 
+dev_num(-1), 
+new_volume(-1)
+{
+	giveinfo();
+	InterlockedExchange (&playing, false);
+	InterlockedExchange (&s_playing, false);
+	InterlockedExchange (&is_available, false);
+	giveinfo();
+	init_device();
+	giveinfo();
+}
+
+Windows_MidiOut::~Windows_MidiOut()
+{
+	giveinfo();
+	if (!is_available) return;
+
+	giveinfo();
+	while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
+	
+	giveinfo();
+	InterlockedExchange (&thread_com, W32MO_THREAD_COM_EXIT);
+
+	giveinfo();
+	int count = 0;
+	
+	giveinfo();
+	while (count < 100)
+	{
+		giveinfo();
+		DWORD code;
+		GetExitCodeThread (thread_handle, &code);
+		
+		giveinfo();
+		// Wait 1 MS before trying again
+		if (code == STILL_ACTIVE) Sleep (10);
+		else break;
+		giveinfo();
+		
+		count++;
+	}
+
+	// We waited a second and it still didn't terminate
+	giveinfo();
+	if (count == 100 && is_available)
+		TerminateThread (thread_handle, 1);
+
+	giveinfo();
+	InterlockedExchange (&is_available, false);
+	giveinfo();
+}
+
+void Windows_MidiOut::init_device()
+{
+	string s;
+		
+	// Opened, lets open the thread
+	giveinfo();
+	InterlockedExchange (&thread_com, W32MO_THREAD_COM_INIT);
+	
+	// Get Win32 Midi Device num
+	dev_num = get_MusicDevice();//0;//MidiDevice;//0;//-1;
+
+	giveinfo();
+	thread_handle = (HANDLE*) CreateThread (NULL, 0, thread_start, this, 0, &thread_id);
+	
+	giveinfo();
+	while (thread_com == W32MO_THREAD_COM_INIT) Sleep (1);
+
+	giveinfo();
+	if (thread_com == W32MO_THREAD_COM_INIT_FAILED) cerr << "Failure to initialize midi playing thread" << endl;
+	giveinfo();
+}
+
+DWORD __stdcall Windows_MidiOut::thread_start(void *data)
+{
+	giveinfo();
+	Windows_MidiOut *ptr=static_cast<Windows_MidiOut *>(data);
+	giveinfo();
+	return ptr->thread_main();
+}
+
+DWORD Windows_MidiOut::thread_main()
+{
+	int i;
+	thread_data = NULL;
+	giveinfo();
+	InterlockedExchange (&playing, false);
+	InterlockedExchange (&s_playing, false);
+
+	giveinfo();
+
+	// List all the midi devices.
+	MIDIOUTCAPS caps;
+	signed long dev_count = (signed long) midiOutGetNumDevs(); 
+	std::cout << dev_count << " Midi Devices Detected" << endl;
+	std::cout << "Listing midi devices:" << endl;
+
+	for (i = -1; i < dev_count; i++)
+	{
+		midiOutGetDevCaps ((UINT) i, &caps, sizeof(caps));
+		std::cout << i << ": " << caps.szPname << endl;
+	}
+
+	if (dev_num < -1 || dev_num >= dev_count)
+	{
+		std::cerr << "Warning Midi device in config is out of range." << endl;
+		dev_num = -1;
+	}
+	midiOutGetDevCaps ((UINT) dev_num, &caps, sizeof(caps));
+	std::cout << "Using device " << dev_num << ": "<< caps.szPname << endl;
+
+	UINT mmsys_err = midiOutOpen (&midi_port, dev_num, 0, 0, 0);
+
+	giveinfo();
+	if (mmsys_err != MMSYSERR_NOERROR)
+	{
+		char buf[512];
+
+		giveinfo();
+		midiOutGetErrorText(mmsys_err, buf, 512);
+		cerr << "Unable to open device: " << buf << endl;
+		giveinfo();
+		InterlockedExchange (&thread_com, W32MO_THREAD_COM_INIT_FAILED);
+		giveinfo();
+		return 1;
+	}
+	giveinfo();
+	InterlockedExchange (&is_available, true);
+	
+//	SetThreadPriority (thread_handle, THREAD_PRIORITY_HIGHEST);
+	giveinfo();
+	SetThreadPriority (thread_handle, THREAD_PRIORITY_TIME_CRITICAL);
+	
+	giveinfo();
+	InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
+	InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
+
+	giveinfo();
+	thread_play();
+	giveinfo();
+
+	giveinfo();
+	Sleep(100);
+	midiOutClose (midi_port);
+	Sleep(100);
+	giveinfo();
+	giveinfo();
+	return 0;
+}
+
+void Windows_MidiOut::thread_play ()
+{
+	int				repeat = false;
+	uint32			aim = 0;
+	sint32			diff = 0;
+	uint32			last_tick = 0;
+	XMIDIEventList	*evntlist = NULL;
+	midi_event		*event = NULL;
+	NoteStack		notes_on;
+	midi_event		*note = NULL;
+
+	//
+	// Xmidi Looping
+	//
+
+	// The for loop event
+	midi_event	*loop_event[XMIDI_MAX_FOR_LOOP_COUNT];
+
+	// The amount of times we have left that we can loop
+	int		loop_count[XMIDI_MAX_FOR_LOOP_COUNT];
+
+	// The level of the loop we are currently in
+	int		loop_num = -1;		
+
+	giveinfo();
+
+	int				s_track = 0;
+	uint32			s_aim = 0;
+	sint32			s_diff = 0;
+	uint32			s_last_tick = 0;
+	NoteStack		s_notes_on;
+	XMIDIEventList	*s_evntlist = NULL;
+	midi_event		*s_event = NULL;
+
+	giveinfo();
+
+	vol_multi = 0xFF;
+
+	// Play while there isn't a message waiting
+	while (1)
+	{
+		if (thread_com == W32MO_THREAD_COM_EXIT && !playing && !s_playing) break;
+		
+		// Volume settings
+		if (new_volume != -1) {
+			vol_multi = new_volume;
+			new_volume = -1;
+
+			for (int i = 0; i < 16; i++) {
+				uint32 message = i;
+				message |= MIDI_STATUS_CONTROLLER << 4;
+				message |= 7 << 8;
+				message |= ((volumes[i] * vol_multi)/0xFF)<<16;
+				midiOutShortMsg (midi_port,  message);
+			}
+		}
+
+		if (thread_com == W32MO_THREAD_COM_STOP)
+		{
+			giveinfo();
+			InterlockedExchange (&playing, FALSE);
+			InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
+
+			// Handle note off's here
+			while (note = notes_on.Pop())
+				midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+			giveinfo();
+		 	// Clean up
+			for (int i = 0; i < 16; i++) reset_channel (i); 
+
+			midiOutReset (midi_port);
+			giveinfo();
+			if (evntlist) evntlist->DecerementCounter();
+			giveinfo();
+			evntlist = NULL;
+			event = NULL;
+			giveinfo();
+
+			// If stop was requested, we are ready to receive another song
+
+			loop_num = -1;
+
+			wmoInitClock ();
+			last_tick = 0;
+		}
+
+		// Handle note off's here
+		while (note = notes_on.PopTime(wmoGetRealTime()))
+			midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+		while (note = s_notes_on.PopTime(wmoGetRealTime()))
+			midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+		while (event && thread_com != W32MO_THREAD_COM_STOP)
+		{
+	 		aim = (event->time-last_tick)*50;
+			diff = aim - wmoGetTime ();
+
+			if (diff > 0) break;
+
+			last_tick = event->time;
+			wmoAddOffset(aim);
+		
+				// XMIDI For Loop
+			if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_FOR_LOOP)
+			{
+				if (loop_num < XMIDI_MAX_FOR_LOOP_COUNT) loop_num++;
+
+				loop_count[loop_num] = event->data[1];
+				loop_event[loop_num] = event;
+
+			}	// XMIDI Next/Break
+			else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_NEXT_BREAK)
+			{
+				if (loop_num != -1)
+				{
+					if (event->data[1] < 64)
+					{
+						loop_num--;
+					}
+				}
+				event = NULL;
+
+			}	// XMIDI Callback Trigger
+			else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_CALLBACK_TRIG)
+			{
+				// TODO
+			}	// Not SysEx
+			else if (event->status < 0xF0)
+			{
+				unsigned int type = event->status >> 4;
+				uint32 data = event->data[0] | (event->data[1] << 8);
+
+				// Channel volume
+				if (type == MIDI_STATUS_CONTROLLER && event->data[0] == 0x7) {
+					volumes[event->status &0xF] = event->data[1];
+					data = event->data[0] | (((event->data[1] * vol_multi)/0xFF)<<8);
+				}
+
+				if ((type != MIDI_STATUS_NOTE_ON || event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
+					if (type == MIDI_STATUS_NOTE_ON) {
+						notes_on.Remove(event);
+						notes_on.Push (event, event->duration * 50 + wmoGetStart());
+					}
+
+					midiOutShortMsg (midi_port, event->status | (data<<8));
+				}
+			}
+		
+		 	if (event) event = event->next;
+	
+	 		if (!event || thread_com != W32MO_THREAD_COM_READY)
+		 	{
+				bool clean = !repeat || (thread_com != W32MO_THREAD_COM_READY) || last_tick == 0;
+
+		 		if (clean)
+		 		{
+					InterlockedExchange (&playing, FALSE);
+					if (thread_com == W32MO_THREAD_COM_STOP)
+						InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
+
+					// Handle note off's here
+					while (note = notes_on.Pop())
+						midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+		 			// Clean up
+					for (int i = 0; i < 16; i++) reset_channel (i); 
+					midiOutReset (midi_port);
+					if (evntlist) evntlist->DecerementCounter();
+					evntlist = NULL;
+					event = NULL;
+
+					loop_num = -1;
+					wmoInitClock ();
+		 		}
+
+				last_tick = 0;
+
+				if (evntlist)
+				{
+	 				if (loop_num == -1) event = evntlist->events;
+					else
+					{
+						event = loop_event[loop_num]->next;
+						last_tick = loop_event[loop_num]->time;
+
+						if (loop_count[loop_num])
+							if (!--loop_count[loop_num])
+								loop_num--;
+					}
+				}
+		 	}
+		}
+
+
+		// Got issued a music play command
+		// set up the music playing routine
+		if (thread_com == W32MO_THREAD_COM_PLAY)
+		{
+			// Handle note off's here
+			while (note = notes_on.Pop())
+				midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+			// Manual Reset since I don't trust midiOutReset()
+			giveinfo();
+			for (int i = 0; i < 16; i++) reset_channel (i);
+			midiOutReset (midi_port);
+
+			if (evntlist) evntlist->DecerementCounter();
+			evntlist = NULL;
+			event = NULL;
+			InterlockedExchange (&playing, FALSE);
+
+			// Make sure that the data exists
+			giveinfo();
+			while (!thread_data) Sleep(1);
+			
+			giveinfo();
+			evntlist = thread_data->list;
+			repeat = thread_data->repeat;
+
+			giveinfo();
+			InterlockedExchange ((LONG*) &thread_data, (LONG) NULL);
+			giveinfo();
+			InterlockedExchange (&thread_com, W32MO_THREAD_COM_READY);
+			
+			giveinfo();
+			if (evntlist) event = evntlist->events;
+			else event = 0;
+
+			giveinfo();
+			last_tick = 0;
+			
+			giveinfo();
+			wmoInitClock ();
+	
+			// Reset XMIDI Looping
+			loop_num = -1;
+
+			giveinfo();
+			InterlockedExchange (&playing, true);
+		}
+
+	 	if (s_event)
+	 	{
+	 		s_aim = (s_event->time-s_last_tick)*50;
+			s_diff = s_aim - wmoGetSFXTime ();
+	 	}
+	 	else 
+	 		s_diff = 1;
+	
+		if (s_diff <= 0)
+		{
+			s_last_tick = s_event->time;
+			wmoAddSFXOffset(s_aim);
+		
+			// Not SysEx
+			if ((s_event->status >> 4) != MIDI_STATUS_SYSEX)
+			{
+				int type = s_event->status >> 4;
+
+				if ((type != MIDI_STATUS_NOTE_ON || s_event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
+					if (type == MIDI_STATUS_NOTE_ON) {
+						s_notes_on.Remove(s_event);
+						s_notes_on.Push (s_event, s_event->duration * 50 + wmoGetSFXStart());
+					}
+
+					midiOutShortMsg (midi_port, s_event->status + (s_event->data[0] << 8) + (s_event->data[1] << 16));
+				}
+				s_track |= 1 << (s_event->status & 0xF);
+			}
+
+		 	s_event = s_event->next;
+		}
+	 	if (s_evntlist && (!s_event || thread_com == W32MO_THREAD_COM_EXIT || sfx_com != W32MO_THREAD_COM_READY))
+		{
+		 	// Play all the remaining note offs 
+			while (note = s_notes_on.Pop())
+				midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+		 	
+		 	// Also reset the played tracks
+			for (int i = 0; i < 16; i++) if ((s_track >> i)&1) reset_channel (i);
+
+			s_evntlist->DecerementCounter();
+			s_evntlist = NULL;
+			s_event = NULL;
+			InterlockedExchange (&s_playing, false);
+			if (sfx_com != W32MO_THREAD_COM_PLAY) InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
+		}
+
+		// Got issued a sound effect play command
+		// set up the sound effect playing routine
+		if (!s_evntlist && sfx_com == W32MO_THREAD_COM_PLAY)
+		{
+			giveinfo();
+			cout << "Play sfx command" << endl;
+
+		 	// Play all the remaining note offs 
+			while (note = s_notes_on.Pop())
+				midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+			// Make sure that the data exists
+			while (!sfx_data) Sleep(1);
+			
+			giveinfo();
+			s_evntlist = sfx_data->list;
+
+			giveinfo();
+			InterlockedExchange ((LONG*) &sfx_data, (LONG) NULL);
+			InterlockedExchange (&sfx_com, W32MO_THREAD_COM_READY);
+			giveinfo();
+			
+			if (s_evntlist) s_event = s_evntlist->events;
+			else s_event = 0;
+
+			giveinfo();
+	
+			s_last_tick = 0;
+			
+			giveinfo();
+			wmoInitSFXClock ();
+
+			giveinfo();
+			InterlockedExchange (&s_playing, true);
+			
+			giveinfo();
+			// Reset thet track counter
+			s_track = 0;
+		}
+
+	 	if (event)
+	 	{
+	 		aim = (event->time-last_tick)*50;
+			diff = aim - wmoGetTime ();
+	 	}
+	 	else 
+	 		diff = 6;
+
+	 	if (s_event)
+	 	{
+	 		s_aim = (s_event->time-s_last_tick)*50;
+			s_diff = s_aim - wmoGetSFXTime ();
+	 	}
+	 	else 
+	 		s_diff = 6;
+
+		//std::cout << sfx_com << endl;
+
+		if (diff > 5 && s_diff > 5) Sleep (1);
+	}
+	// Handle note off's here
+	while (note = notes_on.Pop())
+		midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+	// Play all the remaining note offs 
+	while (note = s_notes_on.PopTime(wmoGetRealTime()))
+		midiOutShortMsg (midi_port, note->status + (note->data[0] << 8));
+
+	if (evntlist) evntlist->DecerementCounter();
+	evntlist = NULL;
+	if (s_evntlist) s_evntlist->DecerementCounter();
+	s_evntlist = NULL;
+	for (int i = 0; i < 16; i++) reset_channel (i); 
+	midiOutReset (midi_port);
+	InterlockedExchange (&new_volume, -1);
+}
+
+void Windows_MidiOut::reset_channel (int i)
+{
+	// Pitch Wheel
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_PITCH_WHEEL << 4) | (combined_value << 8));
+	
+	// All controllers off
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (121 << 8));
+
+	// All notes off
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (123 << 8));
+
+	// Bank Select
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_PROG_CHANGE << 4) | (0 << 8));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (0 << 8));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (32 << 8));
+
+	// Modulation Wheel
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (1 << 8) | (coarse_value << 16));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (33 << 8) | (fine_value << 16));
+	
+	// Volume
+	volumes[i] = coarse_value;
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (7 << 8) | (((coarse_value*vol_multi)/0xFF) << 16));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (39 << 8) | (fine_value << 16));
+
+	// Pan
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (8 << 8) | (coarse_value << 16));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (40 << 8) | (fine_value << 16));
+
+	// Balance
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (10 << 8) | (coarse_value << 16));
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (42 << 8) | (fine_value << 16));
+
+	// Effects (Reverb)
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (91 << 8));
+
+	// Chorus
+	midiOutShortMsg (midi_port, i | (MIDI_STATUS_CONTROLLER << 4) | (93 << 8));
+}
+
+void Windows_MidiOut::start_track (XMIDIEventList *xmidi, bool repeat)
+{
+	giveinfo();
+	if (!is_available)
+		init_device();
+
+	giveinfo();
+	if (!is_available)
+		return;
+
+	giveinfo();
+	while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
+	
+	giveinfo();
+	xmidi->IncerementCounter();
+	data.list = xmidi;
+	data.repeat = repeat;
+//	xmidi->Write("winmidi_out.mid");
+	
+	giveinfo();
+	InterlockedExchange ((LONG*) &thread_data, (LONG) &data);
+	giveinfo();
+	InterlockedExchange (&thread_com, W32MO_THREAD_COM_PLAY);
+	giveinfo();
+}
+
+void Windows_MidiOut::start_sfx(XMIDIEventList *xmidi)
+{
+	giveinfo();
+	if (!is_available)
+		init_device();
+
+	giveinfo();
+	if (!is_available)
+		return;
+	
+	giveinfo();
+	while (sfx_com != W32MO_THREAD_COM_READY) Sleep (1);
+
+	giveinfo();
+	xmidi->IncerementCounter();
+	sdata.list = xmidi;
+	sdata.repeat;
+	
+	giveinfo();
+	InterlockedExchange ((LONG*) &sfx_data, (LONG) &sdata);
+	giveinfo();
+	InterlockedExchange (&sfx_com, W32MO_THREAD_COM_PLAY);
+	giveinfo();
+}
+
+
+void Windows_MidiOut::stop_track(void)
+{
+	giveinfo();
+	if (!is_available)
+		return;
+
+	giveinfo();
+	if (!playing) return;
+
+	giveinfo();
+	while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
+	giveinfo();
+	InterlockedExchange (&thread_com, W32MO_THREAD_COM_STOP);
+	giveinfo();
+	while (thread_com != W32MO_THREAD_COM_READY) Sleep (1);
+	giveinfo();
+}
+
+void Windows_MidiOut::stop_sfx(void)
+{
+	giveinfo();
+	if (!is_available)
+		return;
+
+	giveinfo();
+	if (!s_playing) return;
+
+	giveinfo();
+	while (sfx_com != W32MO_THREAD_COM_READY) Sleep (1);
+	giveinfo();
+	InterlockedExchange (&sfx_com, W32MO_THREAD_COM_STOP);
+	giveinfo();
+}
+
+bool Windows_MidiOut::is_playing(void)
+{
+	giveinfo();
+	return playing!=0;
+}
+
+const char *Windows_MidiOut::copyright(void)
+{
+	giveinfo();
+	return "Internal Win32 Midiout Midi Player for Pentagram. Version 1.2a";
+}
+
+//
+// PSMDEX - Pentagram Streaming Midi Driver Extensions
+//
+
+int Windows_MidiOut::max_streams()
+{
+	return 1;
+}
+
+void Windows_MidiOut::start_stream(int str_num, XMIDIEventList *eventlist, bool repeat, bool activate, int vol)
+{
+	stop_track();
+	set_volume(0, vol);
+	start_track(eventlist, repeat);
+}
+
+void Windows_MidiOut::activate_stream(int str_num)
+{
+
+}
+
+void Windows_MidiOut::stop_stream(int str_num)
+{
+	stop_track();
+}
+
+void Windows_MidiOut::set_volume(int str_num, int level)
+{
+	if (!is_available) return;
+
+	while (new_volume != -1) Sleep (1);
+	InterlockedExchange (&new_volume, level);
+}
+
+bool Windows_MidiOut::is_playing(int str_num)
+{
+	return is_playing();
+}
+
+int Windows_MidiOut::get_active()
+{ 
+	return 0;
+}
+
+extern "C"
+{
+    #include "../duke3d.h"
+
+    int get_MusicDevice()
+    {
+        return MusicDevice;
+    }
+}
+
+extern "C"
+{
+
+// The music functions...
+
+#include "../duke3d.h"
+#include "cache1d.h"
+
+static char warningMessage[80];
+static char errorMessage[80];
+
+char *MUSIC_ErrorString(int ErrorNumber)
+{
+    switch (ErrorNumber)
+    {
+        case MUSIC_Warning:
+            return(warningMessage);
+
+        case MUSIC_Error:
+            return(errorMessage);
+
+        case MUSIC_Ok:
+            return("OK; no error.");
+
+        case MUSIC_ASSVersion:
+            return("Incorrect sound library version.");
+
+        case MUSIC_SoundCardError:
+            return("General sound card error.");
+
+        case MUSIC_InvalidCard:
+            return("Invalid sound card.");
+
+        case MUSIC_MidiError:
+            return("MIDI error.");
+
+        case MUSIC_MPU401Error:
+            return("MPU401 error.");
+
+        case MUSIC_TaskManError:
+            return("Task Manager error.");
+
+        case MUSIC_FMNotDetected:
+            return("FM not detected error.");
+
+        case MUSIC_DPMI_Error:
+            return("DPMI error.");
+
+        default:
+            return("Unknown error.");
+    } // switch
+
+    assert(0);    // shouldn't hit this point.
+    return(NULL);
+} // MUSIC_ErrorString
+
+
+static int music_initialized = 0, ext_music_initialized = 1;
+static int music_context = 0;
+static int music_loopflag = MUSIC_PlayOnce;
+static Windows_MidiOut *midi_device = NULL;
+
+extern void musdebug(const char *fmt, ...);
+extern void init_debugging(void);
+extern void setWarningMessage(const char *msg);
+extern void setErrorMessage(const char *msg);
+extern int MUSIC_ErrorCode;
+#define __FX_TRUE  (1 == 1)
+#define __FX_FALSE (!__FX_TRUE)
+
+#pragma message (" The win_midi code is temp until the SDL midi code functions properly ")
+
+#pragma message (" STUBBED musdebug ")
+void musdebug(const char *fmt, ...)
+{
+#if 0
+    va_list ap;
+
+    if (false)
+    {
+        fprintf(debug_file, "DUKEMUS: ");
+        va_start(ap, fmt);
+        vfprintf(debug_file, fmt, ap);
+        va_end(ap);
+        fprintf(debug_file, "\n");
+        fflush(debug_file);
+    } // if
+#endif
+} // snddebug
+
+#pragma message (" STUBBED setErrorMessage ")
+static void setErrorMessage(const char *msg)
+{
+#if 0
+    strncpy(errorMessage, msg, sizeof (errorMessage));
+    // strncpy() doesn't add the null char if there isn't room...
+    errorMessage[sizeof (errorMessage) - 1] = '\0';
+    snddebug("Error message set to [%s].", errorMessage);
+#endif
+} // setErrorMessage
+
+#pragma message (" STUBBED init_debugging ")
+static void init_debugging(void)
+{
+
+}
+
+
+int MUSIC_Init(int SoundCard, int Address)
+{
+    init_debugging();
+
+    musdebug("INIT! card=>%d, address=>%d...", SoundCard, Address);
+
+    if (music_initialized)
+    {
+        setErrorMessage("Music system is already initialized.");
+        return(MUSIC_Error);
+    } // if
+    
+    music_initialized = 1;
+	
+	midi_device = new Windows_MidiOut();
+
+	if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1) {
+	    printf("Mix_OpenAudio: %s\n", Mix_GetError());
+	}
+	else {
+	    ext_music_initialized = 1;
+	}
+
+    
+    return(MUSIC_Ok);
+} // MUSIC_Init
+
+
+int MUSIC_Shutdown(void)
+{
+    musdebug("shutting down sound subsystem.");
+
+    if ((!music_initialized) && (!ext_music_initialized))
+    {
+        setErrorMessage("Music system is not currently initialized.");
+        return(MUSIC_Error);
+    } // if
+
+    if(midi_device)
+	delete midi_device;
+	midi_device = 0;
+    music_context = 0;
+    music_initialized = 0;
+    music_loopflag = MUSIC_PlayOnce;
+
+    nMusicState = MUSIC_STATUS_IDLE;
+    
+	return(MUSIC_Ok);
+} // MUSIC_Shutdown
+
+
+void MUSIC_SetMaxFMMidiChannel(int channel)
+{
+    musdebug("STUB ... MUSIC_SetMaxFMMidiChannel(%d).\n", channel);
+} // MUSIC_SetMaxFMMidiChannel
+
+
+void MUSIC_SetVolume(int volume)
+{
+	if(midi_device)
+	midi_device->set_volume(0,volume);
+
+	if (ext_music_initialized == 1)
+	Mix_VolumeMusic((int)(volume / 2));
+
+}
+
+void MUSIC_SetMidiChannelVolume(int channel, int volume)
+{
+    musdebug("STUB ... MUSIC_SetMidiChannelVolume(%d, %d).\n", channel, volume);
+} // MUSIC_SetMidiChannelVolume
+
+
+void MUSIC_ResetMidiChannelVolumes(void)
+{
+    musdebug("STUB ... MUSIC_ResetMidiChannelVolumes().\n");
+} // MUSIC_ResetMidiChannelVolumes
+
+
+int MUSIC_GetVolume(void)
+{
+	if(midi_device)
+	return midi_device->vol_multi;
+	else
+	return 0;
+} // MUSIC_GetVolume
+
+
+void MUSIC_SetLoopFlag(int loopflag)
+{
+    music_loopflag = loopflag;
+} // MUSIC_SetLoopFlag
+
+
+int MUSIC_SongPlaying(void)
+{
+    if (ext_music_initialized)
+	{
+	 return((Mix_PlayingMusic()) ? __FX_TRUE : __FX_FALSE);
+	}
+	else
+	{
+		if(midi_device)
+			return midi_device->is_playing()?__FX_TRUE : __FX_FALSE;
+		else
+			return __FX_FALSE;
+	}
+
+} // MUSIC_SongPlaying
+
+
+void MUSIC_Continue(void)
+{
+    if (Mix_PausedMusic())
+        Mix_ResumeMusic();
+    //else if (music_songdata)
+      //  MUSIC_PlaySong(music_songdata, MUSIC_PlayOnce);
+} // MUSIC_Continue
+
+
+void MUSIC_Pause(void)
+{
+    Mix_PauseMusic();
+} // MUSIC_Pause
+
+
+int MUSIC_StopSong(void)
+{
+    if ( (Mix_PlayingMusic()) || (Mix_PausedMusic()) )
+	{
+        Mix_HaltMusic();
+
+		if (music) Mix_FreeMusic(music);
+		
+		nMusicState = MUSIC_STATUS_IDLE;
+	}
+    
+	if(midi_device) midi_device->stop_stream(0);
+
+    return(MUSIC_Ok);
+} // MUSIC_StopSong
+
+
+int MUSIC_PlaySong(unsigned char *song, int loopflag)
+{
+    //SDL_RWops *rw;
+
+    MUSIC_StopSong();
+
+    //music_songdata = song;
+
+    // !!! FIXME: This could be a problem...SDL/SDL_mixer wants a RWops, which
+    // !!! FIXME:  is an i/o abstraction. Since we already have the MIDI data
+    // !!! FIXME:  in memory, we fake it with a memory-based RWops. None of
+    // !!! FIXME:  this is a problem, except the RWops wants to know how big
+    // !!! FIXME:  its memory block is (so it can do things like seek on an
+    // !!! FIXME:  offset from the end of the block), and since we don't have
+    // !!! FIXME:  this information, we have to give it SOMETHING.
+
+    /* !!! ARGH! There's no LoadMUS_RW  ?!
+    rw = SDL_RWFromMem((void *) song, (10 * 1024) * 1024);  // yikes.
+    music_musicchunk = Mix_LoadMUS_RW(rw);
+    Mix_PlayMusic(music_musicchunk, (loopflag == MUSIC_PlayOnce) ? 0 : -1);
+    */
+
+	if(midi_device)
+	midi_device->stop_stream(0);
+
+	BufferDataSource mid_data((char*)song, 1024 * 1024);
+	XMIDI		midfile(&mid_data, XMIDI_CONVERT_EMIDI_GM);
+	XMIDIEventList *eventlist = midfile.GetEventList(0);
+	if (eventlist) 
+	{
+		if(midi_device)
+		midi_device->start_track(eventlist, loopflag?true:false);
+	}
+
+//STUBBED("Need to use PlaySongROTT.  :(");
+
+    return(MUSIC_Ok);
+} // MUSIC_PlaySong
+
+int MUSIC_PlayExtSong(char *fn)
+{
+    MUSIC_StopSong();
+
+	if(midi_device)
+	midi_device->stop_stream(0);
+
+    music = Mix_LoadMUS(fn);
+	if(!music) 
+	{
+		printf("Mix_LoadMUS(\"%s\"): %s\n", fn, Mix_GetError());
+		nMusicState = MUSIC_STATUS_IDLE;
+	}
+	else
+	{
+		
+		if(Mix_PlayMusic(music, -1)==-1) 
+		{
+			printf("Mix_PlayMusic: %s\n", Mix_GetError());
+    		nMusicState = MUSIC_STATUS_IDLE;
+		}
+		else 
+		{
+			nMusicState = MUSIC_STATUS_PLAYING;
+		}
+	}
+return(MUSIC_Ok);
+
+//STUBBED("Need to use PlaySongROTT.  :(");
+
+} // MUSIC_PlaySong
+
+
+extern char ApogeePath[256];
+
+static void CheckAndPlayMusicType(const char* szName, const char* szType)
+{
+
+	char fpath[1024] = {'\0'};
+
+	// Is this a TC?
+	if(game_dir[0] != '\0')
+	{
+		
+	   sprintf(fpath, "%s\\%s%s", game_dir, szName, szType);
+	}
+	else
+	{
+		// FIX_00010: Hi resolution tunes (*.ogg files) are now first searched in .\tunes\ 
+		// and then searched in the main folder. Allows a better separation of files
+		// OGG tunes are NOT required. They are only used if found else normal
+		// MIDI files are used by default for music		
+
+	   sprintf(fpath, "%s\\%s%s", HIRESMUSICPATH, szName, szType);
+	   if (!SafeFileExists(fpath))
+			sprintf(fpath, "%s%s", szName, szType);
+	}
+
+	// Play MP3 file if available
+	if (nMusicState == MUSIC_STATUS_IDLE) 
+	{
+	    //If it exists let's play it.
+	   if (SafeFileExists(fpath)) 
+	   {
+		   MUSIC_PlayExtSong(fpath);
+	   }
+	}
+}
+
+// Duke3D-specific.  --ryan.
+void PlayMusic(char *fn)
+{
+	//extern int File_Exists(char *fn);
+	//extern void GetOnlyNameOfFile(char *fn);
+
+	short      fp;
+    long        l;
+	char *cfn;
+	char *buffer;
+	char fpath[19] = {'\0'};
+
+	cfn = fn;
+
+	//GetOnlyNameOfFile(cfn);
+	buffer = strtok(cfn, ".");
+
+	CheckAndPlayMusicType(buffer, ".ogg");
+	CheckAndPlayMusicType(buffer, ".mp3");
+	CheckAndPlayMusicType(buffer, ".mod");
+	CheckAndPlayMusicType(buffer, ".s3m");
+	CheckAndPlayMusicType(buffer, ".it");
+	CheckAndPlayMusicType(buffer, ".xm");
+	CheckAndPlayMusicType(buffer, ".wav");
+
+	// else fall back to the midis.
+	if (nMusicState == MUSIC_STATUS_IDLE) 
+	{
+		if(MusicToggle == 0) return;
+		if(MusicDevice == NumSoundCards) return;
+		if(eightytwofifty && numplayers > 1) return;
+
+		fp = kopen4load(fn,0);
+
+		if(fp == -1) return;
+
+		l = kfilelength( fp );
+		if(l >= 72000)
+		{
+	        kclose(fp);
+			return;
+		}
+
+		kread( fp, MusicPtr, l);
+		kclose( fp );
+		MUSIC_PlaySong( (unsigned char*)MusicPtr, MUSIC_LoopSong );
+	}
+}
+
+
+void MUSIC_SetContext(int context)
+{
+    musdebug("STUB ... MUSIC_SetContext().\n");
+    music_context = context;
+} // MUSIC_SetContext
+
+
+int MUSIC_GetContext(void)
+{
+    return(music_context);
+} // MUSIC_GetContext
+
+
+void MUSIC_SetSongTick(unsigned long PositionInTicks)
+{
+    musdebug("STUB ... MUSIC_SetSongTick().\n");
+} // MUSIC_SetSongTick
+
+
+void MUSIC_SetSongTime(unsigned long milliseconds)
+{
+    musdebug("STUB ... MUSIC_SetSongTime().\n");
+}// MUSIC_SetSongTime
+
+
+void MUSIC_SetSongPosition(int measure, int beat, int tick)
+{
+    musdebug("STUB ... MUSIC_SetSongPosition().\n");
+} // MUSIC_SetSongPosition
+
+
+void MUSIC_GetSongPosition(songposition *pos)
+{
+    musdebug("STUB ... MUSIC_GetSongPosition().\n");
+} // MUSIC_GetSongPosition
+
+
+void MUSIC_GetSongLength(songposition *pos)
+{
+    musdebug("STUB ... MUSIC_GetSongLength().\n");
+} // MUSIC_GetSongLength
+
+
+int MUSIC_FadeVolume(int tovolume, int milliseconds)
+{
+ //   Mix_FadeOutMusic(milliseconds);
+    return(MUSIC_Ok);
+} // MUSIC_FadeVolume
+
+
+int MUSIC_FadeActive(void)
+{
+   // return((Mix_FadingMusic() == MIX_FADING_OUT) ? __FX_TRUE : __FX_FALSE);
+	return __FX_FALSE;
+} // MUSIC_FadeActive
+
+
+void MUSIC_StopFade(void)
+{
+    musdebug("STUB ... MUSIC_StopFade().\n");
+} // MUSIC_StopFade
+
+
+void MUSIC_RerouteMidiChannel(int channel, int cdecl ( *function )( int event, int c1, int c2 ))
+{
+    musdebug("STUB ... MUSIC_RerouteMidiChannel().\n");
+} // MUSIC_RerouteMidiChannel
+
+
+void MUSIC_RegisterTimbreBank(unsigned char *timbres)
+{
+    musdebug("STUB ... MUSIC_RegisterTimbreBank().\n");
+} // MUSIC_RegisterTimbreBank
+
+
+// end of fx_man.c ...
+
+}
+
+
+
+#endif
--- /dev/null
+++ b/Game/src/midi/win_midiout.h
@@ -1,0 +1,150 @@
+/*
+Copyright (C) 2000, 2001, 2002  Ryan Nunn
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifndef WIN_MIDIOUT_H
+#define WIN_MIDIOUT_H
+
+#if (__GNUG__ >= 2) && (!defined WIN32)
+#  pragma interface
+#endif
+
+#ifdef WIN32
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+// These will prevent inclusion of mmsystem sections
+#define MMNODRV         // Installable driver support
+#define MMNOSOUND       // Sound support
+#define MMNOWAVE        // Waveform support
+#define MMNOAUX         // Auxiliary audio support
+#define MMNOMIXER       // Mixer support
+#define MMNOTIMER       // Timer support
+#define MMNOJOY         // Joystick support
+#define MMNOMCI         // MCI support
+#define MMNOMMIO        // Multimedia file I/O support
+
+#include <windows.h>
+#include <mmsystem.h>
+#include "xmidi.h"
+
+class	Windows_MidiOut
+{
+public:
+	virtual void		start_track(XMIDIEventList *, bool repeat);
+	virtual void		start_sfx(XMIDIEventList *);
+	virtual void		stop_track(void);
+	virtual void		stop_sfx(void);
+	virtual bool		is_playing(void);
+	virtual const char	*copyright(void);
+
+	// PSMDEX - Pentagram Streaming Midi Driver Extensions
+	virtual int			max_streams();
+	virtual void		start_stream(int str_num, XMIDIEventList *, bool repeat, bool activate, int vol);
+	virtual void		activate_stream(int str_num);
+	virtual void		stop_stream(int str_num);
+	virtual void		set_volume(int str_num, int level);
+	virtual bool		is_playing(int str_num);
+	virtual int			get_active();
+
+	Windows_MidiOut();
+	virtual ~Windows_MidiOut();
+
+//private:
+
+	struct mid_data {
+		XMIDIEventList	*list;
+		bool			repeat;
+	};
+
+	static const unsigned short	centre_value;
+	static const unsigned char	fine_value;
+	static const unsigned char	coarse_value;
+	static const unsigned short	combined_value;
+
+	signed int	dev_num;
+	HMIDIOUT	midi_port;
+	
+	HANDLE	 	*thread_handle;
+	DWORD		thread_id;
+
+	// Thread communicatoins
+	LONG		is_available;
+	LONG		playing;
+	LONG		s_playing;
+	LONG		thread_com;
+	LONG		sfx_com;
+
+	LONG		new_volume;
+	LONG		volumes[16];
+	LONG		vol_multi;
+
+	mid_data *thread_data;
+	mid_data *sfx_data;
+
+	mid_data data;
+	mid_data sdata;
+
+	// Methods
+	static DWORD __stdcall thread_start(void *data);
+	void init_device();
+	DWORD thread_main();
+	void thread_play ();
+	void reset_channel (int i);
+
+	// Microsecond Clock
+	unsigned long start;
+	unsigned long sfx_start;
+
+	inline void wmoInitClock ()
+	{ start = GetTickCount()*6; }
+
+	inline void wmoAddOffset (unsigned long offset)
+	{ start += offset; }
+
+	inline unsigned long wmoGetTime ()
+	{ return GetTickCount()*6 - start; }
+
+	inline unsigned long wmoGetStart ()
+	{ return start; }
+
+	inline unsigned long wmoGetRealTime ()
+	{ return GetTickCount()*6; }
+
+	inline void wmoInitSFXClock ()
+	{ sfx_start = GetTickCount()*6; }
+
+	inline void wmoAddSFXOffset (unsigned long offset)
+	{ sfx_start += offset; }
+
+	inline unsigned long wmoGetSFXTime ()
+	{ return GetTickCount()*6 - sfx_start; }
+
+	inline unsigned long wmoGetSFXStart ()
+	{ return sfx_start; }
+};
+
+#endif //WIN32
+
+extern "C"
+{
+    int get_MusicDevice();
+}
+
+#endif //WIN_MIDIOUT_H
--- /dev/null
+++ b/Game/src/midi/xmidi.cpp
@@ -1,0 +1,1784 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifndef ALPHA_LINUX_CXX
+#  include <cassert>
+#  include <cstdio>
+#  include <cmath>
+#  include <iostream>
+#  include <cmath>
+#endif
+#include "xmidi.h"
+
+using std::cerr;
+using std::endl;
+using std::string;
+
+// Here's a bit of joy: WIN32 isn't SMP safe if we use operator new and delete.
+// On the other hand, nothing else is thread-safe if we use malloc()/free().
+// So, we wrap the implementations and use malloc()/calloc()/free() for WIN32, and
+// the C++ thread-safe allocator for other platforms.
+
+template<class T>
+inline T* Malloc(size_t num=1)
+{
+#ifdef WIN32
+	return static_cast<T*>(malloc(num));
+#else
+	return static_cast<T*>(::operator new(num));
+#endif
+}
+
+template<class T>
+inline T* Calloc(size_t num=1,size_t sz=0)
+{
+	if(!sz)
+		sz=sizeof(T);
+#ifdef WIN32
+	return static_cast<T*>(calloc(num,sz));
+#else
+	size_t	total=sz*num;
+	T *tmp=Malloc<T>(total);
+	std::memset(tmp,0,total);
+	return tmp;
+#endif
+}
+
+inline void	Free(void *ptr)
+{
+#ifdef WIN32
+	free(ptr);
+#else
+	::operator delete(ptr);
+#endif
+}
+
+// This is used to correct incorrect patch, vol and pan changes in midi files
+// The bias is just a value to used to work out if a vol and pan belong with a 
+// patch change. This is to ensure that the default state of a midi file is with
+// the tracks centred, unless the first patch change says otherwise.
+#define PATCH_VOL_PAN_BIAS	5
+
+
+// This is a default set of patches to convert from MT32 to GM
+// The index is the MT32 Patch nubmer and the value is the GM Patch
+// This is only suitable for music that doesn'tdo timbre changes
+// XMIDIs that contain Timbre changes will not convert properly
+const char XMIDI::mt32asgm[128] = {
+	0,	// 0	Piano 1
+	1,	// 1	Piano 2
+	2,	// 2	Piano 3 (synth)
+	4,	// 3	EPiano 1
+	4,	// 4	EPiano 2
+	5,	// 5	EPiano 3
+	5,	// 6	EPiano 4
+	3,	// 7	Honkytonk
+	16,	// 8	Organ 1
+	17,	// 9	Organ 2
+	18,	// 10	Organ 3
+	16,	// 11	Organ 4
+	19,	// 12	Pipe Organ 1
+	19,	// 13	Pipe Organ 2
+	19,	// 14	Pipe Organ 3
+	21,	// 15	Accordion
+	6,	// 16	Harpsichord 1
+	6,	// 17	Harpsichord 2
+	6,	// 18	Harpsichord 3
+	7,	// 19	Clavinet 1
+	7,	// 20	Clavinet 2
+	7,	// 21	Clavinet 3
+	8,	// 22	Celesta 1
+	8,	// 23	Celesta 2
+	62,	// 24	Synthbrass 1 (62)
+	63,	// 25	Synthbrass 2 (63)
+	62,	// 26	Synthbrass 3 Bank 8
+	63,	// 27	Synthbrass 4 Bank 8
+	38,	// 28	Synthbass 1
+	39,	// 29	Synthbass 2
+	38,	// 30	Synthbass 3 Bank 8
+	39,	// 31	Synthbass 4 Bank 8
+	88,	// 32	Fantasy
+	90,	// 33	Harmonic Pan - No equiv closest is polysynth(90) :(
+	52,	// 34	Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)???
+	92,	// 35	Glass
+	97,	// 36	Soundtrack
+	99,	// 37	Atmosphere
+	14,	// 38	Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is!
+	54,	// 39	FunnyVox, sounds alot like Bagpipe(109) and Shania(111)
+	98,	// 40	EchoBell, no real equiv, sounds like Crystal(98)
+	96,	// 41	IceRain
+	68,	// 42	Oboe 2001, no equiv, just patching it to normal oboe(68)
+	95,	// 43	EchoPans, no equiv, setting to SweepPad
+	81,	// 44	DoctorSolo Bank 8
+	87,	// 45	SchoolDaze, no real equiv
+	112,	// 46	Bell Singer
+	80,	// 47	SquareWave
+	48,	// 48	Strings 1
+	48,	// 49	Strings 2 - should be 49
+	44,	// 50	Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50
+	45,	// 51	Pizzicato Strings
+	40,	// 52	Violin 1
+	40,	// 53	Violin 2 ? Viola
+	42,	// 54	Cello 1
+	42,	// 55	Cello 2
+	43,	// 56	Contrabass
+	46,	// 57	Harp 1
+	46,	// 58	Harp 2
+	24,	// 59	Guitar 1 (Nylon)
+	25,	// 60	Guitar 2 (Steel)
+	26,	// 61	Elec Guitar 1
+	27,	// 62	Elec Guitar 2
+	104,	// 63	Sitar
+	32,	// 64	Acou Bass 1
+	32,	// 65	Acou Bass 2
+	33,	// 66	Elec Bass 1
+	34,	// 67	Elec Bass 2
+	36,	// 68	Slap Bass 1
+	37,	// 69	Slap Bass 2
+	35,	// 70	Fretless Bass 1
+	35,	// 71	Fretless Bass 2
+	73,	// 72	Flute 1
+	73,	// 73	Flute 2
+	72,	// 74	Piccolo 1
+	72,	// 75	Piccolo 2
+	74,	// 76	Recorder
+	75,	// 77	Pan Pipes
+	64,	// 78	Sax 1
+	65,	// 79	Sax 2
+	66,	// 80	Sax 3
+	67,	// 81	Sax 4
+	71,	// 82	Clarinet 1
+	71,	// 83	Clarinet 2
+	68,	// 84	Oboe
+	69,	// 85	English Horn (Cor Anglais)
+	70,	// 86	Bassoon
+	22,	// 87	Harmonica
+	56,	// 88	Trumpet 1
+	56,	// 89	Trumpet 2
+	57,	// 90	Trombone 1
+	57,	// 91	Trombone 2
+	60,	// 92	French Horn 1
+	60,	// 93	French Horn 2
+	58,	// 94	Tuba	
+	61,	// 95	Brass Section 1
+	61,	// 96	Brass Section 2
+	11,	// 97	Vibes 1
+	11,	// 98	Vibes 2
+	99,	// 99	Syn Mallet Bank 1
+	112,	// 100	WindBell no real equiv Set to TinkleBell(112)
+	9,	// 101	Glockenspiel
+	14,	// 102	Tubular Bells
+	13,	// 103	Xylophone
+	12,	// 104	Marimba
+	107,	// 105	Koto
+	111,	// 106	Sho?? set to Shanai(111)
+	77,	// 107	Shakauhachi
+	78,	// 108	Whistle 1
+	78,	// 109	Whistle 2
+	76,	// 110	Bottle Blow
+	76,	// 111	Breathpipe no real equiv set to bottle blow(76)
+	47,	// 112	Timpani
+	117,	// 113	Melodic Tom
+	116,	// 114	Deap Snare no equiv, set to Taiko(116)
+	118,	// 115	Electric Perc 1
+	118,	// 116	Electric Perc 2
+	116,	// 117	Taiko
+	115,	// 118	Taiko Rim, no real equiv, set to Woodblock(115)
+	119,	// 119	Cymbal, no real equiv, set to reverse cymbal(119)
+	115,	// 120	Castanets, no real equiv, in GM set to Woodblock(115)
+	112,	// 121	Triangle, no real equiv, set to TinkleBell(112)
+	55,	// 122	Orchestral Hit
+	124,	// 123	Telephone
+	123,	// 124	BirdTweet
+	94,	// 125	Big Notes Pad no equiv, set to halo pad (94)
+	98,	// 126	Water Bell set to Crystal Pad(98)
+	121	// 127	Jungle Tune set to Breath Noise
+};
+
+// Same as above, except include patch changes
+// so GS instruments can be used
+const char XMIDI::mt32asgs[256] = {
+	0, 0,	// 0	Piano 1
+	1, 0,	// 1	Piano 2
+	2, 0,	// 2	Piano 3 (synth)
+	4, 0,	// 3	EPiano 1
+	4, 0,	// 4	EPiano 2
+	5, 0,	// 5	EPiano 3
+	5, 0,	// 6	EPiano 4
+	3, 0,	// 7	Honkytonk
+	16, 0,	// 8	Organ 1
+	17, 0,	// 9	Organ 2
+	18, 0,	// 10	Organ 3
+	16, 0,	// 11	Organ 4
+	19, 0,	// 12	Pipe Organ 1
+	19, 0,	// 13	Pipe Organ 2
+	19, 0,	// 14	Pipe Organ 3
+	21, 0,	// 15	Accordion
+	6, 0,	// 16	Harpsichord 1
+	6, 0,	// 17	Harpsichord 2
+	6, 0,	// 18	Harpsichord 3
+	7, 0,	// 19	Clavinet 1
+	7, 0,	// 20	Clavinet 2
+	7, 0,	// 21	Clavinet 3
+	8, 0,	// 22	Celesta 1
+	8, 0,	// 23	Celesta 2
+	62, 0,	// 24	Synthbrass 1 (62)
+	63, 0,	// 25	Synthbrass 2 (63)
+	62, 0,	// 26	Synthbrass 3 Bank 8
+	63, 0,	// 27	Synthbrass 4 Bank 8
+	38, 0,	// 28	Synthbass 1
+	39, 0,	// 29	Synthbass 2
+	38, 0,	// 30	Synthbass 3 Bank 8
+	39, 0,	// 31	Synthbass 4 Bank 8
+	88, 0,	// 32	Fantasy
+	90, 0,	// 33	Harmonic Pan - No equiv closest is polysynth(90) :(
+	52, 0,	// 34	Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)???
+	92, 0,	// 35	Glass
+	97, 0,	// 36	Soundtrack
+	99, 0,	// 37	Atmosphere
+	14, 0,	// 38	Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is!
+	54, 0,	// 39	FunnyVox, sounds alot like Bagpipe(109) and Shania(111)
+	98, 0,	// 40	EchoBell, no real equiv, sounds like Crystal(98)
+	96, 0,	// 41	IceRain
+	68, 0,	// 42	Oboe 2001, no equiv, just patching it to normal oboe(68)
+	95, 0,	// 43	EchoPans, no equiv, setting to SweepPad
+	81, 0,	// 44	DoctorSolo Bank 8
+	87, 0,	// 45	SchoolDaze, no real equiv
+	112, 0,	// 46	Bell Singer
+	80, 0,	// 47	SquareWave
+	48, 0,	// 48	Strings 1
+	48, 0,	// 49	Strings 2 - should be 49
+	44, 0,	// 50	Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50
+	45, 0,	// 51	Pizzicato Strings
+	40, 0,	// 52	Violin 1
+	40, 0,	// 53	Violin 2 ? Viola
+	42, 0,	// 54	Cello 1
+	42, 0,	// 55	Cello 2
+	43, 0,	// 56	Contrabass
+	46, 0,	// 57	Harp 1
+	46, 0,	// 58	Harp 2
+	24, 0,	// 59	Guitar 1 (Nylon)
+	25, 0,	// 60	Guitar 2 (Steel)
+	26, 0,	// 61	Elec Guitar 1
+	27, 0,	// 62	Elec Guitar 2
+	104, 0,	// 63	Sitar
+	32, 0,	// 64	Acou Bass 1
+	32, 0,	// 65	Acou Bass 2
+	33, 0,	// 66	Elec Bass 1
+	34, 0,	// 67	Elec Bass 2
+	36, 0,	// 68	Slap Bass 1
+	37, 0,	// 69	Slap Bass 2
+	35, 0,	// 70	Fretless Bass 1
+	35, 0,	// 71	Fretless Bass 2
+	73, 0,	// 72	Flute 1
+	73, 0,	// 73	Flute 2
+	72, 0,	// 74	Piccolo 1
+	72, 0,	// 75	Piccolo 2
+	74, 0,	// 76	Recorder
+	75, 0,	// 77	Pan Pipes
+	64, 0,	// 78	Sax 1
+	65, 0,	// 79	Sax 2
+	66, 0,	// 80	Sax 3
+	67, 0,	// 81	Sax 4
+	71, 0,	// 82	Clarinet 1
+	71, 0,	// 83	Clarinet 2
+	68, 0,	// 84	Oboe
+	69, 0,	// 85	English Horn (Cor Anglais)
+	70, 0,	// 86	Bassoon
+	22, 0,	// 87	Harmonica
+	56, 0,	// 88	Trumpet 1
+	56, 0,	// 89	Trumpet 2
+	57, 0,	// 90	Trombone 1
+	57, 0,	// 91	Trombone 2
+	60, 0,	// 92	French Horn 1
+	60, 0,	// 93	French Horn 2
+	58, 0,	// 94	Tuba	
+	61, 0,	// 95	Brass Section 1
+	61, 0,	// 96	Brass Section 2
+	11, 0,	// 97	Vibes 1
+	11, 0,	// 98	Vibes 2
+	99, 0,	// 99	Syn Mallet Bank 1
+	112, 0,	// 100	WindBell no real equiv Set to TinkleBell(112)
+	9, 0,	// 101	Glockenspiel
+	14, 0,	// 102	Tubular Bells
+	13, 0,	// 103	Xylophone
+	12, 0,	// 104	Marimba
+	107, 0,	// 105	Koto
+	111, 0,	// 106	Sho?? set to Shanai(111)
+	77, 0,	// 107	Shakauhachi
+	78, 0,	// 108	Whistle 1
+	78, 0,	// 109	Whistle 2
+	76, 0,	// 110	Bottle Blow
+	76, 0,	// 111	Breathpipe no real equiv set to bottle blow(76)
+	47, 0,	// 112	Timpani
+	117, 0,	// 113	Melodic Tom
+	116, 0,	// 114	Deap Snare no equiv, set to Taiko(116)
+	118, 0,	// 115	Electric Perc 1
+	118, 0,	// 116	Electric Perc 2
+	116, 0,	// 117	Taiko
+	115, 0,	// 118	Taiko Rim, no real equiv, set to Woodblock(115)
+	119, 0,	// 119	Cymbal, no real equiv, set to reverse cymbal(119)
+	115, 0,	// 120	Castanets, no real equiv, in GM set to Woodblock(115)
+	112, 0,	// 121	Triangle, no real equiv, set to TinkleBell(112)
+	55, 0,	// 122	Orchestral Hit
+	124, 0,	// 123	Telephone
+	123, 0,	// 124	BirdTweet
+	94, 0,	// 125	Big Notes Pad no equiv, set to halo pad (94)
+	98, 0,	// 126	Water Bell set to Crystal Pad(98)
+	121, 0	// 127	Jungle Tune set to Breath Noise
+};
+
+// Constructor
+XMIDI::XMIDI(DataSource *source, int pconvert) : events(NULL),
+						convert_type(pconvert), is_emidi(false),
+						do_reverb(false), do_chorus(false)
+{
+	if (convert_type == XMIDI_CONVERT_EMIDI_GM)
+	{
+		is_emidi = true;
+		convert_type = XMIDI_CONVERT_NOCONVERSION;
+	}
+
+	memset(bank127,0,sizeof(bank127));
+	
+	ExtractTracks (source);
+}
+
+XMIDI::~XMIDI()
+{
+	if (events)
+	{
+		for (int i=0; i < num_tracks; i++) {
+			events[i]->DecerementCounter();
+			events[i] = NULL;
+		}
+		//delete [] events;
+		Free(events);
+	}
+}
+
+XMIDIEventList *XMIDI::GetEventList (uint32 track)
+{
+	if (!events)
+	{
+		cerr << "No midi data in loaded." << endl;
+		return 0;
+	}
+
+	if (track >= num_tracks)
+	{
+		cerr << "Can't retrieve MIDI data, track out of range" << endl;
+		return 0;
+	}
+
+	return events[track];
+}
+
+// Sets current to the new event and updates list
+void XMIDI::CreateNewEvent (int time)
+{
+	if (!list)
+	{
+		list = current = Calloc<midi_event>(); //new midi_event;
+		if (time > 0)
+			current->time = time;
+		return;
+	}
+
+	if (time < 0 || list->time > time)
+	{
+		midi_event *event = Calloc<midi_event>(); //new midi_event;
+		event->next = list;
+		list = current = event;
+		return;
+	}
+
+	if (!current || current->time > time)
+		current = list;
+
+	while (current->next)
+	{
+		if (current->next->time > time)
+		{
+			midi_event *event = Calloc<midi_event>(); //new midi_event;
+			
+			event->next = current->next;
+			current->next = event;
+			current = event;
+			current->time = time;
+			return;
+		}
+		
+		current = current->next;
+	}
+
+	current->next = Calloc<midi_event>(); //new midi_event;
+	current = current->next;
+	current->time = time;
+}
+
+//
+// GetVLQ
+//
+// Get a Conventional Variable Length Quantity
+//
+int XMIDI::GetVLQ (DataSource *source, uint32 &quant)
+{
+	int i;
+	quant = 0;
+	unsigned int data;
+
+	for (i = 0; i < 4; i++)
+	{
+		data = source->read1();
+		quant <<= 7;
+		quant |= data & 0x7F;
+
+		if (!(data & 0x80))
+		{
+			i++;
+			break;
+		}
+
+	}
+	return i;
+}
+
+//
+// GetVLQ2
+//
+// Get a XMIDI Variable Length Quantity
+//
+int XMIDI::GetVLQ2 (DataSource *source, uint32 &quant)
+{
+	int i;
+	quant = 0;
+	int data = 0;
+	
+	for (i = 0; i < 4; i++)
+	{
+		data = source->read1();
+		if (data & 0x80)
+		{
+			source->skip(-1);
+			break;
+		}
+		quant += data;
+	}
+	return i;
+}
+
+//
+// MovePatchVolAndPan.
+//
+// Well, this is just a modified version of what that method used to do. This
+// is a massive optimization. Speed up should be quite impressive
+//
+void XMIDI::ApplyFirstState(first_state &fs, int chan_mask)
+{
+	for (int channel = 0; channel < 16; channel++)
+	{
+		midi_event *patch = fs.patch[channel];
+		midi_event *vol = fs.vol[channel];
+		midi_event *pan = fs.pan[channel];
+		midi_event *bank = fs.bank[channel];
+		midi_event *reverb = NULL;
+		midi_event *chorus = NULL;
+		midi_event *temp;
+
+		// Got no patch change, return and don't try fixing it
+		if (!patch || !(chan_mask & 1 << channel)) continue;
+#if 0
+		std::cout << "Channel: " << channel+1 << std::endl;
+		std::cout << "Patch: " << (unsigned int) patch->data[0] << " @ " << patch->time << std::endl;
+		if (bank) std::cout << " Bank: " << (unsigned int) bank->data[1] << " @ " << bank->time << std::endl;
+		if (vol) std::cout << "  Vol: " << (unsigned int) vol->data[1] << " @ " << vol->time << std::endl;
+		if (pan) std::cout << "  Pan: " << ((signed int) pan->data[1])-64 << " @ " << pan->time << std::endl;
+		std::cout << std::endl;
+#endif
+
+		// Copy Patch Change Event
+		temp = patch;
+		patch = Calloc<midi_event>(); //new midi_event;
+		patch->time = temp->time;
+		patch->status = channel|(MIDI_STATUS_PROG_CHANGE << 4);
+		patch->data[0] = temp->data[0];
+
+		// Copy Volume
+		if (vol && (vol->time > patch->time+PATCH_VOL_PAN_BIAS || vol->time < patch->time-PATCH_VOL_PAN_BIAS))
+			vol = NULL;
+
+		temp = vol;
+		vol = Calloc<midi_event>(); //new midi_event;
+		vol->status = channel|(MIDI_STATUS_CONTROLLER << 4);
+		vol->data[0] = 7;
+
+		if (!temp)
+		{
+			vol->data[1] = 90;
+		}
+		else
+			vol->data[1] = temp->data[1];
+
+
+		// Copy Bank
+		if (bank && (bank->time > patch->time+PATCH_VOL_PAN_BIAS || bank->time < patch->time-PATCH_VOL_PAN_BIAS))
+			bank = NULL;
+
+		temp = bank;
+		
+		bank = Calloc<midi_event>(); //new midi_event;
+		bank->status = channel|(MIDI_STATUS_CONTROLLER << 4);
+
+		if (!temp)
+			bank->data[1] = 0;
+		else
+			bank->data[1] = temp->data[1];
+
+		// Copy Pan
+		if (pan && (pan->time > patch->time+PATCH_VOL_PAN_BIAS || pan->time < patch->time-PATCH_VOL_PAN_BIAS))
+			pan = NULL;
+
+		temp = pan;
+		pan = Calloc<midi_event>(); //new midi_event;
+		pan->status = channel|(MIDI_STATUS_CONTROLLER << 4);
+		pan->data[0] = 10;
+
+		if (!temp)
+			pan->data[1] = 64;
+		else
+			pan->data[1] = temp->data[1];
+
+		if (do_reverb)
+		{
+			reverb = Calloc<midi_event>(); //new midi_event;
+			reverb->status = channel|(MIDI_STATUS_CONTROLLER << 4);
+			reverb->data[0] = 91;
+			reverb->data[1] = reverb_value;
+		}
+
+		if (do_chorus)
+		{
+			chorus = Calloc<midi_event>(); //new midi_event;
+			chorus->status = channel|(MIDI_STATUS_CONTROLLER << 4);
+			chorus->data[0] = 93;
+			chorus->data[1] = chorus_value;
+		}
+
+		vol->time = 0;
+		pan->time = 0;
+		patch->time = 0;
+		bank->time = 0;
+		
+		if (do_reverb && do_chorus) reverb->next = chorus;
+		else if (do_reverb) reverb->next = bank;
+		if (do_chorus) chorus->next = bank;
+		bank->next = vol;
+		vol->next = pan;
+		pan->next = patch;
+		
+		patch->next = list;
+		if (do_reverb) list = reverb;
+		else if (do_chorus) list = chorus;
+		else list = bank;
+	}
+}
+
+#ifdef _MSC_VER
+typedef unsigned __int64 uint64;
+
+#elif !defined(BEOS)
+// Unsigned 64 Bit Int emulation. Only supports SOME operations
+struct uint64 {
+	uint32	low;		// Low is first so uint64 can be cast as uint32 to get low dword
+	uint32	high;		// 
+
+	uint64() : low(0), high(0) { }
+	uint64(uint32 i) : low(i), high(0) { }
+	uint64(uint32 h, uint32 l) : low(l), high(h) { }
+	uint64(const uint64 &i) : low(i.low), high(i.high) { }
+
+	inline void addlow(uint32 l) {
+		uint32 mid = (low >> 16);
+		low = (low & 0xFFFF) + (l & 0xFFFF);
+		mid += (low >> 16) + (l >> 16);
+		low = (low&0xFFFF) + (mid << 16);
+		high += mid >> 16;
+	}
+
+	// uint64 operations
+
+	inline uint64 & operator = (uint64 &o) {
+		low = o.low;
+		high = o.high;
+		return *this;
+	}
+
+	inline uint64 & operator += (uint64 &o) {
+		addlow(o.low);
+		high += o.high;
+		return *this;
+	}
+
+	inline uint64 operator + (uint64 &o) {
+		uint64 n(*this);
+		n.addlow(o.low);
+		n.high += o.high;
+		return n;
+	}
+
+	// uint32 operations
+
+	inline uint64 & operator = (uint32 i) {
+		low = i;
+		high = 0;
+		return *this;
+	}
+
+	inline uint64 & operator += (uint32 i) {
+		addlow(i);
+		return *this;
+	}
+
+	inline uint64 operator + (uint32 i) {
+		uint64 n(*this);
+		n.addlow(i);
+		return n;
+	}
+
+	inline uint64 & operator *= (uint32 i) {
+		// High 16 bits
+		uint32 h1 =   i >> 16;
+		uint32 h2 = low >> 16;
+		//uint32 h3 = high >> 16;
+
+		// Low 16 Bits
+		uint32 l1 =   i & 0xFFFF;
+		uint32 l2 = low & 0xFFFF;
+		uint32 l3 = high & 0xFFFF;
+
+		// The accumulator
+		uint32 accum;
+
+		// 0 -> 32
+		low = l1*l2;
+		high = 0;
+
+		// 16 -> 48
+		accum = h1*l2;
+		addlow(accum<<16);
+		high += accum>>16;
+
+		// 16 -> 48
+		accum = l1*h2;
+		addlow(accum<<16);	
+		high += accum>>16;
+
+		// 32 -> 64
+		high += h1*h2;
+
+		// 32 -> 64
+		high += l1*l3;
+
+		// 48 -> 80
+		high += (h1*l3) << 16;
+
+		// 48 -> 80
+		high += (l1*l3) << 16;
+
+		return *this;
+	}
+
+	inline uint64 operator * (uint32 i) {
+		uint64 n(*this);
+		return n*=i;
+	}
+
+	inline uint64 & operator /= (uint32 div) {
+
+		// If there isn't a high dword, we only need to work on the low dword
+		if (!high) {
+			low /= div;
+			return *this;
+		}
+
+		// modulus of last division
+		uint32 mod = high;
+		uint32 l = low;
+
+		// Low shift
+		uint32 shift = 32;
+		low = 0;
+
+		// Only High DWORD
+		// Can divide
+		if (mod >= div) {
+			high = mod / div;
+			mod %= div;
+		}
+		else high = 0;
+
+		// Only Both high and low
+		while (--shift) {
+
+			mod <<= 1;
+			mod |= (l>>shift) & 1;
+
+			// Can divide
+			if (mod >= div) {
+				uint32 v = mod / div;
+				mod %= div;
+				addlow(v << shift);
+				high += v >> (32 - shift);
+			}
+		}
+
+
+		// Only Low DWORD
+		mod <<= 1;
+		mod |= l & 1;
+
+		// Can divide
+		if (mod >= div) {
+			uint32 v = mod / div;
+			mod %= div;
+			addlow(v << shift);
+		}
+
+		return *this;
+	}
+
+	inline uint64 operator / (uint32 i) {
+		uint64 n(*this);
+		return n/=i;
+	}
+
+	inline uint64 & operator %= (uint32 div) {
+
+		// If there isn't a high dword, we only need to work on the low dword
+		if (!high) {
+			low %= div;
+			return *this;
+		}
+
+		// Remainder of last division
+		uint32 mod = high;
+
+		// Low shift
+		uint32 shift = 32;
+
+		while (1) {
+
+			// Can divide
+			if (mod >= div) mod %= div;
+
+			if (shift == 0) break;
+
+			mod <<= 1;
+			shift--;
+			mod |= (low>>shift) & 1;
+		}
+
+		high = 0;
+		low = mod;
+
+		return *this;
+	}
+
+	inline uint64 operator % (uint32 i) {
+		uint64 n(*this);
+		return n%=i;
+	}
+
+	inline operator uint32 ()
+	{
+		return low;
+	}
+
+	void printx() {
+		if (high) printf ("%X%08X", high, low);
+		else printf ("%X", low);
+	}
+};
+#endif
+
+//
+// AdjustTimings
+//
+// It converts the midi's to use 120 Hz timing, and also calcs the duration of
+// the notes. It also strips the tempo events, and adds it's own
+//
+// This is used by Midi's ONLY! It will do nothing with Xmidi
+//
+void XMIDI::AdjustTimings(uint32 ppqn)
+{
+	uint32		tempo = 500000;
+	uint32		time_prev = 0;
+	uint32		hs_rem = 0;
+	uint32		hs     = 0;
+
+	ppqn *= 10000;
+
+	// Virtual playing
+	NoteStack notes;
+
+	for (midi_event	*event = list; event; event = event->next) {
+
+			// Note 64 bit int is required because multiplication by tempo can
+			// require 52 bits in some circumstances
+
+			uint64 aim = event->time - time_prev;
+			aim *= tempo;
+
+			hs_rem += aim%ppqn;
+			hs += aim/ppqn;
+			hs += hs_rem/ppqn;
+			hs_rem %= ppqn;
+
+			time_prev = event->time;
+			event->time = (hs*6)/5 + (6*hs_rem)/(5*ppqn);
+				
+			// Note on and note off handling
+			if (event->status <= 0x9F) {
+
+				// Add if it's a note on and remove if it's a note off
+				if ((event->status>>4) == MIDI_STATUS_NOTE_ON && event->data[1]) 
+					notes.Push(event);
+				else {
+					midi_event *prev = notes.FindAndPop(event);
+					if (prev) prev->duration = event->time - prev->time;
+				}
+
+			}
+			else if (event->status == 0xFF && event->data[0] == 0x51) {
+
+				tempo = (event->buffer[0] << 16) +
+					(event->buffer[1] << 8) +
+					event->buffer[2];
+					
+				event->buffer[0] = 0x07;
+				event->buffer[1] = 0xA1;
+				event->buffer[2] = 0x20;
+			}
+	}
+
+	//std::cout << "Max Polyphony: " << notes.GetMaxPolyphony() << std::endl;
+	static const unsigned char tempo_buf[5] = { 0x51, 0x03, 0x07, 0xA1, 0x20 };
+	BufferDataSource ds((char *)tempo_buf, 5);
+	current = list;
+	ConvertSystemMessage (0, 0xFF,&ds);
+}
+
+
+// Converts Events
+//
+// Source is at the first data byte
+// size 1 is single data byte (ConvertEvent Only)
+// size 2 is dual data byte
+// size 3 is XMI Note on (ConvertNote only)
+// Returns bytes converted
+//
+// ConvertNote is used for Note On's and Note offs
+// ConvertSystemMessage is used for SysEx events and Meta events
+// ConvertEvent is used for everything else
+
+int XMIDI::ConvertEvent (const int time, unsigned char status, DataSource *source, const int size, first_state &fs)
+{
+	int	data;
+
+	data = source->read1();
+
+	// Little hacks for EMIDI
+
+	if (use_emidi_112 && (status >> 4) == MIDI_STATUS_PROG_CHANGE)
+	{
+		// Discard all normal program changes if we are using emidi 112
+		return 1;
+	}
+	else if (is_emidi && (status >> 4) == MIDI_STATUS_CONTROLLER && data == EMIDI_CONTROLLER_PROGRAM_CHANGE)
+	{
+		// Convert it into a normal program change event
+		use_emidi_112 = true;
+		status = (status&0x0F) | (MIDI_STATUS_PROG_CHANGE<<4);
+		data = source->read1();
+		source->skip(-1);
+	}
+
+	if (use_emidi_113 && (status >> 4) ==  MIDI_STATUS_CONTROLLER && data == 7)
+	{
+		// Discard all normal volume changes if we are using emidi 113
+		source->skip(1);
+		return 2;
+	}
+	else if (is_emidi && (status >> 4) == MIDI_STATUS_CONTROLLER && data == EMIDI_CONTROLLER_VOLUME)
+	{
+		// Convert it into a normal program change event
+		use_emidi_113 = true;
+		data = 7;
+	}
+
+
+	// Bank changes are handled here
+	if ((status >> 4) == 0xB && data == 0)
+	{
+		data = source->read1();
+		
+		bank127[status&0xF] = false;
+		
+		if (convert_type == XMIDI_CONVERT_MT32_TO_GM || convert_type == XMIDI_CONVERT_MT32_TO_GS
+			|| convert_type == XMIDI_CONVERT_MT32_TO_GS127)
+			return 2;
+
+		CreateNewEvent (time);
+		current->status = status;
+		current->data[0] = 0;
+		current->data[1] = data;
+
+		// Set the bank
+		if (!fs.bank[status&0xF] || fs.bank[status&0xF]->time > time) fs.bank[status&0xF] = current;
+
+		if (convert_type == XMIDI_CONVERT_GS127_TO_GS && data == 127)
+			bank127[status&0xF] = true;
+
+		return 2;
+	}
+
+	// Handling for patch change mt32 conversion, probably should go elsewhere
+	if ((status >> 4) == 0xC && (status&0xF) != 9 && convert_type != XMIDI_CONVERT_NOCONVERSION)
+	{
+		if (convert_type == XMIDI_CONVERT_MT32_TO_GM)
+		{
+			data = mt32asgm[data];
+		}
+		else if ((convert_type == XMIDI_CONVERT_GS127_TO_GS && bank127[status&0xF]) ||
+				convert_type == XMIDI_CONVERT_MT32_TO_GS)
+		{
+			CreateNewEvent (time);
+			current->status = 0xB0 | (status&0xF);
+			current->data[0] = 0;
+			current->data[1] = mt32asgs[data*2+1];
+
+			data = mt32asgs[data*2];
+
+			// Set the bank
+			if (!fs.bank[status&0xF] || fs.bank[status&0xF]->time > time) fs.bank[status&0xF] = current;
+		}
+		else if (convert_type == XMIDI_CONVERT_MT32_TO_GS127)
+		{
+			CreateNewEvent (time);
+			current->status = 0xB0 | (status&0xF);
+			current->data[0] = 0;
+			current->data[1] = 127;
+
+			// Set the bank
+			if (!fs.bank[status&0xF] || fs.bank[status&0xF]->time > time) fs.bank[status&0xF] = current;
+		}
+	}// Disable patch changes on Track 10 is doing a conversion
+	else if ((status >> 4) == 0xC && (status&0xF) == 9 && convert_type != XMIDI_CONVERT_NOCONVERSION)
+	{
+		return size;
+	}
+
+	CreateNewEvent (time);
+	current->status = status;
+
+	current->data[0] = data;
+
+	// Check for patch change, and update fs if req
+	if ((status >> 4) == 0xC) {
+		if (!fs.patch[status&0xF] || fs.patch[status&0xF]->time > time)
+			fs.patch[status&0xF] = current;
+	}
+	// Controllers
+	else if ((status >> 4) == 0xB) {
+		// Volume
+		if (current->data[0] == 7) {
+			if (!fs.vol[status&0xF] || fs.vol[status&0xF]->time > time)
+				fs.vol[status&0xF] = current;
+		}
+		// Pan
+		else if (current->data[0] == 10) {
+			if (!fs.pan[status&0xF] || fs.pan[status&0xF]->time > time)
+				fs.pan[status&0xF] = current;
+		}
+	}
+
+	if (size == 1)
+		return 1;
+
+	current->data[1] = source->read1();
+
+	return 2;
+}
+
+int XMIDI::ConvertNote (const int time, const unsigned char status, DataSource *source, const int size)
+{
+	uint32	delta = 0;
+	int	data;
+
+	data = source->read1();
+
+	CreateNewEvent (time);
+	current->status = status;
+
+	current->data[0] = data;
+	current->data[1] = source->read1();
+
+	if (size == 2)
+		return 2;
+
+	// XMI Note On handling
+
+	// Get the duration
+	int i = GetVLQ (source, delta);
+	
+	// Set the duration
+	current->duration = delta;
+
+	// This is an optimization
+	midi_event *prev = current;
+		
+	// Create a note off
+	CreateNewEvent (time+delta);
+
+	current->status = status;
+	current->data[0] = data;
+	current->data[1] = 0;
+	
+	// Optimization
+	current = prev;
+
+	return i + 2;
+}
+
+// Simple routine to convert system messages
+int XMIDI::ConvertSystemMessage (const int time, const unsigned char status, DataSource *source)
+{
+	int i=0;
+	
+	CreateNewEvent (time);
+	current->status = status;
+	
+	// Handling of Meta events
+	if (status == 0xFF)
+	{
+		current->data[0] = source->read1();
+		i++;	
+	}
+
+	i += GetVLQ (source, current->len);
+
+	if (!current->len)
+	{
+		current->buffer = NULL;
+		return i;
+	}
+	
+	current->buffer = Malloc<unsigned char>(current->len);
+
+	source->read (reinterpret_cast<char *>(current->buffer), current->len);
+
+	return i+current->len;
+}
+
+// XMIDI and Midi to List. Returns bit mask of channels used
+int XMIDI::ConvertFiletoList (DataSource *source, const bool is_xmi, first_state &fs)
+{
+	int 	time = 0;			// 120th of a second
+	uint32 	data;
+	int		end = 0;
+	uint32	status = 0;
+	int		play_size = 2;
+	int		file_size = source->getSize();
+	int		retval = 0;
+
+	if (is_xmi) play_size = 3;
+
+	use_emidi_112 = false;
+	use_emidi_113 = false;
+
+	while (!end && source->getPos() < file_size)
+	{
+		if (!is_xmi)
+		{
+			GetVLQ (source, data);
+			time += data;
+
+			data = source->read1();
+		
+			if (data >= 0x80)
+			{
+				status = data;
+			}
+			else
+				source->skip (-1);
+		}	
+		else
+		{
+			GetVLQ2 (source, data);
+			time += data;
+
+			status = source->read1();
+		}
+
+		switch (status >> 4)
+		{
+			case MIDI_STATUS_NOTE_ON:
+			retval |= 1 << (status & 0xF);
+			ConvertNote (time, status, source, play_size);
+			break;
+
+			case MIDI_STATUS_NOTE_OFF:
+			ConvertNote (time, status, source, 2);
+			break;
+
+			// 2 byte data
+
+			case MIDI_STATUS_CONTROLLER:
+			if (is_emidi)
+			{
+				data = source->read1();
+				if (data == EMIDI_CONTROLLER_TRACK_DESIGNATION)
+				{
+					data = source->read1();
+
+					// Only convert 0 and 127 tracks
+					if (data == 0 || data == 127) continue;
+
+					// Discard all others
+					return 0;
+				}
+				else if (data == EMIDI_CONTROLLER_TRACK_EXCLUSION)
+				{
+					data = source->read1();
+
+					// It's not for some other track
+					if (data != 0) continue;
+
+					// Uh oh, this track is not for 0
+					return 0;
+				}
+				source->skip (-1);
+			}
+			// We can convert like normal
+			
+			case MIDI_STATUS_AFTERTOUCH:
+			case MIDI_STATUS_PITCH_WHEEL:
+			if (is_emidi && (status >> 4))
+			ConvertEvent (time, status, source, 2, fs);
+			break;
+			
+
+			// 1 byte data
+			case MIDI_STATUS_PROG_CHANGE:
+			case MIDI_STATUS_PRESSURE:
+			ConvertEvent (time, status, source, 1, fs);
+			break;
+			
+
+			case MIDI_STATUS_SYSEX:
+			if (status == 0xFF)
+			{
+				int	pos = source->getPos();
+				uint32	data = source->read1();
+				
+				if (data == 0x2F)					// End, of track
+					end = 1;
+				else if (data == 0x51 && is_xmi)	// XMIDI doesn't use tempo
+				{
+					GetVLQ (source, data);
+					source->skip(data);
+					break;
+				}
+				
+				source->seek (pos);
+			}
+			ConvertSystemMessage (time, status, source);
+			break;
+
+			default:
+			break;
+		}
+
+	}
+
+	return retval;
+}
+
+// Assumes correct xmidi
+int XMIDI::ExtractTracksFromXmi (DataSource *source)
+{
+	int				num = 0;
+	uint32			len = 0;
+	char			buf[32];
+
+	first_state	fs;
+
+	while (source->getPos() < source->getSize() && num != num_tracks)
+	{
+		// Read first 4 bytes of name
+		source->read (buf, 4);
+		len = source->read4high();
+
+		// Skip the FORM entries
+		if (!memcmp(buf,"FORM",4))
+		{
+			source->skip (4);
+			source->read (buf, 4);
+			len = source->read4high();
+		}
+
+		if (memcmp(buf,"EVNT",4))
+		{
+			source->skip ((len+1)&~1);
+			continue;
+		}
+
+		list = NULL;
+		memset(&fs, 0, sizeof(fs));
+
+		int begin = source->getPos ();
+
+		// Convert it
+		int chan_mask = ConvertFiletoList (source, true, fs);
+
+		// Apply the first state
+		ApplyFirstState(fs, chan_mask);
+
+		// Add tempo
+		static const unsigned char tempo_buf[5] = { 0x51, 0x03, 0x07, 0xA1, 0x20 };
+		BufferDataSource ds((char *)tempo_buf, 5);
+		current = list;
+		ConvertSystemMessage (0, 0xFF,&ds);
+
+		// Set the list
+		events[num]->events = list;
+
+		// Increment Counter
+		num++;
+
+		// go to start of next track
+		source->seek (begin + ((len+1)&~1));
+	}
+
+	// Return how many were converted
+	return num;
+}
+
+int XMIDI::ExtractTracksFromMid (DataSource *source, const uint32 ppqn, const int num_tracks, const bool type1)
+{
+	int			num = 0;
+	uint32		len = 0;
+	char		buf[32];
+	int			chan_mask = 0;
+
+	first_state	fs;
+	memset(&fs, 0, sizeof(fs));
+
+	list = NULL;
+
+	while (source->getPos() < source->getSize() && num != num_tracks)
+	{
+		// Read first 4 bytes of name
+		source->read (buf, 4);
+		len = source->read4high();
+
+		if (memcmp(buf,"MTrk",4))
+		{
+			source->skip (len);
+			continue;
+		}
+
+		int begin = source->getPos ();
+
+		// Convert it
+		chan_mask |= ConvertFiletoList (source, false, fs);
+
+		if (!type1) {
+			ApplyFirstState(fs, chan_mask);
+			AdjustTimings(ppqn);
+			events[num]->events = list;
+			list = NULL;
+			memset(&fs, 0, sizeof(fs));
+			chan_mask = 0;
+		}
+		
+		// Increment Counter
+		num++;		
+		source->seek (begin+len);
+	}
+
+	if (type1) { 
+		ApplyFirstState(fs, chan_mask);
+		AdjustTimings(ppqn);
+		events[0]->events = list;
+		return num == num_tracks ? 1 : 0;
+	}
+
+	// Return how many were converted
+	return num;
+}
+
+int XMIDI::ExtractTracks (DataSource *source)
+{
+	uint32		i = 0;
+	int		start;
+	uint32		len;
+	uint32		chunk_len;
+	int 		count;
+	char		buf[32];
+
+	string s;
+	
+	do_reverb = false;
+	reverb_value = 0;
+	do_chorus = true;
+	chorus_value = 0;
+
+
+	// Read first 4 bytes of header
+	source->read (buf, 4);
+
+	// Could be XMIDI
+	if (!memcmp (buf, "FORM", 4))
+	{
+		// Read length of 
+		len = source->read4high();
+
+		start = source->getPos();
+		
+		// Read 4 bytes of type
+		source->read (buf, 4);
+
+		// XDIRless XMIDI, we can handle them here.
+		if (!memcmp (buf, "XMID", 4))
+		{	
+			cerr << "Warning: XMIDI doesn't have XDIR" << endl;
+			num_tracks = 1;
+			
+		} // Not an XMIDI that we recognise
+		else if (memcmp (buf, "XDIR", 4))
+		{	
+			cerr << "Not a recognised XMID" << endl;
+			return 0;
+			
+		} // Seems Valid
+		else 
+		{
+			num_tracks = 0;
+		
+			for (i = 4; i < len; i++)
+			{
+				// Read 4 bytes of type
+				source->read (buf, 4);
+
+				// Read length of chunk
+				chunk_len = source->read4high();
+			
+				// Add eight bytes
+				i+=8;
+				
+				if (memcmp (buf, "INFO", 4))
+				{	
+					// Must allign
+					source->skip((chunk_len+1)&~1);
+					i+= (chunk_len+1)&~1;
+					continue;
+				}
+
+				// Must be at least 2 bytes long
+				if (chunk_len < 2)
+					break;
+				
+				num_tracks = source->read2();
+				break;
+			}
+		
+			// Didn't get to fill the header
+			if (num_tracks == 0)
+			{
+				cerr << "Not a valid XMID" << endl;
+				return 0;
+			}
+		
+			// Ok now to start part 2
+			// Goto the right place
+			source->seek (start+((len+1)&~1));
+		
+			// Read 4 bytes of type
+			source->read (buf, 4);
+
+			// Not an XMID
+			if (memcmp (buf, "CAT ", 4))
+			{
+				cerr << "Not a recognised XMID (" << buf[0] << buf[1] << buf[2] << buf[3] << ") should be (CAT )" << endl;
+				return 0;	
+			}
+			
+			// Now read length of this track
+			len = source->read4high();
+			
+			// Read 4 bytes of type
+			source->read (buf, 4);
+
+			// Not an XMID
+			if (memcmp (buf, "XMID", 4))
+			{
+				cerr << "Not a recognised XMID (" << buf[0] << buf[1] << buf[2] << buf[3] << ") should be (XMID)" << endl;
+				return 0;	
+			}
+
+		}
+
+		// Ok it's an XMID, so pass it to the ExtractCode
+
+		events = Calloc<XMIDIEventList*>(num_tracks); //new midi_event *[info.tracks];
+		
+		for (i = 0; i < num_tracks; i++)
+			events[i] = Calloc<XMIDIEventList>();
+
+		count = ExtractTracksFromXmi (source);
+
+		if (count != num_tracks)
+		{
+			cerr << "Error: unable to extract all (" << num_tracks << ") tracks specified from XMIDI. Only ("<< count << ")" << endl;
+			
+			int i = 0;
+			
+			for (i = 0; i < num_tracks; i++) {
+				events[i]->DecerementCounter();
+				events[i] = NULL;
+			}
+			
+			//delete [] events;
+			Free (events);
+			
+			return 0;		
+		}
+
+		return 1;
+		
+	}// Definately a Midi
+	else if (!memcmp (buf, "MThd", 4))
+	{
+		// Simple read length of header
+		len = source->read4high();
+
+		if (len < 6)
+		{
+			cerr << "Not a valid MIDI" << endl;
+			return 0;
+		}
+
+		int type = source->read2high();
+		
+		int actual_num = num_tracks = source->read2high();
+
+		// Type 1 only has 1 track, even though it says it has more
+		if (type == 1) num_tracks = 1;
+
+		events = Calloc<XMIDIEventList*>(num_tracks); //new midi_event *[info.tracks];
+		const uint32 ppqn = source->read2high();
+
+		for (i = 0; i < num_tracks; i++)
+			events[i] = Calloc<XMIDIEventList>();
+		
+		count = ExtractTracksFromMid (source, ppqn, actual_num, type == 1);
+
+		if (count != num_tracks)
+		{
+			cerr << "Error: unable to extract all (" << num_tracks << ") tracks specified from MIDI. Only ("<< count << ")" << endl;
+			
+			for (i = 0; i < num_tracks; i++) {
+				events[i]->DecerementCounter();
+				events[i] = NULL;
+			}
+			
+			Free (events);
+			
+			return 0;
+				
+		}
+
+		return 1;
+		
+	}// A RIFF Midi, just pass the source back to this function at the start of the midi file
+	else if (!memcmp (buf, "RIFF", 4))
+	{
+		// Read len
+		len = source->read4();
+
+		// Read 4 bytes of type
+		source->read (buf, 4);
+		
+		// Not an RMID
+		if (memcmp (buf, "RMID", 4))
+		{
+			cerr << "Invalid RMID" << endl;
+			return 0;
+		}
+
+		// Is a RMID
+
+		for (i = 4; i < len; i++)
+		{
+			// Read 4 bytes of type
+			source->read (buf, 4);
+			
+			chunk_len = source->read4();
+			
+			i+=8;
+				
+			if (memcmp (buf, "data", 4))
+			{	
+				// Must allign
+				source->skip ((chunk_len+1)&~1);
+				i+= (chunk_len+1)&~1;
+				continue;
+			}
+			
+			return ExtractTracks (source);
+
+		}
+		
+		cerr << "Failed to find midi data in RIFF Midi" << endl;
+		return 0;
+	}
+	
+	return 0;	
+}
+
+//
+// XMIDIEventList stuff
+//
+int	XMIDIEventList::Write (const char *filename)
+{
+//	FILE *file = fopen (filename, "wb"); // DARKE FIXME
+//	FileDataSource ds(file);
+//	int ret = Write(&ds);
+//	fclose (file);
+//	return ret;
+	return 0;
+}
+
+int XMIDIEventList::Write (DataSource *dest)
+{
+	int len = 0;
+	
+	if (!events)
+	{
+		cerr << "No midi data in loaded." << endl;
+		return 0;
+	}
+
+	// This is so if using buffer datasource, the caller can know how big to make the buffer
+	if (!dest)
+	{
+		// Header is 14 bytes long and add the rest as well
+		len = ConvertListToMTrk (NULL);
+		return 14 + len;
+	}
+		
+	dest->write1 ('M');
+	dest->write1 ('T');
+	dest->write1 ('h');
+	dest->write1 ('d');
+	
+	dest->write4high (6);
+
+	dest->write2high (0);
+	dest->write2high (1);
+	dest->write2high (60);	// The PPQN
+		
+	len = ConvertListToMTrk (dest);
+
+	return len + 14;
+}
+
+//
+// PutVLQ
+//
+// Write a Conventional Variable Length Quantity
+//
+int XMIDIEventList::PutVLQ(DataSource *dest, uint32 value)
+{
+	int buffer;
+	int i = 1;
+	buffer = value & 0x7F;
+	while (value >>= 7)
+	{
+		buffer <<= 8;
+		buffer |= ((value & 0x7F) | 0x80);
+		i++;
+	}
+	if (!dest) return i;
+	for (int j = 0; j < i; j++)
+	{
+		dest->write1(buffer & 0xFF);
+		buffer >>= 8;
+	}
+	
+	return i;
+}
+
+// Converts and event list to a MTrk
+// Returns bytes of the array
+// buf can be NULL
+uint32 XMIDIEventList::ConvertListToMTrk (DataSource *dest)
+{
+	int time = 0;
+	int lasttime = 0;
+	midi_event	*event;
+	uint32	delta;
+	unsigned char	last_status = 0;
+	uint32 	i = 8;
+	uint32 	j;
+	uint32	size_pos=0;
+
+	if (dest)
+	{
+		dest->write1('M');
+		dest->write1('T');
+		dest->write1('r');
+		dest->write1('k');
+
+		size_pos = dest->getPos();
+		dest->skip(4);
+	}
+
+	for (event = events; event; event=event->next)
+	{
+		// We don't write the end of stream marker here, we'll do it later
+		if (event->status == 0xFF && event->data[0] == 0x2f) {
+			lasttime = event->time;
+			continue;
+		}
+
+		delta = (event->time - time);
+		time = event->time;
+
+		i += PutVLQ (dest, delta);
+
+		if ((event->status != last_status) || (event->status >= 0xF0))
+		{
+			if (dest) dest->write1(event->status);
+			i++;
+		}
+		
+		last_status = event->status;
+		
+		switch (event->status >> 4)
+		{
+			// 2 bytes data
+			// Note off, Note on, Aftertouch, Controller and Pitch Wheel
+			case 0x8: case 0x9: case 0xA: case 0xB: case 0xE:
+			if (dest)
+			{
+				dest->write1(event->data[0]);
+				dest->write1(event->data[1]);
+			}
+			i += 2;
+			break;
+			
+
+			// 1 bytes data
+			// Program Change and Channel Pressure
+			case 0xC: case 0xD:
+			if (dest) dest->write1(event->data[0]);
+			i++;
+			break;
+			
+
+			// Variable length
+			// SysEx
+			case 0xF:
+			if (event->status == 0xFF)
+			{
+				if (dest) dest->write1(event->data[0]);
+				i++;
+			}
+	
+			i += PutVLQ (dest, event->len);
+			
+			if (event->len)
+			{
+				for (j = 0; j < event->len; j++)
+				{
+					if (dest) dest->write1(event->buffer[j]); 
+					i++;
+				}
+			}
+
+			break;
+			
+
+			// Never occur
+			default:
+			cerr << "Not supposed to see this" << endl;
+			break;
+		}
+	}
+
+	// Write out end of stream marker
+	if (lasttime > time) i += PutVLQ (dest, lasttime-time);
+	else i += PutVLQ (dest, 0);
+	if (dest) {
+		dest->write1(0xFF);
+		dest->write1(0x2F);
+	}
+	i += 2+PutVLQ (dest, 0);
+
+	if (dest)
+	{
+		int cur_pos = dest->getPos();
+		dest->seek (size_pos);
+		dest->write4high (i-8);
+		dest->seek (cur_pos);
+	}
+	return i;
+}
+
+
+void XMIDIEventList::DeleteEventList (midi_event *mlist)
+{
+	midi_event *event;
+	midi_event *next;
+	
+	next = mlist;
+	event = mlist;
+
+	while ((event = next))
+	{
+		next = event->next;
+		// We only do this with sysex
+		if ((event->status>>4) == 0xF && event->buffer) Free (event->buffer);
+		Free (event);
+	}
+}
+
+void XMIDIEventList::DecerementCounter()
+{
+	if (--counter < 0) {
+		DeleteEventList(events);
+		Free(this);
+	}
+}
--- /dev/null
+++ b/Game/src/midi/xmidi.h
@@ -1,0 +1,306 @@
+/*
+Copyright (C) 2000  Ryan Nunn
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+// Tab Size = 4
+
+#ifndef __XMIDI_h_
+#define __XMIDI_h_
+
+#include <string>
+//#include "common_types.h"
+#include "databuf.h"
+
+
+// Conversion types for Midi files
+#define XMIDI_CONVERT_NOCONVERSION		0
+#define XMIDI_CONVERT_MT32_TO_GM		1
+#define XMIDI_CONVERT_MT32_TO_GS		2
+#define XMIDI_CONVERT_MT32_TO_GS127		3
+#define XMIDI_CONVERT_OGG               4
+#define XMIDI_CONVERT_GS127_TO_GS		5
+#define XMIDI_CONVERT_EMIDI_GM			6	// For Duke3D! Selects General Midi tracks
+
+// Midi Status Bytes
+#define MIDI_STATUS_NOTE_OFF	0x8
+#define MIDI_STATUS_NOTE_ON		0x9
+#define MIDI_STATUS_AFTERTOUCH	0xA
+#define MIDI_STATUS_CONTROLLER	0xB
+#define MIDI_STATUS_PROG_CHANGE	0xC
+#define MIDI_STATUS_PRESSURE	0xD
+#define MIDI_STATUS_PITCH_WHEEL	0xE
+#define MIDI_STATUS_SYSEX		0xF
+
+// XMIDI Controllers
+#define XMIDI_CONTROLLER_CHAN_LOCK			0x6e	// Channel Lock
+#define XMIDI_CONTROLLER_CHAN_LOCK_PROT		0x6f	// Channel Lock Protect
+#define XMIDI_CONTROLLER_VOICE_PROT			0x70	// Voice Protect
+#define XMIDI_CONTROLLER_TIMBRE_PROT		0x71	// Timbre Protect
+#define XMIDI_CONTROLLER_BANK_CHANGE		0x72	// Bank Change
+#define XMIDI_CONTROLLER_IND_CTRL_PREFIX	0x73	// Indirect Controller Prefix
+#define XMIDI_CONTROLLER_FOR_LOOP			0x74	// For Loop
+#define XMIDI_CONTROLLER_NEXT_BREAK			0x75	// Next/Break
+#define XMIDI_CONTROLLER_CLEAR_BB_COUNT		0x76	// Clear Beat/Bar Count
+#define XMIDI_CONTROLLER_CALLBACK_TRIG		0x77	// Callback Trigger
+#define XMIDI_CONTROLLER_SEQ_BRANCH_INDEX	0x78	// Sequence Branch Index
+
+#define EMIDI_CONTROLLER_TRACK_DESIGNATION	110		// Track Designation
+#define EMIDI_CONTROLLER_TRACK_EXCLUSION	111		// Track Exclusion
+#define EMIDI_CONTROLLER_PROGRAM_CHANGE		112		// Program Change
+#define EMIDI_CONTROLLER_VOLUME				113		// Volume
+#define EMIDI_CONTROLLER_LOOP_BEGIN			XMIDI_CONTROLLER_FOR_LOOP
+#define EMIDI_CONTROLLER_LOOP_END			XMIDI_CONTROLLER_NEXT_BREAK
+
+
+// Maximum number of for loops we'll allow (used by win_midiout)
+#define XMIDI_MAX_FOR_LOOP_COUNT	128
+
+template <class T> class GammaTable;
+
+struct midi_event
+{
+	int				time;
+	unsigned char	status;
+
+	unsigned char	data[2];
+
+	uint32			len;		// Length of SysEx Data
+	unsigned char	*buffer;	// SysEx Data
+	int				duration;	// Duration of note (120 Hz)
+	midi_event		*next_note;	// The next note on the stack
+	uint32			note_time;	// Time note stops playing (6000th of second)
+	midi_event	*next;
+};
+
+class NoteStack {
+	midi_event		*notes;		// Top of the stack
+	int				polyphony;
+	int				max_polyphony;
+public:
+
+	NoteStack() : notes(0), polyphony(0), max_polyphony(0) { }
+
+	// Just clear it. Don't care about what's actually in it
+	void clear() {
+		notes=0;
+		polyphony=0;
+		max_polyphony=0;
+	}
+
+	// Pops the top of the stack if its off_time is <= time (6000th of second)
+	inline midi_event *PopTime(uint32 time) {
+		if (notes && notes->note_time <= time)  {
+			midi_event *note = notes;
+			notes = note->next_note;
+			note->next_note = 0;
+			polyphony--;
+			return note;
+		}
+
+		return 0;
+	}
+
+	// Pops the top of the stack
+	inline midi_event *Pop() {
+		if (notes)  {
+			midi_event *note = notes;
+			notes = note->next_note;
+			note->next_note = 0;
+			polyphony--;
+			return note;
+		}
+
+		return 0;
+	}
+
+	// Pops the top of the stack
+	inline midi_event *Remove(midi_event *event) {
+		midi_event *prev = 0;
+		midi_event *note = notes;
+		while (note) {
+
+			if (note == event) {
+				if (prev) prev->next_note = note->next_note;
+				else notes = note->next_note;
+				note->next_note = 0;
+				polyphony--;
+				return note;
+			}
+			prev = note;
+			note = note->next_note;
+		}
+		return 0;
+	}
+
+	// Finds the note that has same pitch and channel, and pops it
+	inline midi_event *FindAndPop(midi_event *event) {
+
+		midi_event *prev = 0;
+		midi_event *note = notes;
+		while (note) {
+
+			if ((note->status & 0xF) == (event->status & 0xF) && note->data[0] == event->data[0]) {
+				if (prev) prev->next_note = note->next_note;
+				else notes = note->next_note;
+				note->next_note = 0;
+				polyphony--;
+				return note;
+			}
+			prev = note;
+			note = note->next_note;
+		}
+		return 0;
+	}
+
+	// Pushes a note onto the top of the stack
+	inline void Push(midi_event *event) {
+		event->next_note = notes;
+		notes = event;
+		polyphony++;
+		if (max_polyphony < polyphony) max_polyphony = polyphony;
+	}
+
+
+	inline void Push(midi_event *event, uint32 time) {
+		event->note_time = time;
+		event->next_note = 0;
+
+		polyphony++;
+		if (max_polyphony < polyphony) max_polyphony = polyphony;
+
+		if (!notes || time <= notes->note_time) {
+			event->next_note = notes;
+			notes = event;
+		}
+		else {
+			midi_event *prev = notes;
+			while (prev) {
+				midi_event *note = prev->next_note;
+
+				if (!note || time <= note->note_time) {
+					event->next_note = note;
+					prev->next_note = event;
+					return;
+				}
+				prev = note;
+			}
+		}
+	}
+
+	inline int GetPolyphony() {
+		return polyphony;
+	}
+
+	inline int GetMaxPolyphony() {
+		return max_polyphony;
+	}
+};
+
+class XMIDIEventList 
+{
+	int				counter;
+	
+	// Helper funcs for Write
+	int				PutVLQ(DataSource *dest, uint32 value);
+	uint32			ConvertListToMTrk (DataSource *dest);
+
+	static void		DeleteEventList (midi_event *list);
+
+public:
+	midi_event		*events;
+
+	// Write this list to a file/buffer
+	int				Write (const char *filename);	
+	int				Write (DataSource *dest);	
+
+	// Increments the counter
+	void			IncerementCounter () { counter++; }
+
+	// Decrement the counter and delete the event list, if possible
+	void			DecerementCounter ();
+};
+
+class   XMIDI
+{
+protected:
+	uint16				num_tracks;
+
+private:
+	XMIDIEventList		**events;
+
+	midi_event			*list;
+	midi_event			*current;
+	midi_event			*notes_on;
+	
+	const static char	mt32asgm[128];
+	const static char	mt32asgs[256];
+	bool 				bank127[16];
+	int					convert_type;
+
+	bool				is_emidi;
+	bool				use_emidi_112;
+	bool				use_emidi_113;
+	
+	bool				do_reverb;
+	bool				do_chorus;
+	int					chorus_value;
+	int					reverb_value;
+
+public:
+	XMIDI(DataSource *source, int pconvert);
+	~XMIDI();
+
+	int number_of_tracks() { return num_tracks; }
+
+	// External Event list functions
+	XMIDIEventList *GetEventList (uint32 track);
+
+	// Not yet implimented
+	// int apply_patch (int track, DataSource *source);
+
+private:
+	XMIDI(); // No default constructor
+    
+    struct first_state {			// Status,	Data[0]
+		midi_event		*patch[16];	// 0xC
+		midi_event		*bank[16];	// 0xB,		0
+		midi_event		*pan[16];	// 0xB,		7
+		midi_event		*vol[16];	// 0xB,		10
+	};
+
+	// List manipulation
+	void CreateNewEvent (int time);
+
+	// Variable length quantity
+	int GetVLQ (DataSource *source, uint32 &quant);
+	int GetVLQ2 (DataSource *source, uint32 &quant);
+
+	void AdjustTimings(uint32 ppqn);	// This is used by Midi's ONLY!
+	void ApplyFirstState(first_state &fs, int chan_mask);
+
+	int ConvertNote (const int time, const unsigned char status, DataSource *source, const int size);
+	int ConvertEvent (const int time, unsigned char status, DataSource *source, const int size, first_state& fs);
+	int ConvertSystemMessage (const int time, const unsigned char status, DataSource *source);
+
+	int ConvertFiletoList (DataSource *source, const bool is_xmi, first_state& fs);
+
+	int ExtractTracksFromXmi (DataSource *source);
+	int ExtractTracksFromMid (DataSource *source, const uint32 ppqn, const int num_tracks, const bool type1);
+	
+	int ExtractTracks (DataSource *source);
+};
+#endif
--- /dev/null
+++ b/Game/src/mouse.h
@@ -1,0 +1,51 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef __mouse_h
+#define __mouse_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LEFT_MOUSE   1
+#define RIGHT_MOUSE  2
+#define MIDDLE_MOUSE 4
+#define LEFT_MOUSE_PRESSED( button ) ( ( ( button ) & LEFT_MOUSE ) != 0 )
+#define RIGHT_MOUSE_PRESSED( button ) ( ( ( button ) & RIGHT_MOUSE ) != 0 )
+#define MIDDLE_MOUSE_PRESSED( button ) ( ( ( button ) & MIDDLE_MOUSE ) != 0 )
+
+boolean MOUSE_Init( void );
+void    MOUSE_Shutdown( void );
+void    MOUSE_ShowCursor( void );
+void    MOUSE_HideCursor( void );
+int32   MOUSE_GetButtons( void );
+void    MOUSE_GetPosition( int32*x, int32*y  );
+void    MOUSE_GetDelta( int32*x, int32*y  );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* __mouse_h */
--- /dev/null
+++ b/Game/src/names.h
@@ -1,0 +1,754 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#define SECTOREFFECTOR 1
+#define ACTIVATOR 2
+#define TOUCHPLATE 3
+#define ACTIVATORLOCKED 4
+#define MUSICANDSFX 5
+#define LOCATORS 6
+#define CYCLER 7
+#define MASTERSWITCH 8
+#define RESPAWN 9
+#define GPSPEED 10
+#define FOF 13
+#define ARROW 20
+#define FIRSTGUNSPRITE 21
+#define CHAINGUNSPRITE 22
+#define RPGSPRITE 23
+#define FREEZESPRITE 24
+#define SHRINKERSPRITE 25
+#define HEAVYHBOMB 26
+#define TRIPBOMBSPRITE 27
+#define SHOTGUNSPRITE 28
+#define DEVISTATORSPRITE 29
+#define HEALTHBOX 30
+#define AMMOBOX 31
+#define GROWSPRITEICON 32
+#define INVENTORYBOX 33
+#define FREEZEAMMO 37
+#define AMMO 40
+#define BATTERYAMMO 41
+#define DEVISTATORAMMO 42
+#define RPGAMMO 44
+#define GROWAMMO 45
+#define CRYSTALAMMO 46
+#define HBOMBAMMO 47
+#define AMMOLOTS 48
+#define SHOTGUNAMMO 49
+#define COLA 51
+#define SIXPAK 52
+#define FIRSTAID 53
+#define SHIELD 54
+#define STEROIDS 55
+#define AIRTANK 56
+#define JETPACK 57
+#define HEATSENSOR 59
+#define ACCESSCARD 60
+#define BOOTS 61
+#define MIRRORBROKE 70
+#define CLOUDYOCEAN 78
+#define CLOUDYSKIES 79
+#define MOONSKY1 80
+#define MOONSKY2 81
+#define MOONSKY3 82
+#define MOONSKY4 83
+#define BIGORBIT1 84
+#define BIGORBIT2 85
+#define BIGORBIT3 86
+#define BIGORBIT4 87
+#define BIGORBIT5 88
+#define LA 89
+#define REDSKY1 98
+#define REDSKY2 99
+#define ATOMICHEALTH 100
+#define TECHLIGHT2 120
+#define TECHLIGHTBUST2 121
+#define TECHLIGHT4 122
+#define TECHLIGHTBUST4 123
+#define WALLLIGHT4 124
+#define WALLLIGHTBUST4 125
+#define ACCESSSWITCH 130
+#define SLOTDOOR 132
+#define LIGHTSWITCH 134
+#define SPACEDOORSWITCH 136
+#define SPACELIGHTSWITCH 138
+#define FRANKENSTINESWITCH 140
+#define NUKEBUTTON 142
+#define MULTISWITCH 146
+#define DOORTILE5 150
+#define DOORTILE6 151
+#define DOORTILE1 152
+#define DOORTILE2 153
+#define DOORTILE3 154
+#define DOORTILE4 155
+#define DOORTILE7 156
+#define DOORTILE8 157
+#define DOORTILE9 158
+#define DOORTILE10 159
+#define DOORSHOCK 160
+#define DIPSWITCH 162
+#define DIPSWITCH2 164
+#define TECHSWITCH 166
+#define DIPSWITCH3 168
+#define ACCESSSWITCH2 170
+#define REFLECTWATERTILE 180
+#define FLOORSLIME 200
+#define BIGFORCE 230
+#define EPISODE 247
+#define MASKWALL9 255
+#define W_LIGHT 260
+#define SCREENBREAK1 263
+#define SCREENBREAK2 264
+#define SCREENBREAK3 265
+#define SCREENBREAK4 266
+#define SCREENBREAK5 267
+#define SCREENBREAK6 268
+#define SCREENBREAK7 269
+#define SCREENBREAK8 270
+#define SCREENBREAK9 271
+#define SCREENBREAK10 272
+#define SCREENBREAK11 273
+#define SCREENBREAK12 274
+#define SCREENBREAK13 275
+#define MASKWALL1 285
+#define W_TECHWALL1 293
+#define W_TECHWALL2 297
+#define W_TECHWALL15 299
+#define W_TECHWALL3 301
+#define W_TECHWALL4 305
+#define W_TECHWALL10 306
+#define W_TECHWALL16 307
+#define WATERTILE2 336
+#define BPANNEL1 341
+#define PANNEL1 342
+#define PANNEL2 343
+#define WATERTILE 344
+#define STATIC 351
+#define W_SCREENBREAK 357
+#define W_HITTECHWALL3 360
+#define W_HITTECHWALL4 361
+#define W_HITTECHWALL2 362
+#define W_HITTECHWALL1 363
+#define MASKWALL10 387
+#define MASKWALL11 391
+#define DOORTILE22 395
+#define FANSPRITE 407
+#define FANSPRITEBROKE 411
+#define FANSHADOW 412
+#define FANSHADOWBROKE 416
+#define DOORTILE18 447
+#define DOORTILE19 448
+#define DOORTILE20 449
+// #define SPACESHUTTLE 487
+#define SATELLITE 489
+#define VIEWSCREEN2 499
+#define VIEWSCREENBROKE 501
+#define VIEWSCREEN 502
+#define GLASS 503
+#define GLASS2 504
+#define STAINGLASS1 510
+#define MASKWALL5 514
+#define SATELITE 516
+#define FUELPOD 517
+#define SLIMEPIPE 538
+#define CRACK1 546
+#define CRACK2 547
+#define CRACK3 548
+#define CRACK4 549
+#define FOOTPRINTS 550
+#define DOMELITE 551
+#define CAMERAPOLE 554
+#define CHAIR1 556
+#define CHAIR2 557
+#define BROKENCHAIR 559
+#define MIRROR 560
+#define WATERFOUNTAIN 563
+#define WATERFOUNTAINBROKE 567
+#define FEMMAG1 568
+#define TOILET 569
+#define STALL 571
+#define STALLBROKE 573
+#define FEMMAG2 577
+#define REACTOR2 578
+#define REACTOR2BURNT 579
+#define REACTOR2SPARK 580
+#define GRATE1 595
+#define BGRATE1 596
+#define SOLARPANNEL 602
+#define NAKED1 603
+#define ANTENNA 607
+#define MASKWALL12 609
+#define TOILETBROKE 615
+#define PIPE2 616
+#define PIPE1B 617
+#define PIPE3 618
+#define PIPE1 619
+#define CAMERA1 621
+#define BRICK 626
+#define SPLINTERWOOD 630
+#define PIPE2B 633
+#define BOLT1 634
+#define W_NUMBERS 640
+#define WATERDRIP 660
+#define WATERBUBBLE 661
+#define WATERBUBBLEMAKER 662
+#define W_FORCEFIELD 663
+#define VACUUM 669
+#define FOOTPRINTS2 672
+#define FOOTPRINTS3 673
+#define FOOTPRINTS4 674
+#define EGG 675
+#define SCALE 678
+#define CHAIR3 680
+#define CAMERALIGHT 685
+#define MOVIECAMERA 686
+#define IVUNIT 689
+#define POT1 694
+#define POT2 695
+#define POT3 697
+#define PIPE3B 700
+#define WALLLIGHT3 701
+#define WALLLIGHTBUST3 702
+#define WALLLIGHT1 703
+#define WALLLIGHTBUST1 704
+#define WALLLIGHT2 705
+#define WALLLIGHTBUST2 706
+#define LIGHTSWITCH2 712
+#define WAITTOBESEATED 716
+#define DOORTILE14 717
+#define STATUE 753
+#define MIKE 762
+#define VASE 765
+#define SUSHIPLATE1 768
+#define SUSHIPLATE2 769
+#define SUSHIPLATE3 774
+#define SUSHIPLATE4 779
+#define DOORTILE16 781
+#define SUSHIPLATE5 792
+#define OJ 806
+#define MASKWALL13 830
+#define HURTRAIL 859
+#define POWERSWITCH1 860
+#define LOCKSWITCH1 862
+#define POWERSWITCH2 864
+#define ATM 867
+#define STATUEFLASH 869
+#define ATMBROKE 888
+#define BIGHOLE2 893
+#define STRIPEBALL 901
+#define QUEBALL 902
+#define POCKET 903
+#define WOODENHORSE 904
+#define TREE1 908
+#define TREE2 910
+#define CACTUS 911
+#define MASKWALL2 913
+#define MASKWALL3 914
+#define MASKWALL4 915
+#define FIREEXT 916
+#define TOILETWATER 921
+#define NEON1 925
+#define NEON2 926
+#define CACTUSBROKE 939
+#define BOUNCEMINE 940
+#define BROKEFIREHYDRENT 950
+#define BOX 951
+#define BULLETHOLE 952
+#define BOTTLE1 954
+#define BOTTLE2 955
+#define BOTTLE3 956
+#define BOTTLE4 957
+#define FEMPIC5 963
+#define FEMPIC6 964
+#define FEMPIC7 965
+#define HYDROPLANT 969
+#define OCEANSPRITE1 971
+#define OCEANSPRITE2 972
+#define OCEANSPRITE3 973
+#define OCEANSPRITE4 974
+#define OCEANSPRITE5 975
+#define GENERICPOLE 977
+#define CONE 978
+#define HANGLIGHT 979
+#define HYDRENT 981
+#define MASKWALL14 988
+#define TIRE 990
+#define PIPE5 994
+#define PIPE6 995
+#define PIPE4 996
+#define PIPE4B 997
+#define BROKEHYDROPLANT 1003
+#define PIPE5B 1005
+#define NEON3 1007
+#define NEON4 1008
+#define NEON5 1009
+#define BOTTLE5 1012
+#define BOTTLE6 1013
+#define BOTTLE8 1014
+#define SPOTLITE 1020
+#define HANGOOZ 1022
+#define MASKWALL15 1024
+#define BOTTLE7 1025
+#define HORSEONSIDE 1026
+#define GLASSPIECES 1031
+#define HORSELITE 1034
+#define DONUTS 1045
+#define NEON6 1046
+#define MASKWALL6 1059
+#define CLOCK 1060
+#define RUBBERCAN 1062
+#define BROKENCLOCK 1067
+#define PLUG 1069
+#define OOZFILTER 1079
+#define FLOORPLASMA 1082
+#define REACTOR 1088
+#define REACTORSPARK 1092
+#define REACTORBURNT 1096
+#define DOORTILE15 1102
+#define HANDSWITCH 1111
+#define CIRCLEPANNEL 1113
+#define CIRCLEPANNELBROKE 1114
+#define PULLSWITCH 1122
+#define MASKWALL8 1124
+#define BIGHOLE 1141
+#define ALIENSWITCH 1142
+#define DOORTILE21 1144
+#define HANDPRINTSWITCH 1155
+#define BOTTLE10 1157
+#define BOTTLE11 1158
+#define BOTTLE12 1159
+#define BOTTLE13 1160
+#define BOTTLE14 1161
+#define BOTTLE15 1162
+#define BOTTLE16 1163
+#define BOTTLE17 1164
+#define BOTTLE18 1165
+#define BOTTLE19 1166
+#define DOORTILE17 1169
+#define MASKWALL7 1174
+#define JAILBARBREAK 1175
+#define DOORTILE11 1178
+#define DOORTILE12 1179
+#define VENDMACHINE 1212
+#define VENDMACHINEBROKE 1214
+#define COLAMACHINE 1215
+#define COLAMACHINEBROKE 1217
+#define CRANEPOLE 1221
+#define CRANE 1222
+#define BARBROKE 1225
+#define BLOODPOOL 1226
+#define NUKEBARREL 1227
+#define NUKEBARRELDENTED 1228
+#define NUKEBARRELLEAKED 1229
+#define CANWITHSOMETHING 1232
+#define MONEY 1233
+#define BANNER 1236
+#define EXPLODINGBARREL 1238
+#define EXPLODINGBARREL2 1239
+#define FIREBARREL 1240
+#define SEENINE 1247
+#define SEENINEDEAD 1248
+#define STEAM 1250
+#define CEILINGSTEAM 1255
+#define PIPE6B 1260
+#define TRANSPORTERBEAM 1261
+#define RAT 1267
+#define TRASH 1272
+#define FEMPIC1 1280
+#define FEMPIC2 1289
+#define BLANKSCREEN 1293
+#define PODFEM1 1294
+#define FEMPIC3 1298
+#define FEMPIC4 1306
+#define FEM1 1312
+#define FEM2 1317
+#define FEM3 1321
+#define FEM5 1323
+#define BLOODYPOLE 1324
+#define FEM4 1325
+#define FEM6 1334
+#define FEM6PAD 1335
+#define FEM8 1336
+#define HELECOPT 1346
+#define FETUSJIB 1347
+#define HOLODUKE 1348
+#define SPACEMARINE 1353
+#define INDY 1355
+#define FETUS 1358
+#define FETUSBROKE 1359
+#define MONK 1352
+#define LUKE 1354
+#define COOLEXPLOSION1 1360
+#define WATERSPLASH2 1380
+#define FIREVASE 1390
+#define SCRATCH 1393
+#define FEM7 1395
+#define APLAYERTOP 1400
+#define APLAYER 1405
+#define PLAYERONWATER 1420
+#define DUKELYINGDEAD 1518
+#define DUKETORSO 1520
+#define DUKEGUN 1528
+#define DUKELEG 1536
+#define SHARK 1550
+#define BLOOD 1620
+#define FIRELASER 1625
+#define TRANSPORTERSTAR 1630
+#define SPIT 1636
+#define LOOGIE 1637
+#define FIST 1640
+#define FREEZEBLAST 1641
+#define DEVISTATORBLAST 1642
+#define SHRINKSPARK 1646
+#define TONGUE 1647
+#define MORTER 1650
+#define SHRINKEREXPLOSION 1656
+#define RADIUSEXPLOSION 1670
+#define FORCERIPPLE 1671
+#define LIZTROOP 1680
+#define LIZTROOPRUNNING 1681
+#define LIZTROOPSTAYPUT 1682
+#define LIZTOP 1705
+#define LIZTROOPSHOOT 1715
+#define LIZTROOPJETPACK 1725
+#define LIZTROOPDSPRITE 1734
+#define LIZTROOPONTOILET 1741
+#define LIZTROOPJUSTSIT 1742
+#define LIZTROOPDUCKING 1744
+#define HEADJIB1 1768
+#define ARMJIB1 1772
+#define LEGJIB1 1776
+#define CANNONBALL 1817
+#define OCTABRAIN 1820
+#define OCTABRAINSTAYPUT 1821
+#define OCTATOP 1845
+#define OCTADEADSPRITE 1855
+#define INNERJAW 1860
+#define DRONE 1880
+#define EXPLOSION2 1890
+#define COMMANDER 1920
+#define COMMANDERSTAYPUT 1921
+#define RECON 1960
+#define TANK 1975
+#define PIGCOP 2000
+#define PIGCOPSTAYPUT 2001
+#define PIGCOPDIVE 2045
+#define PIGCOPDEADSPRITE 2060
+#define PIGTOP 2061
+#define LIZMAN 2120
+#define LIZMANSTAYPUT 2121
+#define LIZMANSPITTING 2150
+#define LIZMANFEEDING 2160
+#define LIZMANJUMP 2165
+#define LIZMANDEADSPRITE 2185
+#define FECES 2200
+#define LIZMANHEAD1 2201
+#define LIZMANARM1 2205
+#define LIZMANLEG1 2209
+#define EXPLOSION2BOT 2219
+#define USERWEAPON 2235
+#define HEADERBAR 2242
+#define JIBS1 2245
+#define JIBS2 2250
+#define JIBS3 2255
+#define JIBS4 2260
+#define JIBS5 2265
+#define BURNING 2270
+#define FIRE 2271
+#define JIBS6 2286
+#define BLOODSPLAT1 2296
+#define BLOODSPLAT3 2297
+#define BLOODSPLAT2 2298
+#define BLOODSPLAT4 2299
+#define OOZ 2300
+#define OOZ2 2309
+#define WALLBLOOD1 2301
+#define WALLBLOOD2 2302
+#define WALLBLOOD3 2303
+#define WALLBLOOD4 2304
+#define WALLBLOOD5 2305
+#define WALLBLOOD6 2306
+#define WALLBLOOD7 2307
+#define WALLBLOOD8 2308
+#define BURNING2 2310
+#define FIRE2 2311
+#define CRACKKNUCKLES 2324
+#define SMALLSMOKE 2329
+#define SMALLSMOKEMAKER 2330
+#define FLOORFLAME 2333
+#define ROTATEGUN 2360
+#define GREENSLIME 2370
+#define WATERDRIPSPLASH 2380
+#define SCRAP6 2390
+#define SCRAP1 2400
+#define SCRAP2 2404
+#define SCRAP3 2408
+#define SCRAP4 2412
+#define SCRAP5 2416
+#define ORGANTIC 2420
+#define BETAVERSION 2440
+#define PLAYERISHERE 2442
+#define PLAYERWASHERE 2443
+#define SELECTDIR 2444
+#define F1HELP 2445
+#define NOTCHON 2446
+#define NOTCHOFF 2447
+#define GROWSPARK 2448
+#define DUKEICON 2452
+#define BADGUYICON 2453
+#define FOODICON 2454
+#define GETICON 2455
+#define MENUSCREEN 2456
+#define MENUBAR 2457
+#define KILLSICON 2458
+#define FIRSTAID_ICON 2460
+#define HEAT_ICON 2461
+#define BOTTOMSTATUSBAR 2462
+#define BOOT_ICON 2463
+#define FRAGBAR 2465
+#define JETPACK_ICON 2467
+#define AIRTANK_ICON 2468
+#define STEROIDS_ICON 2469
+#define HOLODUKE_ICON 2470
+#define ACCESS_ICON 2471
+#define DIGITALNUM 2472
+#define DUKECAR 2491
+#define CAMCORNER 2482
+#define CAMLIGHT 2484
+#define LOGO 2485
+#define TITLE 2486
+#define NUKEWARNINGICON 2487
+#define MOUSECURSOR 2488
+#define SLIDEBAR 2489
+#define DREALMS 2492
+#define BETASCREEN 2493
+#define WINDOWBORDER1 2494
+#define TEXTBOX 2495
+#define WINDOWBORDER2 2496
+#define DUKENUKEM 2497
+#define THREEDEE 2498
+#define INGAMEDUKETHREEDEE 2499
+#define TENSCREEN 2500
+#define PLUTOPAKSPRITE 2501
+#define DEVISTATOR 2510
+#define KNEE 2521
+#define CROSSHAIR 2523
+#define FIRSTGUN 2524
+#define FIRSTGUNRELOAD 2528
+#define FALLINGCLIP 2530
+#define CLIPINHAND 2531
+#define HAND 2532
+#define SHELL 2533
+#define SHOTGUNSHELL 2535
+#define CHAINGUN 2536
+#define RPGGUN 2544
+#define RPGMUZZLEFLASH 2545
+#define FREEZE 2548
+#define CATLITE 2552
+#define SHRINKER 2556
+#define HANDHOLDINGLASER 2563
+#define TRIPBOMB 2566
+#define LASERLINE 2567
+#define HANDHOLDINGACCESS 2568
+#define HANDREMOTE 2570
+#define HANDTHROW 2573
+#define TIP 2576
+#define GLAIR 2578
+#define SCUBAMASK 2581
+#define SPACEMASK 2584
+#define FORCESPHERE 2590
+#define SHOTSPARK1 2595
+#define RPG 2605
+#define LASERSITE 2612
+#define SHOTGUN 2613
+#define BOSS1 2630
+#define BOSS1STAYPUT 2631
+#define BOSS1SHOOT 2660
+#define BOSS1LOB 2670
+#define BOSSTOP 2696
+#define BOSS2 2710
+#define BOSS3 2760
+#define SPINNINGNUKEICON 2813
+#define BIGFNTCURSOR 2820
+#define SMALLFNTCURSOR 2821
+#define STARTALPHANUM 2822
+#define ENDALPHANUM 2915
+#define BIGALPHANUM 2940
+#define BIGPERIOD 3002
+#define BIGCOMMA 3003
+#define BIGX 3004
+#define BIGQ 3005
+#define BIGSEMI 3006
+#define BIGCOLIN 3007
+#define THREEBYFIVE 3010
+#define BIGAPPOS 3022
+#define BLANK 3026
+#define MINIFONT 3072
+#define BUTTON1 3164
+#define GLASS3 3187
+#define RESPAWNMARKERRED 3190
+#define RESPAWNMARKERYELLOW 3200
+#define RESPAWNMARKERGREEN 3210
+#define BONUSSCREEN 3240
+#define VIEWBORDER 3250
+#define VICTORY1 3260
+#define ORDERING 3270
+#define TEXTSTORY 3280
+#define LOADSCREEN 3281
+#define BORNTOBEWILDSCREEN 3370
+#define BLIMP 3400
+#define FEM9 3450
+#define FOOTPRINT 3701
+#define POOP 4094
+#define FRAMEEFFECT1 4095
+// FIX_00037: steroids trigger a too many sprite respawn in 1.3 and 1.3d.
+#define FRAMEEFFECT1_13CON 3999 // Needed to ensure 1.3/1.3d compliance --mk
+#define PANNEL3 4099
+#define SCREENBREAK14 4120
+#define SCREENBREAK15 4123
+#define SCREENBREAK19 4125
+#define SCREENBREAK16 4127
+#define SCREENBREAK17 4128
+#define SCREENBREAK18 4129
+#define W_TECHWALL11 4130
+#define W_TECHWALL12 4131
+#define W_TECHWALL13 4132
+#define W_TECHWALL14 4133
+#define W_TECHWALL5 4134
+#define W_TECHWALL6 4136
+#define W_TECHWALL7 4138
+#define W_TECHWALL8 4140
+#define W_TECHWALL9 4142
+#define BPANNEL3 4100
+#define W_HITTECHWALL16 4144
+#define W_HITTECHWALL10 4145
+#define W_HITTECHWALL15 4147
+#define W_MILKSHELF 4181
+#define W_MILKSHELFBROKE 4203
+#define PURPLELAVA 4240
+#define LAVABUBBLE 4340
+#define DUKECUTOUT 4352
+#define TARGET 4359
+#define GUNPOWDERBARREL 4360
+#define DUCK 4361
+#define HATRACK 4367
+#define DESKLAMP 4370
+#define COFFEEMACHINE 4372
+#define CUPS 4373
+#define GAVALS 4374
+#define GAVALS2 4375
+#define POLICELIGHTPOLE 4377
+#define FLOORBASKET 4388
+#define PUKE 4389
+#define DOORTILE23 4391
+#define TOPSECRET 4396
+#define SPEAKER 4397
+#define TEDDYBEAR 4400
+#define ROBOTDOG 4402
+#define ROBOTPIRATE 4404
+#define ROBOTMOUSE 4407
+#define MAIL 4410
+#define MAILBAG 4413
+#define HOTMEAT 4427
+#define COFFEEMUG 4438
+#define DONUTS2 4440
+#define TRIPODCAMERA 4444
+#define METER 4453
+#define DESKPHONE 4454
+#define GUMBALLMACHINE 4458
+#define GUMBALLMACHINEBROKE 4459
+#define PAPER 4460
+#define MACE 4464
+#define GENERICPOLE2 4465
+#define XXXSTACY 4470
+#define WETFLOOR 4495
+#define BROOM 4496
+#define MOP 4497
+#define LETTER 4502
+#define PIRATE1A 4510
+#define PIRATE4A 4511
+#define PIRATE2A 4512
+#define PIRATE5A 4513
+#define PIRATE3A 4514
+#define PIRATE6A 4515
+#define PIRATEHALF 4516
+#define CHESTOFGOLD 4520
+#define SIDEBOLT1 4525
+#define FOODOBJECT1 4530
+#define FOODOBJECT2 4531
+#define FOODOBJECT3 4532
+#define FOODOBJECT4 4533
+#define FOODOBJECT5 4534
+#define FOODOBJECT6 4535
+#define FOODOBJECT7 4536
+#define FOODOBJECT8 4537
+#define FOODOBJECT9 4538
+#define FOODOBJECT10 4539
+#define FOODOBJECT11 4540
+#define FOODOBJECT12 4541
+#define FOODOBJECT13 4542
+#define FOODOBJECT14 4543
+#define FOODOBJECT15 4544
+#define FOODOBJECT16 4545
+#define FOODOBJECT17 4546
+#define FOODOBJECT18 4547
+#define FOODOBJECT19 4548
+#define FOODOBJECT20 4549
+#define HEADLAMP 4550
+#define TAMPON 4557
+#define SKINNEDCHICKEN 4554
+#define FEATHEREDCHICKEN 4555
+#define ROBOTDOG2 4560
+#define JOLLYMEAL 4569
+#define DUKEBURGER 4570
+#define SHOPPINGCART 4576
+#define CANWITHSOMETHING2 4580
+#define CANWITHSOMETHING3 4581
+#define CANWITHSOMETHING4 4582
+#define SNAKEP 4590
+#define DOLPHIN1 4591
+#define DOLPHIN2 4592
+#define NEWBEAST 4610
+#define NEWBEASTSTAYPUT 4611
+#define NEWBEASTJUMP 4690
+#define NEWBEASTHANG 4670
+#define NEWBEASTHANGDEAD 4671
+#define BOSS4 4740
+#define BOSS4STAYPUT 4741
+#define FEM10 4864
+#define TOUGHGAL 4866
+#define MAN 4871
+#define MAN2 4872
+#define WOMAN 4874
+#define PLEASEWAIT 4887
+#define NATURALLIGHTNING 4890
+#define WEATHERWARN 4893
+#define DUKETAG 4900
+#define SIGN1 4909
+#define SIGN2 4912
+#define JURYGUY 4943
+
--- /dev/null
+++ b/Game/src/player.c
@@ -1,0 +1,4471 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+// Savage Baggage Masters
+
+#include "duke3d.h"
+
+int32 turnheldtime; //MED
+int32 lastcontroltime; //MED
+
+void setpal(struct player_struct *p)
+{
+    if(p->heat_on) p->palette = slimepal;
+    else switch(sector[p->cursectnum].ceilingpicnum)
+    {
+        case FLOORSLIME:
+        case FLOORSLIME+1:
+        case FLOORSLIME+2:
+            p->palette = slimepal;
+            break;
+        default:
+            if(sector[p->cursectnum].lotag == 2) p->palette = waterpal;
+            else p->palette = palette;
+            break;
+    }
+    restorepalette = 1;
+}
+
+void incur_damage( struct player_struct *p )
+{
+    long  damage = 0L, shield_damage = 0L;
+    short i, damage_source;
+
+    sprite[p->i].extra -= p->extra_extra8>>8;
+
+    damage = sprite[p->i].extra - p->last_extra;
+
+    if ( damage < 0 )
+    {
+        p->extra_extra8 = 0;
+
+        if ( p->shield_amount > 0 )
+        {
+            shield_damage =  damage * (20 + (TRAND%30)) / 100;
+            damage -= shield_damage;
+
+            p->shield_amount += shield_damage;
+
+            if ( p->shield_amount < 0 )
+            {
+                damage += p->shield_amount;
+                p->shield_amount = 0;
+            }
+        }
+
+        sprite[p->i].extra = p->last_extra + damage;
+    }
+}
+
+void quickkill(struct player_struct *p)
+{
+    p->pals[0] = 48;
+    p->pals[1] = 48;
+    p->pals[2] = 48;
+    p->pals_time = 48;
+
+    sprite[p->i].extra = 0;
+    sprite[p->i].cstat |= 32768;
+    if(ud.god == 0) guts(&sprite[p->i],JIBS6,8,myconnectindex);
+    return;
+}
+
+void forceplayerangle(struct player_struct *p)
+{
+    short n;
+
+    n = 128-(TRAND&255);
+
+    p->horiz += 64;
+    p->return_to_center = 9;
+    p->look_ang = n>>1;
+    p->rotscrnang = n>>1;
+}
+
+void tracers(long x1,long y1,long z1,long x2,long y2,long z2,long n)
+{
+     long i, xv, yv, zv;
+     short sect = -1;
+
+	 i = n+1;
+	 xv = (x2-x1)/i;
+	 yv = (y2-y1)/i;
+	 zv = (z2-z1)/i;
+
+     if( ( klabs(x1-x2)+klabs(y1-y2) ) < 3084 )
+         return;
+
+	 for(i=n;i>0;i--)
+	 {
+		  x1 += xv;
+		  y1 += yv;
+		  z1 += zv;
+		  updatesector(x1,y1,&sect);
+          if(sect >= 0)
+          {
+              if(sector[sect].lotag == 2)
+                  EGS(sect,x1,y1,z1,WATERBUBBLE,-32,4+(TRAND&3),4+(TRAND&3),TRAND&2047,0,0,ps[0].i,5);
+              else
+                  EGS(sect,x1,y1,z1,SMALLSMOKE,-32,14,14,0,0,0,ps[0].i,5);
+          }
+	 }
+}
+
+long hits(short i)
+{
+    long sx,sy,sz;
+    short sect,hw,hs;
+    long zoff;
+
+    if(PN == APLAYER) zoff = (40<<8);
+    else zoff = 0;
+
+    hitscan(SX,SY,SZ-zoff,SECT,
+        sintable[(SA+512)&2047],
+        sintable[SA&2047],
+        0,&sect,&hw,&hs,&sx,&sy,&sz,CLIPMASK1);
+
+    return ( FindDistance2D( sx-SX,sy-SY ) );
+}
+
+long hitasprite(short i,short *hitsp)
+{
+    long sx,sy,sz,zoff;
+    short sect,hw;
+
+    if(badguy(&sprite[i]) )
+        zoff = (42<<8);
+    else if(PN == APLAYER) zoff = (39<<8);
+    else zoff = 0;
+
+    hitscan(SX,SY,SZ-zoff,SECT,
+        sintable[(SA+512)&2047],
+        sintable[SA&2047],
+        0,&sect,&hw,hitsp,&sx,&sy,&sz,CLIPMASK1);
+
+    if(hw >= 0 && (wall[hw].cstat&16) && badguy(&sprite[i]) )
+        return((1<<30));
+
+    return ( FindDistance2D(sx-SX,sy-SY) );
+}
+
+/*
+long hitaspriteandwall(short i,short *hitsp,short *hitw,short *x, short *y)
+{
+    long sz;
+    short sect;
+
+    hitscan(SX,SY,SZ,SECT,
+        sintable[(SA+512)&2047],
+        sintable[SA&2047],
+        0,&sect,hitw,hitsp,x,y,&sz,CLIPMASK1);
+
+    return ( FindDistance2D(*x-SX,*y-SY) );
+}
+*/
+
+
+long hitawall(struct player_struct *p,short *hitw)
+{
+    long sx,sy,sz;
+    short sect,hs;
+
+    hitscan(p->posx,p->posy,p->posz,p->cursectnum,
+        sintable[(p->ang+512)&2047],
+        sintable[p->ang&2047],
+        0,&sect,hitw,&hs,&sx,&sy,&sz,CLIPMASK0);
+
+    return ( FindDistance2D(sx-p->posx,sy-p->posy) );
+}
+
+short aim(spritetype *s,short aang, short auto_aim)
+{
+    char gotshrinker,gotfreezer;
+    short i, j, a, k, cans;
+    short aimstats[] = {10,13,1,2};
+    long dx1, dy1, dx2, dy2, dx3, dy3, smax, sdist;
+    long xv, yv;
+
+    a = s->ang;
+
+    j = -1;
+//    if(s->picnum == APLAYER && ps[s->yvel].aim_mode) return -1;
+
+    gotshrinker = s->picnum == APLAYER && ps[s->yvel].curr_weapon == SHRINKER_WEAPON;
+    gotfreezer = s->picnum == APLAYER && ps[s->yvel].curr_weapon == FREEZE_WEAPON;
+
+    smax = 0x7fffffff;
+
+    dx1 = sintable[(a+512-aang)&2047];
+    dy1 = sintable[(a-aang)&2047];
+    dx2 = sintable[(a+512+aang)&2047];
+    dy2 = sintable[(a+aang)&2047];
+
+    dx3 = sintable[(a+512)&2047];
+    dy3 = sintable[a&2047];
+
+	// FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116 and 117 only)
+
+	//if player has AutoAim ON do the function.
+	if((auto_aim && nHostForceDisableAutoaim == 0)||
+		ud.playing_demo_rev == BYTEVERSION_27     ||
+		ud.playing_demo_rev == BYTEVERSION_28     || 
+		ud.playing_demo_rev == BYTEVERSION_116    || 
+		ud.playing_demo_rev == BYTEVERSION_117) 
+		// don't disable any autoaim in case we are playing an old demo
+	{
+	    for(k=0;k<4;k++)
+	    {
+			if( j >= 0 )
+	            break;
+			for(i=headspritestat[aimstats[k]];i >= 0;i=nextspritestat[i])
+	            if( sprite[i].xrepeat > 0 && sprite[i].extra >= 0 && (sprite[i].cstat&(257+32768)) == 257)
+					if( badguy(&sprite[i]) || k < 2 )
+				{
+	                if(badguy(&sprite[i]) || PN == APLAYER || PN == SHARK)
+					{
+	                    if( PN == APLAYER &&
+//							  ud.ffire == 0 &&
+							ud.coop == 1 &&
+							s->picnum == APLAYER &&
+							s != &sprite[i])
+	                            continue;
+	
+						if(gotshrinker && sprite[i].xrepeat < 30 )
+						{
+	                        switch(PN)
+							{
+	                            case SHARK:
+									if(sprite[i].xrepeat < 20) continue;
+	                                    continue;
+								case GREENSLIME:
+								case GREENSLIME+1:
+								case GREENSLIME+2:
+								case GREENSLIME+3:
+								case GREENSLIME+4:
+								case GREENSLIME+5:
+								case GREENSLIME+6:
+								case GREENSLIME+7:
+	                                break;
+								default:
+	                                continue;
+							}
+						}
+						if(gotfreezer && sprite[i].pal == 1) continue;
+					}
+	
+					xv = (SX-s->x);
+					yv = (SY-s->y);
+	
+					if( (dy1*xv) <= (dx1*yv) )
+	                    if( ( dy2*xv ) >= (dx2*yv) )
+					{
+	                    sdist = mulscale(dx3,xv,14) + mulscale(dy3,yv,14);
+						if( sdist > 512 && sdist < smax )
+						{
+	                        if(s->picnum == APLAYER)
+								a = (klabs(scale(SZ-s->z,10,sdist)-(ps[s->yvel].horiz+ps[s->yvel].horizoff-100)) < 100);
+							else a = 1;
+	
+							if(PN == ORGANTIC || PN == ROTATEGUN )
+	                            cans = cansee(SX,SY,SZ,SECT,s->x,s->y,s->z-(32<<8),s->sectnum);
+							else cans = cansee(SX,SY,SZ-(32<<8),SECT,s->x,s->y,s->z-(32<<8),s->sectnum);
+	
+							if( a && cans )
+							{
+	                            smax = sdist;
+								j = i;
+							}
+						}
+					}
+				}
+		}
+	}
+
+    return j;
+}
+
+
+
+
+void shoot(short i,short atwith)
+{
+    short sect, hitsect, hitspr, hitwall, l, sa, p, j, k, scount;
+    long sx, sy, sz, vel, zvel, hitx, hity, hitz, x, oldzvel, dal;
+    unsigned char sizx,sizy;
+    spritetype *s;
+
+    s = &sprite[i];
+    sect = s->sectnum;
+    zvel = 0;
+
+    if( s->picnum == APLAYER )
+    {
+        p = s->yvel;
+
+        sx = ps[p].posx;
+        sy = ps[p].posy;
+        sz = ps[p].posz+ps[p].pyoff+(4<<8);
+        sa = ps[p].ang;
+
+        ps[p].crack_time = 777;
+
+    }
+    else
+    {
+        p = -1;
+        sa = s->ang;
+        sx = s->x;
+        sy = s->y;
+        sz = s->z-((s->yrepeat*tilesizy[s->picnum])<<1)+(4<<8);
+        if(s->picnum != ROTATEGUN)
+        {
+            sz -= (7<<8);
+            if(badguy(s) && PN != COMMANDER)
+            {
+                sx += (sintable[(sa+1024+96)&2047]>>7);
+                sy += (sintable[(sa+512+96)&2047]>>7);
+            }
+        }
+    }
+
+    switch(atwith)
+    {
+        case BLOODSPLAT1:
+        case BLOODSPLAT2:
+        case BLOODSPLAT3:
+        case BLOODSPLAT4:
+
+            if(p >= 0)
+                sa += 64 - (TRAND&127);
+            else sa += 1024 + 64 - (TRAND&127);
+            zvel = 1024-(TRAND&2047);
+        case KNEE:
+            if(atwith == KNEE )
+            {
+                if(p >= 0)
+                {
+                    zvel = (100-ps[p].horiz-ps[p].horizoff)<<5;
+                    sz += (6<<8);
+                    sa += 15;
+                }
+                else
+                {
+                    j = ps[findplayer(s,&x)].i;
+                    zvel = ( (sprite[j].z-sz)<<8 ) / (x+1);
+                    sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+            }
+
+//            writestring(sx,sy,sz,sect,sintable[(sa+512)&2047],sintable[sa&2047],zvel<<6);
+
+            hitscan(sx,sy,sz,sect,
+                sintable[(sa+512)&2047],
+                sintable[sa&2047],zvel<<6,
+                &hitsect,&hitwall,&hitspr,&hitx,&hity,&hitz,CLIPMASK1);
+
+            if( atwith == BLOODSPLAT1 ||
+                atwith == BLOODSPLAT2 ||
+                atwith == BLOODSPLAT3 ||
+                atwith == BLOODSPLAT4 )
+            {
+                if( FindDistance2D(sx-hitx,sy-hity) < 1024 )
+                    if( hitwall >= 0 && wall[hitwall].overpicnum != BIGFORCE )
+                        if( ( wall[hitwall].nextsector >= 0 && hitsect >= 0 &&
+                            sector[wall[hitwall].nextsector].lotag == 0 &&
+                                sector[hitsect].lotag == 0 &&
+                                    sector[wall[hitwall].nextsector].lotag == 0 &&
+                                        (sector[hitsect].floorz-sector[wall[hitwall].nextsector].floorz) > (16<<8) ) ||
+                                            ( wall[hitwall].nextsector == -1 && sector[hitsect].lotag == 0 ) )
+                                                if( (wall[hitwall].cstat&16) == 0)
+                {
+                    if(wall[hitwall].nextsector >= 0)
+                    {
+                        k = headspritesect[wall[hitwall].nextsector];
+                        while(k >= 0)
+                        {
+                            if(sprite[k].statnum == 3 && sprite[k].lotag == 13)
+                                return;
+                            k = nextspritesect[k];
+                        }
+                    }
+
+                    if( wall[hitwall].nextwall >= 0 &&
+                        wall[wall[hitwall].nextwall].hitag != 0 )
+                            return;
+
+                    if(wall[hitwall].hitag == 0)
+                    {
+                        k = spawn(i,atwith);
+                        sprite[k].xvel = -12;
+                        sprite[k].ang = getangle(wall[hitwall].x-wall[wall[hitwall].point2].x,
+                            wall[hitwall].y-wall[wall[hitwall].point2].y)+512;
+                        sprite[k].x = hitx;
+                        sprite[k].y = hity;
+                        sprite[k].z = hitz;
+                        sprite[k].cstat |= (TRAND&4);
+                        ssp(k,CLIPMASK0);
+                        setsprite(k,sprite[k].x,sprite[k].y,sprite[k].z);
+                        if( PN == OOZFILTER || PN == NEWBEAST )
+                            sprite[k].pal = 6;
+                    }
+                }
+                return;
+            }
+
+            if(hitsect < 0) break;
+
+            if( ( klabs(sx-hitx)+klabs(sy-hity) ) < 1024 )
+            {
+                if(hitwall >= 0 || hitspr >= 0)
+                {
+                    j = EGS(hitsect,hitx,hity,hitz,KNEE,-15,0,0,sa,32,0,i,4);
+                    sprite[j].extra += (TRAND&7);
+                    if(p >= 0)
+                    {
+                        k = spawn(j,SMALLSMOKE);
+                        sprite[k].z -= (8<<8);
+                        spritesound(KICK_HIT,j);
+                    }
+
+                    if ( p >= 0 && ps[p].steroids_amount > 0 && ps[p].steroids_amount < 400 )
+                        sprite[j].extra += (max_player_health>>2);
+
+                    if( hitspr >= 0 && sprite[hitspr].picnum != ACCESSSWITCH && sprite[hitspr].picnum != ACCESSSWITCH2 )
+                    {
+                        checkhitsprite(hitspr,j);
+                        if(p >= 0) checkhitswitch(p,hitspr,1);
+                    }
+
+                    else if( hitwall >= 0 )
+                    {
+                        if( wall[hitwall].cstat&2 )
+                            if(wall[hitwall].nextsector >= 0)
+                                if(hitz >= (sector[wall[hitwall].nextsector].floorz) )
+                                    hitwall = wall[hitwall].nextwall;
+
+                        if( hitwall >= 0 && wall[hitwall].picnum != ACCESSSWITCH && wall[hitwall].picnum != ACCESSSWITCH2 )
+                        {
+                            checkhitwall(j,hitwall,hitx,hity,hitz,atwith);
+                            if(p >= 0) checkhitswitch(p,hitwall,0);
+                        }
+                    }
+                }
+                else if(p >= 0 && zvel > 0 && sector[hitsect].lotag == 1)
+                {
+                    j = spawn(ps[p].i,WATERSPLASH2);
+                    sprite[j].x = hitx;
+                    sprite[j].y = hity;
+                    sprite[j].ang = ps[p].ang; // Total tweek
+                    sprite[j].xvel = 32;
+                    ssp(i,CLIPMASK0);
+                    sprite[j].xvel = 0;
+
+                }
+            }
+
+            break;
+
+        case SHOTSPARK1:
+        case SHOTGUN:
+        case CHAINGUN:
+
+            if( s->extra >= 0 ) s->shade = -96;
+
+            if(p >= 0)
+            {
+				j = aim( s, AUTO_AIM_ANGLE, ps[p].auto_aim!= 0 );
+                if(j >= 0)
+                {
+                    dal = ((sprite[j].xrepeat*tilesizy[sprite[j].picnum])<<1)+(5<<8);
+                    switch(sprite[j].picnum)
+                    {
+                        case GREENSLIME:
+                        case GREENSLIME+1:
+                        case GREENSLIME+2:
+                        case GREENSLIME+3:
+                        case GREENSLIME+4:
+                        case GREENSLIME+5:
+                        case GREENSLIME+6:
+                        case GREENSLIME+7:
+                        case ROTATEGUN:
+                            dal -= (8<<8);
+                            break;
+                    }
+                    zvel = ( ( sprite[j].z-sz-dal )<<8 ) / ldist(&sprite[ps[p].i], &sprite[j]) ;
+                    sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+
+                if(atwith == SHOTSPARK1)
+                {
+                    if(j == -1)
+                    {
+                        sa += 16-(TRAND&31);
+                        zvel = (100-ps[p].horiz-ps[p].horizoff)<<5;
+                        zvel += 128-(TRAND&255);
+                    }
+                }
+                else
+                {
+                    sa += 16-(TRAND&31);
+                    if(j == -1) zvel = (100-ps[p].horiz-ps[p].horizoff)<<5;
+                    zvel += 128-(TRAND&255);
+                }
+                sz -= (2<<8);
+            }
+            else
+            {
+                j = findplayer(s,&x);
+                sz -= (4<<8);
+                zvel = ( (ps[j].posz-sz) <<8 ) / (ldist(&sprite[ps[j].i], s ) );
+                if(s->picnum != BOSS1)
+                {
+                    zvel += 128-(TRAND&255);
+                    sa += 32-(TRAND&63);
+                }
+                else
+                {
+                    zvel += 128-(TRAND&255);
+                    sa = getangle(ps[j].posx-sx,ps[j].posy-sy)+64-(TRAND&127);
+                }
+            }
+
+            s->cstat &= ~257;
+            hitscan(sx,sy,sz,sect,
+                sintable[(sa+512)&2047],
+                sintable[sa&2047],
+                zvel<<6,&hitsect,&hitwall,&hitspr,&hitx,&hity,&hitz,CLIPMASK1);
+            s->cstat |= 257;
+
+            if(hitsect < 0) return;
+
+            if( (TRAND&15) == 0 && sector[hitsect].lotag == 2 )
+                tracers(hitx,hity,hitz,sx,sy,sz,8-(ud.multimode>>1));
+
+            if(p >= 0)
+            {
+                k = EGS(hitsect,hitx,hity,hitz,SHOTSPARK1,-15,10,10,sa,0,0,i,4);
+                sprite[k].extra = *actorscrptr[atwith];
+                sprite[k].extra += (TRAND%6);
+
+                if( hitwall == -1 && hitspr == -1)
+                {
+                    if( zvel < 0 )
+                    {
+                        if( sector[hitsect].ceilingstat&1 )
+                        {
+                            sprite[k].xrepeat = 0;
+                            sprite[k].yrepeat = 0;
+                            return;
+                        }
+                        else
+                            checkhitceiling(hitsect);
+                    }
+                    spawn(k,SMALLSMOKE);
+                }
+
+                if(hitspr >= 0)
+                {
+                    checkhitsprite(hitspr,k);
+                    if( sprite[hitspr].picnum == APLAYER && (ud.coop != 1 || ud.ffire == 1) )
+                    {
+                        l = spawn(k,JIBS6);
+                        sprite[k].xrepeat = sprite[k].yrepeat = 0;
+                        sprite[l].z += (4<<8);
+                        sprite[l].xvel = 16;
+                        sprite[l].xrepeat = sprite[l].yrepeat = 24;
+                        sprite[l].ang += 64-(TRAND&127);
+                    }
+                    else spawn(k,SMALLSMOKE);
+
+                    if(p >= 0 && (
+                        sprite[hitspr].picnum == DIPSWITCH ||
+                        sprite[hitspr].picnum == DIPSWITCH+1 ||
+                        sprite[hitspr].picnum == DIPSWITCH2 ||
+                        sprite[hitspr].picnum == DIPSWITCH2+1 ||
+                        sprite[hitspr].picnum == DIPSWITCH3 ||
+                        sprite[hitspr].picnum == DIPSWITCH3+1 ||
+                        sprite[hitspr].picnum == HANDSWITCH ||
+                        sprite[hitspr].picnum == HANDSWITCH+1) )
+                    {
+                        checkhitswitch(p,hitspr,1);
+                        return;
+                    }
+                }
+                else if( hitwall >= 0 )
+                {
+                    spawn(k,SMALLSMOKE);
+
+                    if( isadoorwall(wall[hitwall].picnum) == 1 )
+                        goto SKIPBULLETHOLE;
+                    if(p >= 0 && (
+                        wall[hitwall].picnum == DIPSWITCH ||
+                        wall[hitwall].picnum == DIPSWITCH+1 ||
+                        wall[hitwall].picnum == DIPSWITCH2 ||
+                        wall[hitwall].picnum == DIPSWITCH2+1 ||
+                        wall[hitwall].picnum == DIPSWITCH3 ||
+                        wall[hitwall].picnum == DIPSWITCH3+1 ||
+                        wall[hitwall].picnum == HANDSWITCH ||
+                        wall[hitwall].picnum == HANDSWITCH+1) )
+                    {
+                        checkhitswitch(p,hitwall,0);
+                        return;
+                    }
+
+                    if(wall[hitwall].hitag != 0 || ( wall[hitwall].nextwall >= 0 && wall[wall[hitwall].nextwall].hitag != 0 ) )
+                        goto SKIPBULLETHOLE;
+
+                    if( hitsect >= 0 && sector[hitsect].lotag == 0 )
+                        if( wall[hitwall].overpicnum != BIGFORCE )
+                            if( (wall[hitwall].nextsector >= 0 && sector[wall[hitwall].nextsector].lotag == 0 ) ||
+                                ( wall[hitwall].nextsector == -1 && sector[hitsect].lotag == 0 ) )
+                                    if( (wall[hitwall].cstat&16) == 0)
+                    {
+                        if(wall[hitwall].nextsector >= 0)
+                        {
+                            l = headspritesect[wall[hitwall].nextsector];
+                            while(l >= 0)
+                            {
+                                if(sprite[l].statnum == 3 && sprite[l].lotag == 13)
+                                    goto SKIPBULLETHOLE;
+                                l = nextspritesect[l];
+                            }
+                        }
+
+                        l = headspritestat[5];
+                        while(l >= 0)
+                        {
+                            if(sprite[l].picnum == BULLETHOLE)
+                                if(dist(&sprite[l],&sprite[k]) < (12+(TRAND&7)) )
+                                    goto SKIPBULLETHOLE;
+                            l = nextspritestat[l];
+                        }
+                        l = spawn(k,BULLETHOLE);
+                        sprite[l].xvel = -1;
+                        sprite[l].ang = getangle(wall[hitwall].x-wall[wall[hitwall].point2].x,
+                            wall[hitwall].y-wall[wall[hitwall].point2].y)+512;
+                        ssp(l,CLIPMASK0);
+                    }
+
+                    SKIPBULLETHOLE:
+
+                    if( wall[hitwall].cstat&2 )
+                        if(wall[hitwall].nextsector >= 0)
+                            if(hitz >= (sector[wall[hitwall].nextsector].floorz) )
+                                hitwall = wall[hitwall].nextwall;
+
+                    checkhitwall(k,hitwall,hitx,hity,hitz,SHOTSPARK1);
+                }
+            }
+            else
+            {
+                k = EGS(hitsect,hitx,hity,hitz,SHOTSPARK1,-15,24,24,sa,0,0,i,4);
+                sprite[k].extra = *actorscrptr[atwith];
+
+                if( hitspr >= 0 )
+                {
+                    checkhitsprite(hitspr,k);
+                    if( sprite[hitspr].picnum != APLAYER )
+                        spawn(k,SMALLSMOKE);
+                    else sprite[k].xrepeat = sprite[k].yrepeat = 0;
+                }
+                else if( hitwall >= 0 )
+                    checkhitwall(k,hitwall,hitx,hity,hitz,SHOTSPARK1);
+            }
+
+            if( (TRAND&255) < 4 )
+                xyzsound(PISTOL_RICOCHET,k,hitx,hity,hitz);
+
+            return;
+
+        case FIRELASER:
+        case SPIT:
+        case COOLEXPLOSION1:
+
+            if( s->extra >= 0 ) s->shade = -96;
+
+            scount = 1;
+            if(atwith == SPIT) vel = 292;
+            else
+            {
+                if(atwith == COOLEXPLOSION1)
+                {
+                    if(s->picnum == BOSS2) vel = 644;
+                    else vel = 348;
+                    sz -= (4<<7);
+                }
+                else
+                {
+                    vel = 840;
+                    sz -= (4<<7);
+                }
+            }
+
+            if(p >= 0)
+            {
+				j = aim( s, AUTO_AIM_ANGLE, ps[p].auto_aim==2 );
+                if(j >= 0)
+                {
+                    dal = ((sprite[j].xrepeat*tilesizy[sprite[j].picnum])<<1)-(12<<8);
+                    zvel = ((sprite[j].z-sz-dal)*vel ) / ldist(&sprite[ps[p].i], &sprite[j]) ;
+                    sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+                else
+                    zvel = (100-ps[p].horiz-ps[p].horizoff)*98;
+            }
+            else
+            {
+                j = findplayer(s,&x);
+//                sa = getangle(ps[j].oposx-sx,ps[j].oposy-sy);
+                sa += 16-(TRAND&31);
+                zvel = ( ( (ps[j].oposz - sz + (3<<8) ) )*vel ) / ldist(&sprite[ps[j].i],s);
+            }
+
+            oldzvel = zvel;
+
+            if(atwith == SPIT) { sizx = 18;sizy = 18,sz -= (10<<8); }
+            else
+            {
+                if( atwith == FIRELASER )
+                {
+                    if(p >= 0)
+                    {
+                        
+                        sizx = 34;
+                        sizy = 34;
+                    }
+                    else
+                    {
+                        sizx = 18;
+                        sizy = 18;
+                    }
+                }
+                else
+                {
+                    sizx = 18;
+                    sizy = 18;
+                }
+            }
+
+            if(p >= 0) sizx = 7,sizy = 7;
+
+            while(scount > 0)
+            {
+                j = EGS(sect,sx,sy,sz,atwith,-127,sizx,sizy,sa,vel,zvel,i,4);
+                sprite[j].extra += (TRAND&7);
+
+                if(atwith == COOLEXPLOSION1)
+                {
+                    sprite[j].shade = 0;
+                    if(PN == BOSS2)
+                    {
+                        l = sprite[j].xvel;
+                        sprite[j].xvel = 1024;
+                        ssp(j,CLIPMASK0);
+                        sprite[j].xvel = l;
+                        sprite[j].ang += 128-(TRAND&255);
+                    }
+                }
+
+                sprite[j].cstat = 128;
+                sprite[j].clipdist = 4;
+
+                sa = s->ang+32-(TRAND&63);
+                zvel = oldzvel+512-(TRAND&1023);
+
+                scount--;
+            }
+
+            return;
+
+        case FREEZEBLAST:
+            sz += (3<<8);
+        case RPG:
+
+            if( s->extra >= 0 ) s->shade = -96;
+
+            scount = 1;
+            vel = 644;
+
+            j = -1;
+
+            if(p >= 0)
+			{
+                j = aim( s, 48, ps[p].auto_aim==2);
+                if(j >= 0)
+                {
+                    dal = ((sprite[j].xrepeat*tilesizy[sprite[j].picnum])<<1)+(8<<8);
+                    zvel = ( (sprite[j].z-sz-dal)*vel ) / ldist(&sprite[ps[p].i], &sprite[j]);
+                    if( sprite[j].picnum != RECON )
+                        sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+                else zvel = (100-ps[p].horiz-ps[p].horizoff)*81;
+                if(atwith == RPG)
+                    spritesound(RPG_SHOOT,i);
+
+            }
+            else
+            {
+                j = findplayer(s,&x);
+                sa = getangle(ps[j].oposx-sx,ps[j].oposy-sy);
+                if(PN == BOSS3)
+                    sz -= (32<<8);
+                else if(PN == BOSS2)
+                {
+                    vel += 128;
+                    sz += 24<<8;
+                }
+
+                l = ldist(&sprite[ps[j].i],s);
+                zvel = ( (ps[j].oposz-sz)*vel) / l;
+
+                if( badguy(s) && (s->hitag&face_player_smart) )
+                    sa = s->ang+(TRAND&31)-16;
+            }
+
+            if( p >= 0 && j >= 0)
+               l = j;
+            else l = -1;
+
+            j = EGS(sect,
+                sx+(sintable[(348+sa+512)&2047]/448),
+                sy+(sintable[(sa+348)&2047]/448),
+                sz-(1<<8),atwith,0,14,14,sa,vel,zvel,i,4);
+
+            sprite[j].extra += (TRAND&7);
+            if(atwith != FREEZEBLAST)
+                sprite[j].yvel = l;
+            else
+            {
+                sprite[j].yvel = numfreezebounces;
+                sprite[j].xrepeat >>= 1;
+                sprite[j].yrepeat >>= 1;
+                sprite[j].zvel -= (2<<4);
+            }
+
+            if(p == -1)
+            {
+                if(PN == BOSS3)
+                {
+                    if(TRAND&1)
+                    {
+                        sprite[j].x -= sintable[sa&2047]>>6;
+                        sprite[j].y -= sintable[(sa+1024+512)&2047]>>6;
+                        sprite[j].ang -= 8;
+                    }
+                    else
+                    {
+                        sprite[j].x += sintable[sa&2047]>>6;
+                        sprite[j].y += sintable[(sa+1024+512)&2047]>>6;
+                        sprite[j].ang += 4;
+                    }
+                    sprite[j].xrepeat = 42;
+                    sprite[j].yrepeat = 42;
+                }
+                else if(PN == BOSS2)
+                {
+                    sprite[j].x -= sintable[sa&2047]/56;
+                    sprite[j].y -= sintable[(sa+1024+512)&2047]/56;
+                    sprite[j].ang -= 8+(TRAND&255)-128;
+                    sprite[j].xrepeat = 24;
+                    sprite[j].yrepeat = 24;
+                }
+                else if(atwith != FREEZEBLAST)
+                {
+                    sprite[j].xrepeat = 30;
+                    sprite[j].yrepeat = 30;
+                    sprite[j].extra >>= 2;
+                }
+            }
+            else if(ps[p].curr_weapon == DEVISTATOR_WEAPON)
+            {
+                sprite[j].extra >>= 2;
+                sprite[j].ang += 16-(TRAND&31);
+                sprite[j].zvel += 256-(TRAND&511);
+
+                if( ps[p].hbomb_hold_delay )
+                {
+                    sprite[j].x -= sintable[sa&2047]/644;
+                    sprite[j].y -= sintable[(sa+1024+512)&2047]/644;
+                }
+                else
+                {
+                    sprite[j].x += sintable[sa&2047]>>8;
+                    sprite[j].y += sintable[(sa+1024+512)&2047]>>8;
+                }
+                sprite[j].xrepeat >>= 1;
+                sprite[j].yrepeat >>= 1;
+            }
+
+            sprite[j].cstat = 128;
+            if(atwith == RPG)
+                sprite[j].clipdist = 4;
+            else
+                sprite[j].clipdist = 40;
+
+            break;
+
+        case HANDHOLDINGLASER:
+
+            if(p >= 0)
+                zvel = (100-ps[p].horiz-ps[p].horizoff)*32;
+            else zvel = 0;
+
+            hitscan(sx,sy,sz-ps[p].pyoff,sect,
+                sintable[(sa+512)&2047],
+                sintable[sa&2047],
+                zvel<<6,&hitsect,&hitwall,&hitspr,&hitx,&hity,&hitz,CLIPMASK1);
+
+            j = 0;
+            if(hitspr >= 0) break;
+
+            if(hitwall >= 0 && hitsect >= 0)
+                if( ((hitx-sx)*(hitx-sx)+(hity-sy)*(hity-sy)) < (290*290) )
+            {
+                if( wall[hitwall].nextsector >= 0)
+                {
+                    if( sector[wall[hitwall].nextsector].lotag <= 2 && sector[hitsect].lotag <= 2 )
+                        j = 1;
+                }
+                else if( sector[hitsect].lotag <= 2 )
+                    j = 1;
+            }
+
+            if(j == 1)
+            {
+                k = EGS(hitsect,hitx,hity,hitz,TRIPBOMB,-16,4,5,sa,0,0,i,6);
+
+                sprite[k].hitag = k;
+                spritesound(LASERTRIP_ONWALL,k);
+                sprite[k].xvel = -20;
+                ssp(k,CLIPMASK0);
+                sprite[k].cstat = 16;
+                hittype[k].temp_data[5] = sprite[k].ang = getangle(wall[hitwall].x-wall[wall[hitwall].point2].x,wall[hitwall].y-wall[wall[hitwall].point2].y)-512;
+
+                if(p >= 0)
+                    ps[p].ammo_amount[TRIPBOMB_WEAPON]--;
+
+            }
+            return;
+
+        case BOUNCEMINE:
+        case MORTER:
+
+            if( s->extra >= 0 ) s->shade = -96;
+
+            j = ps[findplayer(s,&x)].i;
+            x = ldist(&sprite[j],s);
+
+            zvel = -x>>1;
+
+            if(zvel < -4096)
+                zvel = -2048;
+            vel = x>>4;
+
+            EGS(sect,
+                sx+(sintable[(512+sa+512)&2047]>>8),
+                sy+(sintable[(sa+512)&2047]>>8),
+                sz+(6<<8),atwith,-64,32,32,sa,vel,zvel,i,1);
+            break;
+
+        case GROWSPARK:
+
+            if(p >= 0)
+            {
+                j = aim( s, AUTO_AIM_ANGLE, ps[p].auto_aim==2);
+                if(j >= 0)
+                {
+                    dal = ((sprite[j].xrepeat*tilesizy[sprite[j].picnum])<<1)+(5<<8);
+                    switch(sprite[j].picnum)
+                    {
+                        case GREENSLIME:
+                        case GREENSLIME+1:
+                        case GREENSLIME+2:
+                        case GREENSLIME+3:
+                        case GREENSLIME+4:
+                        case GREENSLIME+5:
+                        case GREENSLIME+6:
+                        case GREENSLIME+7:
+                        case ROTATEGUN:
+                            dal -= (8<<8);
+                            break;
+                    }
+                    zvel = ( ( sprite[j].z-sz-dal )<<8 ) / (ldist(&sprite[ps[p].i], &sprite[j]) );
+                    sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+                else
+                {
+                    sa += 16-(TRAND&31);
+                    zvel = (100-ps[p].horiz-ps[p].horizoff)<<5;
+                    zvel += 128-(TRAND&255);
+                }
+
+                sz -= (2<<8);
+            }
+            else
+            {
+                j = findplayer(s,&x);
+                sz -= (4<<8);
+                zvel = ( (ps[j].posz-sz) <<8 ) / (ldist(&sprite[ps[j].i], s ) );
+                zvel += 128-(TRAND&255);
+                sa += 32-(TRAND&63);
+            }
+
+            k = 0;
+
+//            RESHOOTGROW:
+
+            s->cstat &= ~257;
+            hitscan(sx,sy,sz,sect,
+                sintable[(sa+512)&2047],
+                sintable[sa&2047],
+                zvel<<6,&hitsect,&hitwall,&hitspr,&hitx,&hity,&hitz,CLIPMASK1);
+
+            s->cstat |= 257;
+
+            j = EGS(sect,hitx,hity,hitz,GROWSPARK,-16,28,28,sa,0,0,i,1);
+
+            sprite[j].pal = 2;
+            sprite[j].cstat |= 130;
+            sprite[j].xrepeat = sprite[j].yrepeat = 1;
+
+            if( hitwall == -1 && hitspr == -1 && hitsect >= 0)
+            {
+                if( zvel < 0 && (sector[hitsect].ceilingstat&1) == 0)
+                    checkhitceiling(hitsect);
+            }
+            else if(hitspr >= 0) checkhitsprite(hitspr,j);
+            else if(hitwall >= 0 && wall[hitwall].picnum != ACCESSSWITCH && wall[hitwall].picnum != ACCESSSWITCH2 )
+            {
+            /*    if(wall[hitwall].overpicnum == MIRROR && k == 0)
+                {
+                    l = getangle(
+                        wall[wall[hitwall].point2].x-wall[hitwall].x,
+                        wall[wall[hitwall].point2].y-wall[hitwall].y);
+
+                    sx = hitx;
+                    sy = hity;
+                    sz = hitz;
+                    sect = hitsect;
+                    sa = ((l<<1) - sa)&2047;
+                    sx += sintable[(sa+512)&2047]>>12;
+                    sy += sintable[sa&2047]>>12;
+
+                    k++;
+                    goto RESHOOTGROW;
+                }
+                else */
+                    checkhitwall(j,hitwall,hitx,hity,hitz,atwith);
+            }
+
+            break;
+        case SHRINKER:
+            if( s->extra >= 0 ) s->shade = -96;
+            if(p >= 0)
+            {
+                j = aim( s, AUTO_AIM_ANGLE, ps[p].auto_aim==2);
+                if(j >= 0)
+                {
+                    dal = ((sprite[j].xrepeat*tilesizy[sprite[j].picnum])<<1);
+                    zvel = ( (sprite[j].z-sz-dal-(4<<8))*768) / (ldist( &sprite[ps[p].i], &sprite[j]));
+                    sa = getangle(sprite[j].x-sx,sprite[j].y-sy);
+                }
+                else zvel = (100-ps[p].horiz-ps[p].horizoff)*98;
+            }
+            else if(s->statnum != 3)
+            {
+                j = findplayer(s,&x);
+                l = ldist(&sprite[ps[j].i],s);
+                zvel = ( (ps[j].oposz-sz)*512) / l ;
+            }
+            else zvel = 0;
+
+            j = EGS(sect,
+                sx+(sintable[(512+sa+512)&2047]>>12),
+                sy+(sintable[(sa+512)&2047]>>12),
+                sz+(2<<8),SHRINKSPARK,-16,28,28,sa,768,zvel,i,4);
+
+            sprite[j].cstat = 128;
+            sprite[j].clipdist = 32;
+
+
+            return;
+    }
+    return;
+}
+
+void displayloogie(short snum)
+{
+    long i, a, x, y, z;
+
+    if(ps[snum].loogcnt == 0) return;
+
+    y = (ps[snum].loogcnt<<2);
+    for(i=0;i<ps[snum].numloogs;i++)
+    {
+        a = klabs(sintable[((ps[snum].loogcnt+i)<<5)&2047])>>5;
+        z = 4096+((ps[snum].loogcnt+i)<<9);
+        x = (-sync[snum].avel)+(sintable[((ps[snum].loogcnt+i)<<6)&2047]>>10);
+
+        rotatesprite(
+            (ps[snum].loogiex[i]+x)<<16,(200+ps[snum].loogiey[i]-y)<<16,z-(i<<8),256-a,
+            LOOGIE,0,0,2,0,0,xdim-1,ydim-1);
+    }
+}
+
+char animatefist(short gs,short snum)
+{
+    short looking_arc,fisti,fistpal;
+    long fistzoom, fistz;
+
+    fisti = ps[snum].fist_incs;
+    if(fisti > 32) fisti = 32;
+    if(fisti <= 0) return 0;
+
+    looking_arc = klabs(ps[snum].look_ang)/9;
+
+    fistzoom = 65536L - (sintable[(512+(fisti<<6))&2047]<<2);
+    if(fistzoom > 90612L)
+        fistzoom = 90612L;
+    if(fistzoom < 40920)
+        fistzoom = 40290;
+    fistz = 194 + (sintable[((6+fisti)<<7)&2047]>>9);
+
+    if(sprite[ps[snum].i].pal == 1)
+        fistpal = 1;
+    else
+        fistpal = sector[ps[snum].cursectnum].floorpal;
+
+    rotatesprite(
+        (-fisti+222+(sync[snum].avel>>4))<<16,
+        (looking_arc+fistz)<<16,
+        fistzoom,0,FIST,gs,fistpal,2,0,0,xdim-1,ydim-1);
+
+    return 1;
+}
+
+char animateknee(short gs,short snum)
+{
+    short knee_y[] = {0,-8,-16,-32,-64,-84,-108,-108,-108,-72,-32,-8};
+    short looking_arc, pal;
+
+    if(ps[snum].knee_incs > 11 || ps[snum].knee_incs == 0 || sprite[ps[snum].i].extra <= 0) return 0;
+
+    looking_arc = knee_y[ps[snum].knee_incs] + klabs(ps[snum].look_ang)/9;
+
+    looking_arc -= (ps[snum].hard_landing<<3);
+
+    if(sprite[ps[snum].i].pal == 1)
+        pal = 1;
+    else
+    {
+        pal = sector[ps[snum].cursectnum].floorpal;
+        if(pal == 0)
+            pal = ps[snum].palookup;
+    }
+
+    myospal(105+(sync[snum].avel>>4)-(ps[snum].look_ang>>1)+(knee_y[ps[snum].knee_incs]>>2),looking_arc+280-((ps[snum].horiz-ps[snum].horizoff)>>4),KNEE,gs,4,pal);
+
+    return 1;
+}
+
+char animateknuckles(short gs,short snum)
+{
+    short knuckle_frames[] = {0,1,2,2,3,3,3,2,2,1,0};
+    short looking_arc, pal;
+
+    if(ps[snum].knuckle_incs == 0 || sprite[ps[snum].i].extra <= 0) return 0;
+
+    looking_arc = klabs(ps[snum].look_ang)/9;
+
+    looking_arc -= (ps[snum].hard_landing<<3);
+
+    if(sprite[ps[snum].i].pal == 1)
+        pal = 1;
+    else
+        pal = sector[ps[snum].cursectnum].floorpal;
+
+    myospal(160+(sync[snum].avel>>4)-(ps[snum].look_ang>>1),looking_arc+180-((ps[snum].horiz-ps[snum].horizoff)>>4),CRACKKNUCKLES+knuckle_frames[ps[snum].knuckle_incs>>1],gs,4,pal);
+
+    return 1;
+}
+
+
+
+long lastvisinc;
+
+void displaymasks(short snum)
+{
+    short i, p;
+
+    if(sprite[ps[snum].i].pal == 1)
+        p = 1;
+    else
+        p = sector[ps[snum].cursectnum].floorpal;
+
+     if(ps[snum].scuba_on)
+	 {
+        if(ud.screen_size > 4)
+        {
+            rotatesprite(43<<16,(200-8-(tilesizy[SCUBAMASK])<<16),65536,0,SCUBAMASK,0,p,2+16,windowx1,windowy1,windowx2,windowy2);
+            rotatesprite((320-43)<<16,(200-8-(tilesizy[SCUBAMASK])<<16),65536,1024,SCUBAMASK,0,p,2+4+16,windowx1,windowy1,windowx2,windowy2);
+        }
+        else
+        {
+            rotatesprite(43<<16,(200-(tilesizy[SCUBAMASK])<<16),65536,0,SCUBAMASK,0,p,2+16,windowx1,windowy1,windowx2,windowy2);
+            rotatesprite((320-43)<<16,(200-(tilesizy[SCUBAMASK])<<16),65536,1024,SCUBAMASK,0,p,2+4+16,windowx1,windowy1,windowx2,windowy2);
+        }
+	 }
+}
+
+char animatetip(short gs,short snum)
+{
+    short p,looking_arc;
+    short tip_y[] = {0,-8,-16,-32,-64,-84,-108,-108,-108,-108,-108,-108,-108,-108,-108,-108,-96,-72,-64,-32,-16};
+
+    if(ps[snum].tipincs == 0) return 0;
+
+    looking_arc = klabs(ps[snum].look_ang)/9;
+    looking_arc -= (ps[snum].hard_landing<<3);
+
+    if(sprite[ps[snum].i].pal == 1)
+        p = 1;
+    else
+        p = sector[ps[snum].cursectnum].floorpal;
+
+/*    if(ps[snum].access_spritenum >= 0)
+        p = sprite[ps[snum].access_spritenum].pal;
+    else
+        p = wall[ps[snum].access_wallnum].pal;
+  */
+    myospal(170+(sync[snum].avel>>4)-(ps[snum].look_ang>>1),
+        (tip_y[ps[snum].tipincs]>>1)+looking_arc+240-((ps[snum].horiz-ps[snum].horizoff)>>4),TIP+((26-ps[snum].tipincs)>>4),gs,0,p);
+
+    return 1;
+}
+
+char animateaccess(short gs,short snum)
+{
+    short access_y[] = {0,-8,-16,-32,-64,-84,-108,-108,-108,-108,-108,-108,-108,-108,-108,-108,-96,-72,-64,-32,-16};
+    short looking_arc;
+    char p;
+
+    if(ps[snum].access_incs == 0 || sprite[ps[snum].i].extra <= 0) return 0;
+
+    looking_arc = access_y[ps[snum].access_incs] + klabs(ps[snum].look_ang)/9;
+    looking_arc -= (ps[snum].hard_landing<<3);
+
+    if(ps[snum].access_spritenum >= 0)
+        p = sprite[ps[snum].access_spritenum].pal;
+    else p = 0;
+//    else
+//        p = wall[ps[snum].access_wallnum].pal;
+
+    if((ps[snum].access_incs-3) > 0 && (ps[snum].access_incs-3)>>3)
+        myospal(170+(sync[snum].avel>>4)-(ps[snum].look_ang>>1)+(access_y[ps[snum].access_incs]>>2),looking_arc+266-((ps[snum].horiz-ps[snum].horizoff)>>4),HANDHOLDINGLASER+(ps[snum].access_incs>>3),gs,0,p);
+    else
+        myospal(170+(sync[snum].avel>>4)-(ps[snum].look_ang>>1)+(access_y[ps[snum].access_incs]>>2),looking_arc+266-((ps[snum].horiz-ps[snum].horizoff)>>4),HANDHOLDINGACCESS,gs,4,p);
+
+    return 1;
+}
+
+short fistsign;
+
+void displayweapon(short snum)
+{
+    long gun_pos, looking_arc, cw;
+    long weapon_xoffset, i, j, x1, y1, x2;
+    char o,pal;
+    signed char gs;
+    struct player_struct *p;
+    short *kb;
+
+    p = &ps[snum];
+    kb = &p->kickback_pic;
+
+    o = 0;
+
+    looking_arc = klabs(p->look_ang)/9;
+
+    gs = sprite[p->i].shade;
+    if(gs > 24) gs = 24;
+
+    if(p->newowner >= 0 || ud.camerasprite >= 0 || p->over_shoulder_on > 0 || (sprite[p->i].pal != 1 && sprite[p->i].extra <= 0) || animatefist(gs,snum) || animateknuckles(gs,snum) || animatetip(gs,snum) || animateaccess(gs,snum) )
+        return;
+
+    animateknee(gs,snum);
+
+    gun_pos = 80-(p->weapon_pos*p->weapon_pos);
+
+    weapon_xoffset =  (160)-90;
+    weapon_xoffset -= (sintable[((p->weapon_sway>>1)+512)&2047]/(1024+512));
+    weapon_xoffset -= 58 + p->weapon_ang;
+    if( sprite[p->i].xrepeat < 32 )
+        gun_pos -= klabs(sintable[(p->weapon_sway<<2)&2047]>>9);
+    else gun_pos -= klabs(sintable[(p->weapon_sway>>1)&2047]>>10);
+
+    gun_pos -= (p->hard_landing<<3);
+
+    if(p->last_weapon >= 0)
+        cw = p->last_weapon;
+    else cw = p->curr_weapon;
+
+    j = 14-p->quick_kick;
+    if(j != 14)
+    {
+        if(sprite[p->i].pal == 1)
+            pal = 1;
+        else
+        {
+            pal = sector[p->cursectnum].floorpal;
+            if(pal == 0)
+                pal = p->palookup;
+        }
+
+
+        if( j < 5 || j > 9 )
+            myospal(weapon_xoffset+80-(p->look_ang>>1),
+                looking_arc+250-gun_pos,KNEE,gs,o|4,pal);
+        else myospal(weapon_xoffset+160-16-(p->look_ang>>1),
+            looking_arc+214-gun_pos,KNEE+1,gs,o|4,pal);
+    }
+
+    if( sprite[p->i].xrepeat < 40 )
+    {
+        if(p->jetpack_on == 0 )
+        {
+            i = sprite[p->i].xvel;
+            looking_arc += 32-(i>>1);
+            fistsign += i>>1;
+        }
+        cw = weapon_xoffset;
+        weapon_xoffset += sintable[(fistsign)&2047]>>10;
+        myos(weapon_xoffset+250-(p->look_ang>>1),
+             looking_arc+258-(klabs(sintable[(fistsign)&2047]>>8)),
+             FIST,gs,o);
+        weapon_xoffset = cw;
+        weapon_xoffset -= sintable[(fistsign)&2047]>>10;
+        myos(weapon_xoffset+40-(p->look_ang>>1),
+             looking_arc+200+(klabs(sintable[(fistsign)&2047]>>8)),
+             FIST,gs,o|4);
+    }
+    else 
+	{	
+		// FIX_00026: Weapon can now be hidden (on your screen only).
+		if(!ud.hideweapon || cw==KNEE_WEAPON || cw == HANDREMOTE_WEAPON)
+		{
+			switch(cw)
+			{
+				case KNEE_WEAPON:
+					if( (*kb) > 0 )
+					{
+						if(sprite[p->i].pal == 1)
+							pal = 1;
+						else
+						{
+							pal = sector[p->cursectnum].floorpal;
+							if(pal == 0)
+								pal = p->palookup;
+						}
+
+						if( (*kb) < 5 || (*kb) > 9 )
+							myospal(weapon_xoffset+220-(p->look_ang>>1),
+								looking_arc+250-gun_pos,KNEE,gs,o,pal);
+						else
+							myospal(weapon_xoffset+160-(p->look_ang>>1),
+							looking_arc+214-gun_pos,KNEE+1,gs,o,pal);
+					}
+					break;
+
+				case TRIPBOMB_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					weapon_xoffset += 8;
+					gun_pos -= 10;
+
+					if((*kb) > 6)
+						looking_arc += ((*kb)<<3);
+					else if((*kb) < 4)
+						myospal(weapon_xoffset+142-(p->look_ang>>1),
+								looking_arc+234-gun_pos,HANDHOLDINGLASER+3,gs,o,pal);
+
+					myospal(weapon_xoffset+130-(p->look_ang>>1),
+							looking_arc+249-gun_pos,
+							HANDHOLDINGLASER+((*kb)>>2),gs,o,pal);
+					myospal(weapon_xoffset+152-(p->look_ang>>1),
+							looking_arc+249-gun_pos,
+							HANDHOLDINGLASER+((*kb)>>2),gs,o|4,pal);
+
+					break;
+
+				case RPG_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else pal = sector[p->cursectnum].floorpal;
+
+					weapon_xoffset -= sintable[(768+((*kb)<<7))&2047]>>11;
+					gun_pos += sintable[(768+((*kb)<<7)&2047)]>>11;
+
+					if(*kb > 0)
+					{
+						if(*kb < 8)
+						{
+							myospal(weapon_xoffset+164,(looking_arc<<1)+176-gun_pos,
+									RPGGUN+((*kb)>>1),gs,o,pal);
+						}
+					}
+
+					myospal(weapon_xoffset+164,(looking_arc<<1)+176-gun_pos,
+							RPGGUN,gs,o,pal);
+
+					break;
+
+				case SHOTGUN_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					weapon_xoffset -= 8;
+
+					switch(*kb)
+					{
+						case 1:
+						case 2:
+							myospal(weapon_xoffset+168-(p->look_ang>>1),looking_arc+201-gun_pos,
+							SHOTGUN+2,-128,o,pal);
+						case 0:
+						case 6:
+						case 7:
+						case 8:
+							myospal(weapon_xoffset+146-(p->look_ang>>1),looking_arc+202-gun_pos,
+								SHOTGUN,gs,o,pal);
+							break;
+						case 3:
+						case 4:
+						case 5:
+						case 9:
+						case 10:
+						case 11:
+						case 12:
+							if( *kb > 1 && *kb < 5 )
+							{
+								gun_pos -= 40;
+								weapon_xoffset += 20;
+
+								myospal(weapon_xoffset+178-(p->look_ang>>1),looking_arc+194-gun_pos,
+									SHOTGUN+1+((*(kb)-1)>>1),-128,o,pal);
+							}
+
+							myospal(weapon_xoffset+158-(p->look_ang>>1),looking_arc+220-gun_pos,
+								SHOTGUN+3,gs,o,pal);
+
+							break;
+						case 13:
+						case 14:
+						case 15:
+							myospal(32+weapon_xoffset+166-(p->look_ang>>1),looking_arc+210-gun_pos,
+								SHOTGUN+4,gs,o,pal);
+							break;
+						case 16:
+						case 17:
+						case 18:
+						case 19:
+							myospal(64+weapon_xoffset+170-(p->look_ang>>1),looking_arc+196-gun_pos,
+								SHOTGUN+5,gs,o,pal);
+							break;
+						case 20:
+						case 21:
+						case 22:
+						case 23:
+							myospal(64+weapon_xoffset+176-(p->look_ang>>1),looking_arc+196-gun_pos,
+								SHOTGUN+6,gs,o,pal);
+							break;
+						case 24:
+						case 25:
+						case 26:
+						case 27:
+							myospal(64+weapon_xoffset+170-(p->look_ang>>1),looking_arc+196-gun_pos,
+								SHOTGUN+5,gs,o,pal);
+							break;
+						case 28:
+						case 29:
+						case 30:
+							myospal(32+weapon_xoffset+156-(p->look_ang>>1),looking_arc+206-gun_pos,
+								SHOTGUN+4,gs,o,pal);
+							break;
+							}
+					break;
+
+
+
+				case CHAINGUN_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					if(*kb > 0)
+						gun_pos -= sintable[(*kb)<<7]>>12;
+
+					if(*kb > 0 && sprite[p->i].pal != 1) weapon_xoffset += 1-(rand()&3);
+
+					myospal(weapon_xoffset+168-(p->look_ang>>1),looking_arc+260-gun_pos,
+						CHAINGUN,gs,o,pal);
+					switch(*kb)
+					{
+						case 0:
+							myospal(weapon_xoffset+178-(p->look_ang>>1),looking_arc+233-gun_pos,
+								CHAINGUN+1,gs,o,pal);
+							break;
+						default:
+							if(*kb > 4 && *kb < 12)
+							{
+								i = 0;
+								if(sprite[p->i].pal != 1) i = rand()&7;
+								myospal(i+weapon_xoffset-4+140-(p->look_ang>>1),i+looking_arc-((*kb)>>1)+208-gun_pos,
+									CHAINGUN+5+((*kb-4)/5),gs,o,pal);
+								if(sprite[p->i].pal != 1) i = rand()&7;
+								myospal(i+weapon_xoffset-4+184-(p->look_ang>>1),i+looking_arc-((*kb)>>1)+208-gun_pos,
+									CHAINGUN+5+((*kb-4)/5),gs,o,pal);
+							}
+							if(*kb < 8)
+							{
+								i = rand()&7;
+								myospal(i+weapon_xoffset-4+162-(p->look_ang>>1),i+looking_arc-((*kb)>>1)+208-gun_pos,
+									CHAINGUN+5+((*kb-2)/5),gs,o,pal);
+								myospal(weapon_xoffset+178-(p->look_ang>>1),looking_arc+233-gun_pos,
+									CHAINGUN+1+((*kb)>>1),gs,o,pal);
+							}
+							else myospal(weapon_xoffset+178-(p->look_ang>>1),looking_arc+233-gun_pos,
+								CHAINGUN+1,gs,o,pal);
+							break;
+					}
+					break;
+				case PISTOL_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					if( (*kb) < 5)
+					{
+						short kb_frames[] = {0,1,2,0,0},l;
+
+						l = 195-12+weapon_xoffset;
+
+						if((*kb) == 2)
+							l -= 3;
+						myospal(
+							(l-(p->look_ang>>1)),
+							(looking_arc+244-gun_pos),
+							FIRSTGUN+kb_frames[*kb],
+							gs,2,pal);
+					}
+					else
+					{
+						if((*kb) < 10)
+							myospal(194-(p->look_ang>>1),looking_arc+230-gun_pos,FIRSTGUN+4,gs,o,pal);
+						else if((*kb) < 15)
+						{
+							myospal(244-((*kb)<<3)-(p->look_ang>>1),looking_arc+130-gun_pos+((*kb)<<4),FIRSTGUN+6,gs,o,pal);
+							myospal(224-(p->look_ang>>1),looking_arc+220-gun_pos,FIRSTGUN+5,gs,o,pal);
+						}
+						else if((*kb) < 20)
+						{
+							myospal(124+((*kb)<<1)-(p->look_ang>>1),looking_arc+430-gun_pos-((*kb)<<3),FIRSTGUN+6,gs,o,pal);
+							myospal(224-(p->look_ang>>1),looking_arc+220-gun_pos,FIRSTGUN+5,gs,o,pal);
+						}
+						else if((*kb) < 23)
+						{
+							myospal(184-(p->look_ang>>1),looking_arc+235-gun_pos,FIRSTGUN+8,gs,o,pal);
+							myospal(224-(p->look_ang>>1),looking_arc+210-gun_pos,FIRSTGUN+5,gs,o,pal);
+						}
+						else if((*kb) < 25)
+						{
+							myospal(164-(p->look_ang>>1),looking_arc+245-gun_pos,FIRSTGUN+8,gs,o,pal);
+							myospal(224-(p->look_ang>>1),looking_arc+220-gun_pos,FIRSTGUN+5,gs,o,pal);
+						}
+						else if((*kb) < 27)
+							myospal(194-(p->look_ang>>1),looking_arc+235-gun_pos,FIRSTGUN+5,gs,o,pal);
+					}
+
+					break;
+				case HANDBOMB_WEAPON:
+				{
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					if((*kb))
+					{
+						char throw_frames[]
+							= {0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2};
+
+						if((*kb) < 7)
+							gun_pos -= 10*(*kb);        //D
+						else if((*kb) < 12)
+							gun_pos += 20*((*kb)-10); //U
+						else if((*kb) < 20)
+							gun_pos -= 9*((*kb)-14);  //D
+
+						myospal(weapon_xoffset+190-(p->look_ang>>1),looking_arc+250-gun_pos,HANDTHROW+throw_frames[(*kb)],gs,o,pal);
+					}
+					else
+						myospal(weapon_xoffset+190-(p->look_ang>>1),looking_arc+260-gun_pos,HANDTHROW,gs,o,pal);
+				}
+				break;
+
+				case HANDREMOTE_WEAPON:
+				{
+						signed char remote_frames[] = {0,1,1,2,1,1,0,0,0,0,0};
+						if(sprite[p->i].pal == 1)
+							pal = 1;
+						else
+							pal = sector[p->cursectnum].floorpal;
+
+						weapon_xoffset = -48;
+
+						if((*kb))
+							myospal(weapon_xoffset+150-(p->look_ang>>1),looking_arc+258-gun_pos,HANDREMOTE+remote_frames[(*kb)],gs,o,pal);
+						else
+							myospal(weapon_xoffset+150-(p->look_ang>>1),looking_arc+258-gun_pos,HANDREMOTE,gs,o,pal);
+					}
+					break;
+				case DEVISTATOR_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					if((*kb))
+					{
+						char cycloidy[] = {0,4,12,24,12,4,0};
+
+						i = sgn((*kb)>>2);
+
+						if(p->hbomb_hold_delay)
+						{
+							myospal( (cycloidy[*kb]>>1)+weapon_xoffset+268-(p->look_ang>>1),cycloidy[*kb]+looking_arc+238-gun_pos,DEVISTATOR+i,-32,o,pal);
+							myospal(weapon_xoffset+30-(p->look_ang>>1),looking_arc+240-gun_pos,DEVISTATOR,gs,o|4,pal);
+						}
+						else
+						{
+							myospal( -(cycloidy[*kb]>>1)+weapon_xoffset+30-(p->look_ang>>1),cycloidy[*kb]+looking_arc+240-gun_pos,DEVISTATOR+i,-32,o|4,pal);
+							myospal(weapon_xoffset+268-(p->look_ang>>1),looking_arc+238-gun_pos,DEVISTATOR,gs,o,pal);
+						}
+					}
+					else
+					{
+						myospal(weapon_xoffset+268-(p->look_ang>>1),looking_arc+238-gun_pos,DEVISTATOR,gs,o,pal);
+						myospal(weapon_xoffset+30-(p->look_ang>>1),looking_arc+240-gun_pos,DEVISTATOR,gs,o|4,pal);
+					}
+					break;
+
+				case FREEZE_WEAPON:
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+
+					if((*kb))
+					{
+						char cat_frames[] = { 0,0,1,1,2,2 };
+
+						if(sprite[p->i].pal != 1)
+						{
+							weapon_xoffset += rand()&3;
+							looking_arc += rand()&3;
+						}
+						gun_pos -= 16;
+						myospal(weapon_xoffset+210-(p->look_ang>>1),looking_arc+261-gun_pos,FREEZE+2,-32,o,pal);
+						myospal(weapon_xoffset+210-(p->look_ang>>1),looking_arc+235-gun_pos,FREEZE+3+cat_frames[*kb%6],-32,o,pal);
+					}
+					else myospal(weapon_xoffset+210-(p->look_ang>>1),looking_arc+261-gun_pos,FREEZE,gs,o,pal);
+
+					break;
+
+				case SHRINKER_WEAPON:
+				case GROW_WEAPON:
+					weapon_xoffset += 28;
+					looking_arc += 18;
+					if(sprite[p->i].pal == 1)
+						pal = 1;
+					else
+						pal = sector[p->cursectnum].floorpal;
+					if((*kb) == 0)
+					{
+						if(cw == GROW_WEAPON)
+						{
+							myospal(weapon_xoffset+184-(p->look_ang>>1),
+								looking_arc+240-gun_pos,SHRINKER+2,
+								16-(sintable[p->random_club_frame&2047]>>10),
+								o,2);
+
+							myospal(weapon_xoffset+188-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER-2,gs,o,pal);
+						}
+						else
+						{
+							myospal(weapon_xoffset+184-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER+2,
+							16-(sintable[p->random_club_frame&2047]>>10),
+							o,0);
+
+							myospal(weapon_xoffset+188-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER,gs,o,pal);
+						}
+					}
+					else
+					{
+						if(sprite[p->i].pal != 1)
+						{
+							weapon_xoffset += rand()&3;
+							gun_pos += (rand()&3);
+						}
+
+						if(cw == GROW_WEAPON)
+						{
+							myospal(weapon_xoffset+184-(p->look_ang>>1),
+								looking_arc+240-gun_pos,SHRINKER+3+((*kb)&3),-32,
+								o,2);
+
+							myospal(weapon_xoffset+188-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER-1,gs,o,pal);
+
+						}
+						else
+						{
+							myospal(weapon_xoffset+184-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER+3+((*kb)&3),-32,
+							o,0);
+
+							myospal(weapon_xoffset+188-(p->look_ang>>1),
+							looking_arc+240-gun_pos,SHRINKER+1,gs,o,pal);
+						}
+					}
+					break;
+			}
+		}
+	}
+
+    displayloogie(snum);
+
+}
+
+#define TURBOTURNTIME (TICRATE/8) // 7
+#define NORMALTURN   15
+#define PREAMBLETURN 5
+#define NORMALKEYMOVE 40
+#define MAXVEL       ((NORMALKEYMOVE*2)+10)
+#define MAXSVEL      ((NORMALKEYMOVE*2)+10)
+#define MAXANGVEL    127
+#define MAXHORIZ     127
+
+long myaimmode = 0, myaimstat = 0, omyaimstat = 0;
+
+void getinput(short snum)
+{
+
+    short j, daang;
+// MED
+    ControlInfo info;
+    int32 tics;
+    boolean running;
+    int32 turnamount;
+    int32 keymove;
+    int32 momx,momy;
+    struct player_struct *p;
+
+	// FIX_00038: Improved Mouse accuracy (losses of integer computation)    
+	static fixed previousInfoDyaw = 0;
+	static fixed previousInfoDpitch = 0;
+	static fixed previousInfoDyawSvel = 0;
+
+    momx = momy = 0;
+    p = &ps[snum];
+
+    CONTROL_GetInput( &info );
+
+	// FIX_00021: Duke was moving when moving the mouse up/down. Y axis move is disabled.
+	info.dz = 0; // remove y axis
+
+    if( (p->gm&MODE_MENU) || (p->gm&MODE_TYPE) || (ud.pause_on && !KB_KeyPressed(sc_Pause)) )
+    {
+         loc.fvel = vel = 0;
+         loc.svel = svel = 0;
+         loc.avel = angvel = 0;
+         loc.horz = horiz = 0;
+         loc.bits = (((long)gamequit)<<26);
+         info.dz = info.dyaw = 0;
+         return;
+    }
+
+    tics = totalclock-lastcontroltime;
+    lastcontroltime = totalclock;
+
+
+    if (MouseAiming)
+          myaimmode = ACTION(gamefunc_Mouse_Aiming); // mouse aiming button is temporary
+     else
+	 {		// mouse aiming button is a toggle
+		  omyaimstat = myaimstat; myaimstat = ACTION(gamefunc_Mouse_Aiming);
+		  if (myaimstat > omyaimstat)
+          {
+				myaimmode ^= 1;
+                FTA(44+myaimmode,p,1);
+          }
+	 }
+
+	// FIX_00039: Toggle autoaim between Normal (full) and partial (on bullet weapons only)
+	if( ACTION(gamefunc_Auto_Aim) && nHostForceDisableAutoaim == 0)
+	{
+		ud.auto_aim++;
+		ud.auto_aim = ((ud.auto_aim-1)%2)+1;
+		sprintf(fta_quotes[103],"AUTOAIM %s", ud.auto_aim?(ud.auto_aim==1)?"BULLET ONLY":"NORMAL (FULL)":"OFF");
+		vscrn();	// FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+					// This is because we use the same FTA for BULLET and NORMAL text: duke doesn't see we 
+					// changed the text and doesnt issue a refresh 
+		FTA(103,&ps[screenpeek],1); // Originally reserved for "screen saved". Now used dynamically. 
+		CONTROL_ClearAction(gamefunc_Auto_Aim);
+	}
+
+    if(multiflag == 1)
+    {
+        loc.bits =   1<<17;
+        loc.bits |=   multiwhat<<18;
+		loc.bits |=   multipos<<19;
+		multiflag = 0;
+        return;
+    }
+
+    loc.bits =   ACTION(gamefunc_Jump);
+    loc.bits |=   ACTION(gamefunc_Crouch)<<1;
+    loc.bits |=   ACTION(gamefunc_Fire)<<2;
+    loc.bits |=   ACTION(gamefunc_Aim_Up)<<3;
+    loc.bits |=   ACTION(gamefunc_Aim_Down)<<4;
+    loc.bits |=   ACTION(gamefunc_Run)<<5;
+#if 0  // this function won't be recorded anymore, but it's not really needed as it's *local*
+    loc.bits |=   ACTION(gamefunc_Look_Left)<<6;
+    loc.bits |=   ACTION(gamefunc_Look_Right)<<7;
+#endif
+	loc.bits |=   (ud.auto_aim==2)<<6; // 2 = normal, 1 = bullet only, 0 = disabled (not implemented)
+	loc.bits |=   ud.weaponautoswitch<<7;
+
+    j=0;
+    if (ACTION(gamefunc_Weapon_1))
+       j = 1;
+    if (ACTION(gamefunc_Weapon_2))
+       j = 2;
+    if (ACTION(gamefunc_Weapon_3))
+       j = 3;
+    if (ACTION(gamefunc_Weapon_4))
+       j = 4;
+    if (ACTION(gamefunc_Weapon_5))
+       j = 5;
+    if (ACTION(gamefunc_Weapon_6))
+       j = 6;
+
+    if (ACTION(gamefunc_Previous_Weapon))
+        j = 11;
+    if (ACTION(gamefunc_Next_Weapon))
+        j = 12;
+
+    if (!VOLUMEONE)
+	{
+		if (ACTION(gamefunc_Weapon_7))
+	        j = 7;
+	    if (ACTION(gamefunc_Weapon_8))
+	       j = 8;
+	    if (ACTION(gamefunc_Weapon_9))
+	       j = 9;
+	    if (ACTION(gamefunc_Weapon_10))
+	       j = 10;
+	}
+
+    loc.bits |=   j<<8;
+    loc.bits |=   ACTION(gamefunc_Steroids)<<12;
+    loc.bits |=   ACTION(gamefunc_Look_Up)<<13;
+    loc.bits |=   ACTION(gamefunc_Look_Down)<<14;
+    loc.bits |=   ACTION(gamefunc_NightVision)<<15;
+	if(ud.gitdat_mdk)
+	{
+		if(sprite[ps[myconnectindex].i].extra < max_player_health && ps[myconnectindex].firstaid_amount) // avoid medkit overloading controls
+			loc.bits |=   ACTION(gamefunc_MedKit)<<16; 
+	}
+	else
+	{
+		loc.bits |=   ACTION(gamefunc_MedKit)<<16; 
+	}
+    loc.bits |=   ACTION(gamefunc_Center_View)<<18;
+	loc.bits |=   ACTION(gamefunc_Holster_Weapon)<<19;
+    if(ACTION(gamefunc_Hide_Weapon))
+	{
+		ud.hideweapon = !ud.hideweapon;
+		vscrn(); // FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+		CONTROL_ClearAction(gamefunc_Hide_Weapon);
+	}
+	loc.bits |=   ACTION(gamefunc_Inventory_Left)<<20;
+    loc.bits |=   KB_KeyPressed(sc_Pause)<<21;
+    loc.bits |=   ACTION(gamefunc_Quick_Kick)<<22;
+    loc.bits |=   myaimmode<<23;
+    loc.bits |=   ACTION(gamefunc_Holo_Duke)<<24;
+    loc.bits |=   ACTION(gamefunc_Jetpack)<<25;
+    loc.bits |=   (((long)gamequit)<<26);
+    loc.bits |=   ACTION(gamefunc_Inventory_Right)<<27;
+    loc.bits |=   ACTION(gamefunc_TurnAround)<<28;
+    loc.bits |=   ACTION(gamefunc_Open)<<29;
+    loc.bits |=   ACTION(gamefunc_Inventory)<<30;
+    loc.bits |=   KB_KeyPressed(sc_Escape)<<31;
+
+    running = ACTION(gamefunc_Run)|ud.auto_run;
+    svel = vel = angvel = horiz = 0;
+
+    if( CONTROL_JoystickEnabled )
+	{
+        if ( running ) 
+		{
+			info.dz *= 2;
+		}
+	}
+
+    if( ACTION(gamefunc_Strafe) )
+	{
+		svel = -(info.dyaw+previousInfoDyawSvel)/8;
+	}
+    else
+	{ 
+		angvel = (info.dyaw+previousInfoDyaw)/64; 
+	}
+
+	previousInfoDyaw = (previousInfoDyaw+info.dyaw)%64; // % xduke: dont waste mouse tics
+	previousInfoDyawSvel = (previousInfoDyawSvel+info.dyaw)%8;
+
+	// svel -= info.dx;
+	svel = -info.dx>>6; // This helps the analog feel a bit.
+
+    vel = -info.dz>>6;
+
+	// Account for which mode we're in. (1, 2 or 7)
+	switch(ControllerType)
+	{
+		case controltype_keyboardandjoystick:
+
+		case controltype_joystickandmouse:
+
+			if(CONTROL_JoystickEnabled)
+			{
+				if(ud.mouseflip)
+				{
+					horiz = -(info.dpitch+previousInfoDpitch)/(314-128);
+				}
+				else
+				{
+					horiz = (info.dpitch+previousInfoDpitch)/(314-128);
+				}
+				horiz = (horiz>=0)?horiz+1:horiz; // xduke: fix assymetry (speed of 2 is like -1)
+				previousInfoDpitch = (previousInfoDpitch+info.dpitch)%(314-128);
+				info.dpitch = 0;
+			}
+			break;
+
+		default:
+			// If Mouse aim active
+			if( myaimmode )
+			{        
+				//
+				//
+				if(ud.mouseflip)
+				{
+					horiz = -(info.dpitch+previousInfoDpitch)/(314-128);
+				}
+				else
+				{
+					horiz = (info.dpitch+previousInfoDpitch)/(314-128);
+				}
+				horiz = (horiz>=0)?horiz+1:horiz; // xduke: fix assymetry (speed of 2 is like -1)
+				previousInfoDpitch = (previousInfoDpitch+info.dpitch)%(314-128);
+				info.dpitch = 0;         
+			}
+			break;
+	}
+
+
+
+    if (running)
+    {
+        turnamount = NORMALTURN<<1;
+        keymove = NORMALKEYMOVE<<1;
+    }
+    else
+    {
+        turnamount = NORMALTURN;
+        keymove = NORMALKEYMOVE;
+    }
+
+    if (ACTION(gamefunc_Strafe))
+    {
+        if ( ACTION(gamefunc_Turn_Left))
+           {
+           svel -= -keymove;
+           }
+        if ( ACTION(gamefunc_Turn_Right))
+           {
+           svel -= keymove;
+           }
+    }
+	else
+    {
+        if ( ACTION(gamefunc_Turn_Left))
+           {
+           turnheldtime += tics;
+           if (turnheldtime>=TURBOTURNTIME)
+              {
+              angvel -= turnamount;
+              }
+           else
+              {
+              angvel -= PREAMBLETURN;
+              }
+           }
+        else if ( ACTION(gamefunc_Turn_Right))
+           {
+           turnheldtime += tics;
+           if (turnheldtime>=TURBOTURNTIME)
+              {
+              angvel += turnamount;
+              }
+           else
+              {
+              angvel += PREAMBLETURN;
+              }
+           }
+        else
+           {
+           turnheldtime=0;
+           }
+    }
+
+    if ( ACTION( gamefunc_Strafe_Left ) )
+        svel += keymove;
+
+    if ( ACTION( gamefunc_Strafe_Right ) )
+        svel += -keymove;
+
+    if ( ACTION(gamefunc_Move_Forward) )
+        vel += keymove;
+
+    if ( ACTION(gamefunc_Move_Backward) )
+        vel += -keymove;
+
+    if(vel < -MAXVEL) vel = -MAXVEL;
+    if(vel > MAXVEL) vel = MAXVEL;
+    if(svel < -MAXSVEL) svel = -MAXSVEL;
+    if(svel > MAXSVEL) svel = MAXSVEL;
+    if(angvel < -MAXANGVEL) angvel = -MAXANGVEL;
+    if(angvel > MAXANGVEL) angvel = MAXANGVEL;
+    if(horiz < -MAXHORIZ) horiz = -MAXHORIZ;
+    if(horiz > MAXHORIZ) horiz = MAXHORIZ;
+
+    if(ud.scrollmode && ud.overhead_on)
+    {
+        ud.folfvel = vel;
+        ud.folavel = angvel;
+        loc.fvel = 0;
+        loc.svel = 0;
+        loc.avel = 0;
+        loc.horz = 0;
+        return;
+    }
+
+    if( numplayers > 1 )
+        daang = myang;
+    else daang = p->ang;
+
+    momx = mulscale9(vel,sintable[(daang+2560)&2047]);
+    momy = mulscale9(vel,sintable[(daang+2048)&2047]);
+
+    momx += mulscale9(svel,sintable[(daang+2048)&2047]);
+    momy += mulscale9(svel,sintable[(daang+1536)&2047]);
+
+    momx += fricxv;
+    momy += fricyv;
+	
+	loc.fvel = momx;
+	loc.svel = momy;
+    loc.avel = angvel;
+//	if(loc.avel)
+//		printf("getinput loc.avel=%d\n", loc.avel);
+    loc.horz = horiz;
+}
+
+
+char doincrements(struct player_struct *p)
+{
+    long /*j,*/i,snum;
+
+    snum = sprite[p->i].yvel;
+//    j = sync[snum].avel;
+//    p->weapon_ang = -(j/5);
+
+    p->player_par++;
+
+    if(p->invdisptime > 0)
+        p->invdisptime--;
+
+    if(p->tipincs > 0) p->tipincs--;
+
+    if(p->last_pissed_time > 0 )
+    {
+        p->last_pissed_time--;
+
+        if( p->last_pissed_time == (26*219) )
+        {
+            spritesound(FLUSH_TOILET,p->i);
+            if(snum == screenpeek || ud.coop == 1)
+                spritesound(DUKE_PISSRELIEF,p->i);
+        }
+
+        if( p->last_pissed_time == (26*218) )
+        {
+            p->holster_weapon = 0;
+            p->weapon_pos = 10;
+        }
+    }
+
+    if(p->crack_time > 0)
+    {
+        p->crack_time--;
+        if(p->crack_time == 0)
+        {
+            p->knuckle_incs = 1;
+            p->crack_time = 777;
+        }
+    }
+
+    if( p->steroids_amount > 0 && p->steroids_amount < 400)
+    {
+        p->steroids_amount--;
+        if(p->steroids_amount == 0)
+            checkavailinven(p);
+        if( !(p->steroids_amount&7) )
+            if(snum == screenpeek || ud.coop == 1)
+                spritesound(DUKE_HARTBEAT,p->i);
+    }
+
+    if(p->heat_on && p->heat_amount > 0)
+    {
+        p->heat_amount--;
+        if( p->heat_amount == 0 )
+        {
+            p->heat_on = 0;
+            checkavailinven(p);
+            spritesound(NITEVISION_ONOFF,p->i);
+            setpal(p);
+        }
+    }
+
+    if( p->holoduke_on >= 0 )
+    {
+        p->holoduke_amount--;
+        if(p->holoduke_amount <= 0)
+        {
+            spritesound(TELEPORTER,p->i);
+            p->holoduke_on = -1;
+            checkavailinven(p);
+        }
+    }
+
+    if( p->jetpack_on && p->jetpack_amount > 0 )
+    {
+        p->jetpack_amount--;
+        if(p->jetpack_amount <= 0)
+        {
+            p->jetpack_on = 0;
+            checkavailinven(p);
+            spritesound(DUKE_JETPACK_OFF,p->i);
+            stopsound(DUKE_JETPACK_IDLE);
+            stopsound(DUKE_JETPACK_ON);
+        }
+    }
+
+    if(p->quick_kick > 0 && sprite[p->i].pal != 1)
+    {
+        p->quick_kick--;
+        if( p->quick_kick == 8 )
+            shoot(p->i,KNEE);
+    }
+
+    if(p->access_incs && sprite[p->i].pal != 1)
+    {
+        p->access_incs++;
+        if(sprite[p->i].extra <= 0)
+            p->access_incs = 12;
+        if(p->access_incs == 12)
+        {
+            if(p->access_spritenum >= 0)
+            {
+                checkhitswitch(snum,p->access_spritenum,1);
+                switch(sprite[p->access_spritenum].pal)
+                {
+                    case 0:p->got_access &= (0xffff-0x1);break;
+                    case 21:p->got_access &= (0xffff-0x2);break;
+                    case 23:p->got_access &= (0xffff-0x4);break;
+                }
+                p->access_spritenum = -1;
+            }
+            else
+            {
+                checkhitswitch(snum,p->access_wallnum,0);
+                switch(wall[p->access_wallnum].pal)
+                {
+                    case 0:p->got_access &= (0xffff-0x1);break;
+                    case 21:p->got_access &= (0xffff-0x2);break;
+                    case 23:p->got_access &= (0xffff-0x4);break;
+                }
+            }
+        }
+
+        if(p->access_incs > 20)
+        {
+            p->access_incs = 0;
+            p->weapon_pos = 10;
+            p->kickback_pic = 0;
+        }
+    }
+
+    if(p->scuba_on == 0 && sector[p->cursectnum].lotag == 2)
+    {
+        if(p->scuba_amount > 0)
+        {
+            p->scuba_on = 1;
+            p->inven_icon = 6;
+            FTA(76,p,0);
+        }
+        else
+        {
+            if(p->airleft > 0)
+                p->airleft--;
+            else
+            {
+                p->extra_extra8 += 32;
+                if(p->last_extra < (max_player_health>>1) && (p->last_extra&3) == 0)
+                    spritesound(DUKE_LONGTERM_PAIN,p->i);
+            }
+        }
+    }
+    else if(p->scuba_amount > 0 && p->scuba_on)
+    {
+        p->scuba_amount--;
+        if(p->scuba_amount == 0)
+        {
+            p->scuba_on = 0;
+            checkavailinven(p);
+        }
+    }
+
+    if(p->knuckle_incs)
+    {
+        p->knuckle_incs ++;
+        if(p->knuckle_incs==10)
+        {
+            if(totalclock > 1024)
+                if(snum == screenpeek || ud.coop == 1)
+            {
+                if(rand()&1)
+                    spritesound(DUKE_CRACK,p->i);
+                else spritesound(DUKE_CRACK2,p->i);
+            }
+            spritesound(DUKE_CRACK_FIRST,p->i);
+        }
+        else if( p->knuckle_incs == 22 || (sync[snum].bits&(1<<2)))
+            p->knuckle_incs=0;
+
+        return 1;
+    }
+    return 0;
+}
+
+short weapon_sprites[MAX_WEAPONS] = { KNEE, FIRSTGUNSPRITE, SHOTGUNSPRITE,
+        CHAINGUNSPRITE, RPGSPRITE, HEAVYHBOMB, SHRINKERSPRITE, DEVISTATORSPRITE,
+        TRIPBOMBSPRITE, FREEZESPRITE, HEAVYHBOMB, SHRINKERSPRITE};
+
+void checkweapons(struct player_struct *p)
+{
+    short j,cw;
+
+    cw = p->curr_weapon;
+
+    if(cw < 1 || cw >= MAX_WEAPONS) return;
+
+    if(cw)
+    {
+        if(TRAND&1)
+            spawn(p->i,weapon_sprites[cw]);
+        else switch(cw)
+        {
+            case RPG_WEAPON:
+            case HANDBOMB_WEAPON:
+                spawn(p->i,EXPLOSION2);
+                break;
+        }
+    }
+}
+
+void processinput(short snum)
+{
+    long j, i, k, doubvel, fz, cz, hz, lz, truefdist, x, y;
+    char shrunk;
+    unsigned long sb_snum;
+    short psect, psectlotag,*kb, tempsect, pi;
+    struct player_struct *p;
+    spritetype *s;
+
+    p = &ps[snum];
+    pi = p->i;
+    s = &sprite[pi];
+
+    kb = &p->kickback_pic;
+
+    if(p->cheat_phase <= 0) sb_snum = sync[snum].bits;
+    else sb_snum = 0;
+
+    psect = p->cursectnum;
+    if(psect == -1)
+    {
+        if(s->extra > 0 && ud.clipping == 0)
+        {
+            quickkill(p);
+            spritesound(SQUISHED,pi);
+        }
+        psect = 0;
+    }
+
+    psectlotag = sector[psect].lotag;
+    p->spritebridge = 0;
+
+    shrunk = (s->yrepeat < 32);
+    getzrange(p->posx,p->posy,p->posz,psect,&cz,&hz,&fz,&lz,163L,CLIPMASK0);
+
+    j = getflorzofslope(psect,p->posx,p->posy);
+
+    p->truefz = j;
+    p->truecz = getceilzofslope(psect,p->posx,p->posy);
+
+    truefdist = klabs(p->posz-j);
+    if( (lz&49152) == 16384 && psectlotag == 1 && truefdist > PHEIGHT+(16<<8) )
+        psectlotag = 0;
+
+    hittype[pi].floorz = fz;
+    hittype[pi].ceilingz = cz;
+
+    p->ohoriz = p->horiz;
+    p->ohorizoff = p->horizoff;
+
+    if( p->aim_mode == 0 && p->on_ground && psectlotag != 2 && (sector[psect].floorstat&2) )
+    {
+          x = p->posx+(sintable[(p->ang+512)&2047]>>5);
+          y = p->posy+(sintable[p->ang&2047]>>5);
+          tempsect = psect;
+          updatesector(x,y,&tempsect);
+          if (tempsect >= 0)
+          {
+              k = getflorzofslope(psect,x,y);
+              if (psect == tempsect)
+                  p->horizoff += mulscale16(j-k,160);
+              else if (klabs(getflorzofslope(tempsect,x,y)-k) <= (4<<8))
+                  p->horizoff += mulscale16(j-k,160);
+          }
+     }
+     if (p->horizoff > 0) p->horizoff -= ((p->horizoff>>3)+1);
+     else if (p->horizoff < 0) p->horizoff += (((-p->horizoff)>>3)+1);
+
+    if( hz >= 0 && (hz&49152) == 49152)
+    {
+        hz &= (MAXSPRITES-1);
+
+        if(sprite[hz].statnum == 1 && sprite[hz].extra >= 0)
+        {
+            hz = 0;
+            cz = p->truecz;
+        }
+    }
+
+    if(lz >= 0 && (lz&49152) == 49152)
+    {
+        j = lz&(MAXSPRITES-1);
+
+        if( (sprite[j].cstat&33) == 33 )
+        {
+            psectlotag = 0;
+            p->footprintcount = 0;
+            p->spritebridge = 1;
+        }
+        else if(badguy(&sprite[j]) && sprite[j].xrepeat > 24 && klabs(s->z-sprite[j].z) < (84<<8) )
+        {
+            j = getangle(sprite[j].x-p->posx,sprite[j].y-p->posy);
+            p->posxv -= sintable[(j+512)&2047]<<4;
+            p->posyv -= sintable[j&2047]<<4;
+        }
+    }
+
+
+    if ( s->extra > 0 ) incur_damage( p );
+    else
+    {
+        s->extra = 0;
+        p->shield_amount = 0;
+    }
+
+    p->last_extra = s->extra;
+
+    if(p->loogcnt > 0) p->loogcnt--;
+    else p->loogcnt = 0;
+
+    if(p->fist_incs)
+    {
+        p->fist_incs++;
+        if(p->fist_incs == 28)
+        {
+            if(ud.recstat == 1) closedemowrite();
+            sound(PIPEBOMB_EXPLODE);
+            p->pals[0] = 64;
+            p->pals[1] = 64;
+            p->pals[2] = 64;
+            p->pals_time = 48;
+        }
+        if(p->fist_incs > 42)
+        {
+            if(p->buttonpalette && ud.from_bonus == 0)
+            {
+                ud.from_bonus = ud.level_number+1;
+                if(ud.secretlevel > 0 && ud.secretlevel < 12) ud.level_number = ud.secretlevel-1;
+                ud.m_level_number = ud.level_number;
+            }
+            else
+            {
+                if(ud.from_bonus)
+                {
+                    ud.level_number = ud.from_bonus;
+                    ud.m_level_number = ud.level_number;
+                    ud.from_bonus = 0;
+                }
+                else
+                {
+                    if(ud.level_number == ud.secretlevel && ud.from_bonus > 0 )
+                        ud.level_number = ud.from_bonus;
+                    else ud.level_number++;
+
+                    if(ud.level_number > 10) ud.level_number = 0;
+                    ud.m_level_number = ud.level_number;
+
+                }
+            }
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                ps[i].gm = MODE_EOL;
+            p->fist_incs = 0;
+
+            return;
+        }
+    }
+
+    if(p->timebeforeexit > 1 && p->last_extra > 0)
+    {
+        p->timebeforeexit--;
+        if(p->timebeforeexit == 26*5)
+        {
+            FX_StopAllSounds();
+            clearsoundlocks();
+            if(p->customexitsound >= 0)
+            {
+                sound(p->customexitsound);
+                FTA(102,p,0);
+            }
+        }
+        else if(p->timebeforeexit == 1)
+        {
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                ps[i].gm = MODE_EOL;
+            if(ud.from_bonus)
+            {
+                ud.level_number = ud.from_bonus;
+                ud.m_level_number = ud.level_number;
+                ud.from_bonus = 0;
+            }
+            else
+            {
+                ud.level_number++;
+                ud.m_level_number = ud.level_number;
+            }
+            return;
+        }
+    }
+/*
+    if(p->select_dir)
+    {
+        if(psectlotag != 15 || (sb_snum&(1<<31)) )
+            p->select_dir = 0;
+        else
+        {
+            if(sync[snum].fvel > 127)
+            {
+                p->select_dir = 0;
+                activatewarpelevators(pi,-1);
+            }
+            else if(sync[snum].fvel <= -127)
+            {
+                p->select_dir = 0;
+                activatewarpelevators(pi,1);
+            }
+            return;
+        }
+    }
+  */
+    if(p->pals_time > 0)
+        p->pals_time--;
+
+    if(p->fta > 0)
+    {
+        p->fta--;
+        if(p->fta == 0)
+        {
+            pub = NUMPAGES;
+            pus = NUMPAGES;
+            p->ftq = 0;
+        }
+    }
+
+    if( s->extra <= 0 )
+    {
+        if(p->dead_flag == 0)
+        {
+            if(s->pal != 1)
+            {
+                p->pals[0] = 63;
+                p->pals[1] = 0;
+                p->pals[2] = 0;
+                p->pals_time = 63;
+                p->posz -= (16<<8);
+                s->z -= (16<<8);
+            }
+
+            if(ud.recstat == 1 && ud.multimode < 2)
+                closedemowrite();
+
+            if(s->pal != 1)
+                p->dead_flag = (512-((TRAND&1)<<10)+(TRAND&255)-512)&2047;
+
+            p->jetpack_on = 0;
+            p->holoduke_on = -1;
+
+            stopsound(DUKE_JETPACK_IDLE);
+            if(p->scream_voice > FX_Ok)
+            {
+                FX_StopSound(p->scream_voice);
+                testcallback(DUKE_SCREAM);
+                p->scream_voice = FX_Ok;
+            }
+
+            if( s->pal != 1 && (s->cstat&32768) == 0) s->cstat = 0;
+
+            if( ud.multimode > 1 && ( s->pal != 1 || (s->cstat&32768) ) )
+            {
+                if(p->frag_ps != snum)
+                {
+                    ps[p->frag_ps].frag++;
+                    frags[p->frag_ps][snum]++;
+
+                    if( ud.user_name[p->frag_ps][0] != 0)
+                    {
+                        if(snum == screenpeek)
+                        {
+                            sprintf(&fta_quotes[115][0],"KILLED BY %s",&ud.user_name[p->frag_ps][0]);
+                            FTA(115,p,1);
+                        }
+                        else if(screenpeek == p->frag_ps)
+						// FIX_00076: Added default names for bots + fixed a "killed <name>" bug in Fakeplayers with AI
+                        {
+                            sprintf(&fta_quotes[116][0],"KILLED %s",&ud.user_name[snum][0]);
+                            FTA(116,&ps[p->frag_ps],1);
+                        }
+                    }
+                    else
+                    {
+                        if(snum == screenpeek)
+                        {
+                            sprintf(&fta_quotes[115][0],"KILLED BY PLAYER %ld",1+p->frag_ps);
+                            FTA(115,p,1);
+                        }
+                        else if(screenpeek == p->frag_ps)
+                        {
+                            sprintf(&fta_quotes[116][0],"KILLED PLAYER %ld",1+snum);
+                            FTA(116,&ps[p->frag_ps],1);
+                        }
+                    }
+                }
+                else p->fraggedself++;
+
+                if(myconnectindex == connecthead)
+                {
+                    sprintf(tempbuf,"frag %d killed %d\n",p->frag_ps+1,snum+1);
+                    sendscore(tempbuf);
+//                    printf(tempbuf);
+                }
+
+                p->frag_ps = snum;
+                pus = NUMPAGES;
+            }
+        }
+
+        if( psectlotag == 2 )
+        {
+            if(p->on_warping_sector == 0)
+            {
+                if( klabs(p->posz-fz) > (PHEIGHT>>1))
+                    p->posz += 348;
+            }
+            else
+            {
+                s->z -= 512;
+                s->zvel = -348;
+            }
+
+            clipmove(&p->posx,&p->posy,
+                &p->posz,&p->cursectnum,
+                0,0,164L,(4L<<8),(4L<<8),CLIPMASK0);
+//            p->bobcounter += 32;
+        }
+
+		p->oposx = p->posx;
+		p->oposy = p->posy;
+		p->oposz = p->posz;
+		p->oang = p->ang;
+		p->opyoff = p->pyoff;
+
+        p->horiz = 100;
+        p->horizoff = 0;
+
+        updatesector(p->posx,p->posy,&p->cursectnum);
+
+        pushmove(&p->posx,&p->posy,&p->posz,&p->cursectnum,128L,(4L<<8),(20L<<8),CLIPMASK0);
+
+        if( fz > cz+(16<<8) && s->pal != 1)
+            p->rotscrnang = (p->dead_flag + ( (fz+p->posz)>>7))&2047;
+
+        p->on_warping_sector = 0;
+
+        return;
+    }
+
+    if(p->transporter_hold > 0)
+    {
+        p->transporter_hold--;
+        if(p->transporter_hold == 0 && p->on_warping_sector)
+            p->transporter_hold = 2;
+    }
+    if(p->transporter_hold < 0)
+        p->transporter_hold++;
+
+    if(p->newowner >= 0)
+    {
+        i = p->newowner;
+		p->posx = SX;
+        p->posy = SY;
+        p->posz = SZ;
+        p->ang =  SA;
+        p->posxv = p->posyv = s->xvel = 0;
+        p->look_ang = 0;
+        p->rotscrnang = 0;
+        doincrements(p);
+
+        if(p->curr_weapon == HANDREMOTE_WEAPON) goto SHOOTINCODE;
+
+        return;
+    }
+
+	p->weaponautoswitch = (sb_snum&(1<<7))?1:0;
+	p->auto_aim = (sb_snum&(1<<6))?2:1; // 2 == normal == full; 1 == partial; 0 = none (not implemented)
+
+	doubvel = TICSPERFRAME;
+
+    if (p->rotscrnang > 0) p->rotscrnang -= ((p->rotscrnang>>1)+1);
+    else if (p->rotscrnang < 0) p->rotscrnang += (((-p->rotscrnang)>>1)+1);
+
+    p->look_ang -= (p->look_ang>>2);
+
+	// 1<<6: toggle ud.auto_aim
+	if(	(ud.playing_demo_rev == BYTEVERSION_27 ||
+		ud.playing_demo_rev == BYTEVERSION_28 || 
+		ud.playing_demo_rev == BYTEVERSION_116 || 
+		ud.playing_demo_rev == BYTEVERSION_117) &&
+		sb_snum&(1<<6) || 
+		ACTION(gamefunc_Look_Left) && (p->gm&MODE_GAME) && 
+		!(p->gm&MODE_MENU) && !(p->gm&MODE_TYPE) && !(ud.pause_on) && (ud.recstat != 2))
+	{
+		p->look_ang -= 152;
+		p->rotscrnang += 24;
+	}
+
+	// 1<<7 : ANTIWEAPONSWITCH
+	if(	(ud.playing_demo_rev == BYTEVERSION_27 ||
+		ud.playing_demo_rev == BYTEVERSION_28 || 
+		ud.playing_demo_rev == BYTEVERSION_116 || 
+		ud.playing_demo_rev == BYTEVERSION_117) &&
+		sb_snum&(1<<7) || 
+		ACTION(gamefunc_Look_Right) && (p->gm&MODE_GAME) && 
+		!(p->gm&MODE_MENU) && !(p->gm&MODE_TYPE) && !(ud.pause_on) && (ud.recstat != 2))
+	{
+		p->look_ang += 152;
+		p->rotscrnang -= 24;
+	}
+
+	if(p->on_crane >= 0)
+        goto HORIZONLY;
+
+    j = ksgn(sync[snum].avel);
+    /*
+    if( j && ud.screen_tilting == 2)
+    {
+        k = 4;
+        if(sb_snum&(1<<5)) k <<= 2;
+        p->rotscrnang -= k*j;
+        p->look_ang += k*j;
+    }
+    */
+
+    if( s->xvel < 32 || p->on_ground == 0 || p->bobcounter == 1024 )
+    {
+        if( (p->weapon_sway&2047) > (1024+96) )
+            p->weapon_sway -= 96;
+        else if( (p->weapon_sway&2047) < (1024-96) )
+            p->weapon_sway += 96;
+        else p->weapon_sway = 1024;
+    }
+    else p->weapon_sway = p->bobcounter;
+
+    s->xvel =
+        ksqrt( (p->posx-p->bobposx)*(p->posx-p->bobposx)+(p->posy-p->bobposy)*(p->posy-p->bobposy));
+    if(p->on_ground) p->bobcounter += sprite[p->i].xvel>>1;
+
+    if( ud.clipping == 0 && ( sector[p->cursectnum].floorpicnum == MIRROR || p->cursectnum < 0 || p->cursectnum >= MAXSECTORS) )
+    {
+        p->posx = p->oposx;
+        p->posy = p->oposy;
+    }
+    else
+    {
+        p->oposx = p->posx;
+        p->oposy = p->posy;
+    }
+
+    p->bobposx = p->posx;
+    p->bobposy = p->posy;
+
+    p->oposz = p->posz;
+    p->opyoff = p->pyoff;
+    p->oang = p->ang;
+
+    if(p->one_eighty_count < 0)
+    {
+        p->one_eighty_count += 128;
+        p->ang += 128;
+    }
+
+    // Shrinking code
+
+    i = 40;
+
+    if( psectlotag == 2)
+    {
+        p->jumping_counter = 0;
+
+        p->pycount += 32;
+        p->pycount &= 2047;
+        p->pyoff = sintable[p->pycount]>>7;
+
+        if( Sound[DUKE_UNDERWATER].num == 0 )
+            spritesound(DUKE_UNDERWATER,pi);
+
+        if ( sb_snum&1 )
+        {
+            if(p->poszv > 0) p->poszv = 0;
+            p->poszv -= 348;
+            if(p->poszv < -(256*6)) p->poszv = -(256*6);
+        }
+        else if (sb_snum&(1<<1))
+        {
+            if(p->poszv < 0) p->poszv = 0;
+            p->poszv += 348;
+            if(p->poszv > (256*6)) p->poszv = (256*6);
+        }
+        else
+        {
+            if(p->poszv < 0)
+            {
+                p->poszv += 256;
+                if(p->poszv > 0)
+                    p->poszv = 0;
+            }
+            if(p->poszv > 0)
+            {
+                p->poszv -= 256;
+                if(p->poszv < 0)
+                    p->poszv = 0;
+            }
+        }
+
+        if(p->poszv > 2048)
+            p->poszv >>= 1;
+
+        p->posz += p->poszv;
+
+        if(p->posz > (fz-(15<<8)) )
+            p->posz += ((fz-(15<<8))-p->posz)>>1;
+
+        if(p->posz < (cz+(4<<8)) )
+        {
+            p->posz = cz+(4<<8);
+            p->poszv = 0;
+        }
+
+        if( p->scuba_on && (TRAND&255) < 8 )
+        {
+            j = spawn(pi,WATERBUBBLE);
+            sprite[j].x +=
+                sintable[(p->ang+512+64-(global_random&128))&2047]>>6;
+            sprite[j].y +=
+                sintable[(p->ang+64-(global_random&128))&2047]>>6;
+            sprite[j].xrepeat = 3;
+            sprite[j].yrepeat = 2;
+            sprite[j].z = p->posz+(8<<8);
+        }
+    }
+
+    else if(p->jetpack_on)
+    {
+        p->on_ground = 0;
+        p->jumping_counter = 0;
+        p->hard_landing = 0;
+        p->falling_counter = 0;
+
+        p->pycount += 32;
+        p->pycount &= 2047;
+        p->pyoff = sintable[p->pycount]>>7;
+
+        if(p->jetpack_on < 11)
+        {
+            p->jetpack_on++;
+            p->posz -= (p->jetpack_on<<7); //Goin up
+        }
+        else if(p->jetpack_on == 11 && Sound[DUKE_JETPACK_IDLE].num < 1)
+            spritesound(DUKE_JETPACK_IDLE,pi);
+
+        if(shrunk) j = 512;
+        else j = 2048;
+
+        if ( sb_snum&1 )                            //A (soar high)
+        {
+            p->posz -= j;
+            p->crack_time = 777;
+        }
+
+        if (sb_snum&(1<<1))                            //Z (soar low)
+        {
+            p->posz += j;
+            p->crack_time = 777;
+        }
+
+        if( shrunk == 0 && (psectlotag == 0 || psectlotag == 2)) k = 32;
+        else k = 16;
+
+        if( psectlotag != 2 && p->scuba_on == 1 )
+            p->scuba_on = 0;
+
+        if(p->posz > (fz-(k<<8)) )
+            p->posz += ((fz-(k<<8))-p->posz)>>1;
+        if(p->posz < (hittype[pi].ceilingz+(18<<8)) )
+            p->posz = hittype[pi].ceilingz+(18<<8);
+
+    }
+    else if( psectlotag != 2 )
+    {
+        if(p->airleft != 15*26)
+            p->airleft = 15*26; //Aprox twenty seconds.
+
+        if(p->scuba_on == 1)
+            p->scuba_on = 0;
+
+        if( psectlotag == 1 && p->spritebridge == 0)
+        {
+            if(shrunk == 0)
+            {
+                i = 34;
+                p->pycount += 32;
+                p->pycount &= 2047;
+                p->pyoff = sintable[p->pycount]>>6;
+            }
+            else i = 12;
+
+            if(shrunk == 0 && truefdist <= PHEIGHT)
+            {
+                if(p->on_ground == 1)
+                {
+                    if( p->dummyplayersprite == -1 )
+                        p->dummyplayersprite =
+                            spawn(pi,PLAYERONWATER);
+
+                    p->footprintcount = 6;
+                    if(sector[p->cursectnum].floorpicnum == FLOORSLIME)
+                        p->footprintpal = 8;
+                    else p->footprintpal = 0;
+                    p->footprintshade = 0;
+                }
+            }
+        }
+        else
+        {
+            if(p->footprintcount > 0 && p->on_ground)
+                if( (sector[p->cursectnum].floorstat&2) != 2 )
+            {
+                for(j=headspritesect[psect];j>=0;j=nextspritesect[j])
+                    if( sprite[j].picnum == FOOTPRINTS || sprite[j].picnum == FOOTPRINTS2 || sprite[j].picnum == FOOTPRINTS3 || sprite[j].picnum == FOOTPRINTS4 )
+                        if (klabs(sprite[j].x-p->posx) < 384)
+                            if (klabs(sprite[j].y-p->posy) < 384)
+                                break;
+                if(j < 0)
+                {
+                    p->footprintcount--;
+                    if( sector[p->cursectnum].lotag == 0 && sector[p->cursectnum].hitag == 0 )
+                    {
+                        switch(TRAND&3)
+                        {
+                            case 0:  j = spawn(pi,FOOTPRINTS); break;
+                            case 1:  j = spawn(pi,FOOTPRINTS2); break;
+                            case 2:  j = spawn(pi,FOOTPRINTS3); break;
+                            default: j = spawn(pi,FOOTPRINTS4); break;
+                        }
+                        sprite[j].pal = p->footprintpal;
+                        sprite[j].shade = p->footprintshade;
+                    }
+                }
+            }
+        }
+
+        if(p->posz < (fz-(i<<8)) ) //falling
+        {
+            if( (sb_snum&3) == 0 && p->on_ground && (sector[psect].floorstat&2) && p->posz >= (fz-(i<<8)-(16<<8) ) )
+                p->posz = fz-(i<<8);
+            else
+            {
+                p->on_ground = 0;
+                p->poszv += (gc+80); // (TICSPERFRAME<<6);
+                if(p->poszv >= (4096+2048)) p->poszv = (4096+2048);
+                if(p->poszv > 2400 && p->falling_counter < 255)
+                {
+                    p->falling_counter++;
+                    if( p->falling_counter == 38 )
+                        p->scream_voice = spritesound(DUKE_SCREAM,pi);
+                }
+
+                if( (p->posz+p->poszv) >= (fz-(i<<8)) ) // hit the ground
+                    if(sector[p->cursectnum].lotag != 1)
+                    {
+                        if( p->falling_counter > 62 ) quickkill(p);
+
+                        else if( p->falling_counter > 9 )
+                        {
+                            j = p->falling_counter;
+                            s->extra -= j-(TRAND&3);
+                            if(s->extra <= 0)
+                            {
+                                spritesound(SQUISHED,pi);
+                                p->pals[0] = 63;
+                                p->pals[1] = 0;
+                                p->pals[2] = 0;
+                                p->pals_time = 63;
+                            }
+                            else
+                            {
+                                spritesound(DUKE_LAND,pi);
+                                spritesound(DUKE_LAND_HURT,pi);
+                            }
+
+                            p->pals[0] = 16;
+                            p->pals[1] = 0;
+                            p->pals[2] = 0;
+                            p->pals_time = 32;
+                        }
+                        else if(p->poszv > 2048) spritesound(DUKE_LAND,pi);
+                    }
+            }
+        }
+
+        else
+        {
+            p->falling_counter = 0;
+            if(p->scream_voice > FX_Ok)
+            {
+                FX_StopSound(p->scream_voice);
+                p->scream_voice = FX_Ok;
+            }
+
+            if(psectlotag != 1 && psectlotag != 2 && p->on_ground == 0 && p->poszv > (6144>>1))
+                p->hard_landing = p->poszv>>10;
+
+            p->on_ground = 1;
+
+            if( i==40 )
+            {
+                //Smooth on the ground
+
+                k = ((fz-(i<<8))-p->posz)>>1;
+                if( klabs(k) < 256 ) k = 0;
+                p->posz += k;
+                p->poszv -= 768;
+                if(p->poszv < 0) p->poszv = 0;
+            }
+            else if(p->jumping_counter == 0)
+            {
+                p->posz += ((fz-(i<<7))-p->posz)>>1; //Smooth on the water
+                if(p->on_warping_sector == 0 && p->posz > fz-(16<<8))
+                {
+                    p->posz = fz-(16<<8);
+                    p->poszv >>= 1;
+                }
+            }
+
+            p->on_warping_sector = 0;
+
+            if( (sb_snum&2) )
+            {
+                p->posz += (2048+768);
+                p->crack_time = 777;
+            }
+
+            if( (sb_snum&1) == 0 && p->jumping_toggle == 1)
+                p->jumping_toggle = 0;
+
+            else if( (sb_snum&1) && p->jumping_toggle == 0 )
+            {
+                if( p->jumping_counter == 0 )
+                    if( (fz-cz) > (56<<8) )
+                    {
+                        p->jumping_counter = 1;
+                        p->jumping_toggle = 1;
+                    }
+            }
+
+            if( p->jumping_counter && (sb_snum&1) == 0 )
+                p->jumping_toggle = 0;
+        }
+
+        if(p->jumping_counter)
+        {
+            if( (sb_snum&1) == 0 && p->jumping_toggle == 1)
+                p->jumping_toggle = 0;
+
+            if( p->jumping_counter < (1024+256) )
+            {
+                if(psectlotag == 1 && p->jumping_counter > 768)
+                {
+                    p->jumping_counter = 0;
+                    p->poszv = -512;
+                }
+                else
+                {
+                    p->poszv -= (sintable[(2048-128+p->jumping_counter)&2047])/12;
+                    p->jumping_counter += 180;
+                    p->on_ground = 0;
+                }
+            }
+            else
+            {
+                p->jumping_counter = 0;
+                p->poszv = 0;
+            }
+        }
+
+        p->posz += p->poszv;
+
+        if(p->posz < (cz+(4<<8)))
+        {
+            p->jumping_counter = 0;
+            if(p->poszv < 0)
+                p->posxv = p->posyv = 0;
+            p->poszv = 128;
+            p->posz = cz+(4<<8);
+        }
+    }
+
+    //Do the quick lefts and rights
+
+    if ( p->fist_incs ||
+         p->transporter_hold > 2 ||
+         p->hard_landing ||
+         p->access_incs > 0 ||
+         p->knee_incs > 0 ||
+         (p->curr_weapon == TRIPBOMB_WEAPON &&
+          *kb > 1 &&
+          *kb < 4 ) )
+    {
+        doubvel = 0;
+        p->posxv = 0;
+        p->posyv = 0;
+    }
+    else if ( sync[snum].avel )          //p->ang += syncangvel * constant
+    {                         //ENGINE calculates angvel for you
+        long tempang;
+
+        tempang = sync[snum].avel<<1;
+
+        if( psectlotag == 2 ) p->angvel =(tempang-(tempang>>3))*ksgn(doubvel);
+        else p->angvel = tempang*ksgn(doubvel);
+
+        p->ang += p->angvel;
+        p->ang &= 2047;
+        p->crack_time = 777;
+		}
+
+    if(p->spritebridge == 0)
+    {
+        j = sector[s->sectnum].floorpicnum;
+
+        if( j == PURPLELAVA || sector[s->sectnum].ceilingpicnum == PURPLELAVA )
+        {
+            if(p->boot_amount > 0)
+            {
+                p->boot_amount--;
+                p->inven_icon = 7;
+                if(p->boot_amount <= 0)
+                    checkavailinven(p);
+            }
+            else
+            {
+                if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                    spritesound(DUKE_LONGTERM_PAIN,pi);
+                p->pals[0] = 0; p->pals[1] = 8; p->pals[2] = 0;
+                p->pals_time = 32;
+                s->extra--;
+            }
+        }
+
+        k = 0;
+
+        if(p->on_ground && truefdist <= PHEIGHT+(16<<8))
+        {
+            switch(j)
+            {
+                case HURTRAIL:
+                    if( rnd(32) )
+                    {
+                        if(p->boot_amount > 0)
+                            k = 1;
+                        else
+                        {
+                            if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                spritesound(DUKE_LONGTERM_PAIN,pi);
+                            p->pals[0] = 64; p->pals[1] = 64; p->pals[2] = 64;
+                            p->pals_time = 32;
+                            s->extra -= 1+(TRAND&3);
+                            if(Sound[SHORT_CIRCUIT].num < 1)
+                                spritesound(SHORT_CIRCUIT,pi);
+                        }
+                    }
+                    break;
+                case FLOORSLIME:
+                    if( rnd(16) )
+                    {
+                        if(p->boot_amount > 0)
+                            k = 1;
+                        else
+                        {
+                            if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                spritesound(DUKE_LONGTERM_PAIN,pi);
+                            p->pals[0] = 0; p->pals[1] = 8; p->pals[2] = 0;
+                            p->pals_time = 32;
+                            s->extra -= 1+(TRAND&3);
+                        }
+                    }
+                    break;
+                case FLOORPLASMA:
+                    if( rnd(32) )
+                    {
+                        if( p->boot_amount > 0 )
+                            k = 1;
+                        else
+                        {
+                            if(Sound[DUKE_LONGTERM_PAIN].num < 1)
+                                spritesound(DUKE_LONGTERM_PAIN,pi);
+                            p->pals[0] = 8; p->pals[1] = 0; p->pals[2] = 0;
+                            p->pals_time = 32;
+                            s->extra -= 1+(TRAND&3);
+                        }
+                    }
+                    break;
+            }
+        }
+
+        if( k )
+        {
+            FTA(75,p,0);
+            p->boot_amount -= 2;
+            if(p->boot_amount <= 0)
+                checkavailinven(p);
+        }
+    }
+
+    if ( p->posxv || p->posyv || sync[snum].fvel || sync[snum].svel )
+    {
+        p->crack_time = 777;
+
+        k = sintable[p->bobcounter&2047]>>12;
+
+        if(truefdist < PHEIGHT+(8<<8) )
+            if( k == 1 || k == 3 )
+        {
+            if(p->spritebridge == 0 && p->walking_snd_toggle == 0 && p->on_ground)
+            {
+                switch( psectlotag )
+                {
+                    case 0:
+
+                        if(lz >= 0 && (lz&(MAXSPRITES-1))==49152 )
+                            j = sprite[lz&(MAXSPRITES-1)].picnum;
+                        else j = sector[psect].floorpicnum;
+
+                        switch(j)
+                        {
+                            case PANNEL1:
+                            case PANNEL2:
+                                spritesound(DUKE_WALKINDUCTS,pi);
+                                p->walking_snd_toggle = 1;
+                                break;
+                        }
+                        break;
+                    case 1:
+                        if((TRAND&1) == 0)
+                            spritesound(DUKE_ONWATER,pi);
+                        p->walking_snd_toggle = 1;
+                        break;
+                }
+            }
+        }
+        else if(p->walking_snd_toggle > 0)
+            p->walking_snd_toggle --;
+
+        if(p->jetpack_on == 0 && p->steroids_amount > 0 && p->steroids_amount < 400)
+            doubvel <<= 1;
+
+        p->posxv += ((sync[snum].fvel*doubvel)<<6);
+        p->posyv += ((sync[snum].svel*doubvel)<<6);
+
+        if( ( p->curr_weapon == KNEE_WEAPON && *kb > 10 && p->on_ground ) || ( p->on_ground && (sb_snum&2) ) )
+        {
+            p->posxv = mulscale(p->posxv,dukefriction-0x2000,16);
+            p->posyv = mulscale(p->posyv,dukefriction-0x2000,16);
+        }
+        else
+        {
+            if(psectlotag == 2)
+            {
+                p->posxv = mulscale(p->posxv,dukefriction-0x1400,16);
+                p->posyv = mulscale(p->posyv,dukefriction-0x1400,16);
+            }
+            else
+            {
+                p->posxv = mulscale(p->posxv,dukefriction,16);
+                p->posyv = mulscale(p->posyv,dukefriction,16);
+            }
+        }
+
+        if( abs(p->posxv) < 2048 && abs(p->posyv) < 2048 )
+            p->posxv = p->posyv = 0;
+
+        if( shrunk )
+        {
+            p->posxv =
+                mulscale16(p->posxv,dukefriction-(dukefriction>>1)+(dukefriction>>2));
+            p->posyv =
+                mulscale16(p->posyv,dukefriction-(dukefriction>>1)+(dukefriction>>2));
+        }
+    }
+
+    HORIZONLY:
+
+        if(psectlotag == 1 || p->spritebridge == 1) i = (4L<<8);
+        else i = (20L<<8);
+
+        if(sector[p->cursectnum].lotag == 2) k = 0;
+        else k = 1;
+
+        if(ud.clipping)
+        {
+            j = 0;
+            p->posx += p->posxv>>14;
+            p->posy += p->posyv>>14;
+            updatesector(p->posx,p->posy,&p->cursectnum);
+            changespritesect(pi,p->cursectnum);
+        }
+        else
+            j = clipmove(&p->posx,&p->posy,
+                &p->posz,&p->cursectnum,
+                p->posxv,p->posyv,164L,(4L<<8),i,CLIPMASK0);
+
+        if(p->jetpack_on == 0 && psectlotag != 2 && psectlotag != 1 && shrunk)
+            p->posz += 32<<8;
+
+        if(j)
+            checkplayerhurt(p,j);
+
+        if(p->jetpack_on == 0)
+        {
+            if( s->xvel > 16 )
+            {
+                if( psectlotag != 1 && psectlotag != 2 && p->on_ground )
+                {
+                    p->pycount += 52;
+                    p->pycount &= 2047;
+                    p->pyoff =
+                        klabs(s->xvel*sintable[p->pycount])/1596;
+                }
+            }
+            else if( psectlotag != 2 && psectlotag != 1 )
+                p->pyoff = 0;
+        }
+
+        // RBG***
+        setsprite(pi,p->posx,p->posy,p->posz+PHEIGHT);
+
+        if( psectlotag < 3 )
+        {
+            psect = s->sectnum;
+            if( ud.clipping == 0 && sector[psect].lotag == 31)
+            {
+                if( sprite[sector[psect].hitag].xvel && hittype[sector[psect].hitag].temp_data[0] == 0)
+                {
+                    quickkill(p);
+                    return;
+                }
+            }
+        }
+
+        if(truefdist < PHEIGHT && p->on_ground && psectlotag != 1 && shrunk == 0 && sector[p->cursectnum].lotag == 1)
+            if( Sound[DUKE_ONWATER].num == 0 )
+                spritesound(DUKE_ONWATER,pi);
+
+        if (p->cursectnum != s->sectnum)
+            changespritesect(pi,p->cursectnum);
+
+        if(ud.clipping == 0)
+            j = ( pushmove(&p->posx,&p->posy,&p->posz,&p->cursectnum,164L,(4L<<8),(4L<<8),CLIPMASK0) < 0 && furthestangle(pi,8) < 512 );
+        else j = 0;
+
+        if(ud.clipping == 0)
+        {
+            if( klabs(hittype[pi].floorz-hittype[pi].ceilingz) < (48<<8) || j )
+            {
+                if ( !(sector[s->sectnum].lotag&0x8000) && ( isanunderoperator(sector[s->sectnum].lotag) ||
+                    isanearoperator(sector[s->sectnum].lotag) ) )
+                        activatebysector(s->sectnum,pi);
+                if(j)
+                {
+                    quickkill(p);
+                    return;
+                }
+            }
+            else if( klabs(fz-cz) < (32<<8) && isanunderoperator(sector[psect].lotag) )
+                activatebysector(psect,pi);
+        }
+
+        if( sb_snum&(1<<18) || p->hard_landing)
+            p->return_to_center = 9;
+
+        if( sb_snum&(1<<13) )
+        {
+            p->return_to_center = 9;
+            if( sb_snum&(1<<5) ) p->horiz += 12;
+            p->horiz += 12;
+        }
+
+        else if( sb_snum&(1<<14) )
+        {
+            p->return_to_center = 9;
+            if( sb_snum&(1<<5) ) p->horiz -= 12;
+            p->horiz -= 12;
+        }
+
+        else if( sb_snum&(1<<3) )
+        {
+            if( sb_snum&(1<<5) ) p->horiz += 6;
+            p->horiz += 6;
+        }
+
+        else if( sb_snum&(1<<4) )
+        {
+            if( sb_snum&(1<<5) ) p->horiz -= 6;
+            p->horiz -= 6;
+        }
+        if(p->return_to_center > 0)
+            if( (sb_snum&(1<<13)) == 0 && (sb_snum&(1<<14)) == 0 )
+        {
+            p->return_to_center--;
+            p->horiz += 33-(p->horiz/3);
+        }
+
+        if(p->hard_landing > 0)
+        {
+            p->hard_landing--;
+            p->horiz -= (p->hard_landing<<4);
+        }
+
+        if(p->aim_mode)
+            p->horiz += sync[snum].horz>>1;
+        else
+        {
+             if( p->horiz > 95 && p->horiz < 105) p->horiz = 100;
+             if( p->horizoff > -5 && p->horizoff < 5) p->horizoff = 0;
+        }
+
+        if(p->horiz > 299) p->horiz = 299;
+        else if(p->horiz < -99) p->horiz = -99;
+
+    //Shooting code/changes
+
+    if( p->show_empty_weapon > 0)
+    {
+        p->show_empty_weapon--;
+        if(p->show_empty_weapon == 0)
+        {
+            if(p->last_full_weapon == GROW_WEAPON)
+                p->subweapon |= (1<<GROW_WEAPON);
+            else if(p->last_full_weapon == SHRINKER_WEAPON)
+                p->subweapon &= ~(1<<GROW_WEAPON);
+            addweapon( p, p->last_full_weapon );
+            return;
+        }
+    }
+
+    if(p->knee_incs > 0)
+    {
+        p->knee_incs++;
+        p->horiz -= 48;
+        p->return_to_center = 9;
+        if(p->knee_incs > 15)
+        {
+            p->knee_incs = 0;
+            p->holster_weapon = 0;
+            if(p->weapon_pos < 0)
+                p->weapon_pos = -p->weapon_pos;
+            if(p->actorsqu >= 0 && dist(&sprite[pi],&sprite[p->actorsqu]) < 1400 )
+            {
+                guts(&sprite[p->actorsqu],JIBS6,7,myconnectindex);
+                spawn(p->actorsqu,BLOODPOOL);
+                spritesound(SQUISHED,p->actorsqu);
+                switch(sprite[p->actorsqu].picnum)
+                {
+                    case FEM1:
+                    case FEM2:
+                    case FEM3:
+                    case FEM4:
+                    case FEM5:
+                    case FEM6:
+                    case FEM7:
+                    case FEM8:
+                    case FEM9:
+                    case FEM10:
+                    case PODFEM1:
+                    case NAKED1:
+                    case STATUE:
+                        if(sprite[p->actorsqu].yvel)
+                            operaterespawns(sprite[p->actorsqu].yvel);
+                        break;
+                }
+
+                if(sprite[p->actorsqu].picnum == APLAYER)
+                {
+                    quickkill(&ps[sprite[p->actorsqu].yvel]);
+                    ps[sprite[p->actorsqu].yvel].frag_ps = snum;
+                }
+                else if(badguy(&sprite[p->actorsqu]))
+                {
+                    deletesprite(p->actorsqu);
+                    p->actors_killed++;
+                }
+                else deletesprite(p->actorsqu);
+            }
+            p->actorsqu = -1;
+        }
+        else if(p->actorsqu >= 0)
+            p->ang += getincangle(p->ang,getangle(sprite[p->actorsqu].x-p->posx,sprite[p->actorsqu].y-p->posy))>>2;
+    }
+
+    if( doincrements(p) ) return;
+
+    if(p->weapon_pos != 0)
+    {
+        if(p->weapon_pos == -9)
+        {
+            if(p->last_weapon >= 0)
+            {
+                p->weapon_pos = 10;
+//                if(p->curr_weapon == KNEE_WEAPON) *kb = 1;
+                p->last_weapon = -1;
+            }
+            else if(p->holster_weapon == 0)
+                p->weapon_pos = 10;
+        }
+        else p->weapon_pos--;
+    }
+
+    // HACKS
+
+    SHOOTINCODE:
+
+    if( p->curr_weapon == SHRINKER_WEAPON || p->curr_weapon == GROW_WEAPON )
+        p->random_club_frame += 64; // Glowing
+
+    if(p->rapid_fire_hold == 1)
+    {
+        if( sb_snum&(1<<2) ) return;
+        p->rapid_fire_hold = 0;
+    }
+
+    if(shrunk || p->tipincs || p->access_incs)
+        sb_snum &= ~(1<<2);
+    else if ( shrunk == 0 && (sb_snum&(1<<2)) && (*kb) == 0 && p->fist_incs == 0 &&
+         p->last_weapon == -1 && ( p->weapon_pos == 0 || p->holster_weapon == 1 ) )
+    {
+
+        p->crack_time = 777;
+
+        if(p->holster_weapon == 1)
+        {
+            if( p->last_pissed_time <= (26*218) && p->weapon_pos == -9)
+            {
+                p->holster_weapon = 0;
+                p->weapon_pos = 10;
+                FTA(74,p,1);
+            }
+        }
+        else switch(p->curr_weapon)
+        {
+            case HANDBOMB_WEAPON:
+                p->hbomb_hold_delay = 0;
+                if( p->ammo_amount[HANDBOMB_WEAPON] > 0 )
+                    (*kb)=1;
+                break;
+            case HANDREMOTE_WEAPON:
+                p->hbomb_hold_delay = 0;
+                (*kb) = 1;
+                break;
+
+            case PISTOL_WEAPON:
+                if( p->ammo_amount[PISTOL_WEAPON] > 0 )
+                {
+                    p->ammo_amount[PISTOL_WEAPON]--;
+                    (*kb) = 1;
+                }
+                break;
+
+
+            case CHAINGUN_WEAPON:
+                if( p->ammo_amount[CHAINGUN_WEAPON] > 0 ) // && p->random_club_frame == 0)
+                    (*kb)=1;
+                break;
+
+            case SHOTGUN_WEAPON:
+                if( p->ammo_amount[SHOTGUN_WEAPON] > 0 && p->random_club_frame == 0 )
+                    (*kb)=1;
+                break;
+
+            case TRIPBOMB_WEAPON:
+				if (VOLUMEONE) break;
+                if ( p->ammo_amount[TRIPBOMB_WEAPON] > 0 )
+                {
+                    long sx,sy,sz;
+                    short sect,hw,hitsp;
+
+                    hitscan( p->posx, p->posy, p->posz,
+                             p->cursectnum, sintable[(p->ang+512)&2047],
+                             sintable[p->ang&2047], (100-p->horiz-p->horizoff)*32,
+                             &sect, &hw, &hitsp, &sx, &sy, &sz,CLIPMASK1);
+
+                    if(sect < 0 || hitsp >= 0)
+                        break;
+
+                    if( hw >= 0 && sector[sect].lotag > 2 )
+                        break;
+
+                    if(hw >= 0 && wall[hw].overpicnum >= 0)
+                        if(wall[hw].overpicnum == BIGFORCE)
+                            break;
+
+                    j = headspritesect[sect];
+                    while(j >= 0)
+                    {
+                        if( sprite[j].picnum == TRIPBOMB &&
+                            klabs(sprite[j].z-sz) < (12<<8) && ((sprite[j].x-sx)*(sprite[j].x-sx)+(sprite[j].y-sy)*(sprite[j].y-sy)) < (290*290) )
+                                    break;
+                        j = nextspritesect[j];
+                    }
+
+                    if(j == -1 && hw >= 0 && (wall[hw].cstat&16) == 0 )
+                        if( ( wall[hw].nextsector >= 0 && sector[wall[hw].nextsector].lotag <= 2 ) || ( wall[hw].nextsector == -1 && sector[sect].lotag <= 2 ) )
+                            if( ( (sx-p->posx)*(sx-p->posx) + (sy-p->posy)*(sy-p->posy) ) < (290*290) )
+                    {
+                        p->posz = p->oposz;
+                        p->poszv = 0;
+                        (*kb) = 1;
+                    }
+                }
+                break;
+
+            case SHRINKER_WEAPON:
+            case GROW_WEAPON:
+				if (VOLUMEONE) break;
+                if( p->curr_weapon == GROW_WEAPON )
+                {
+                    if( p->ammo_amount[GROW_WEAPON] > 0 )
+                    {
+                        (*kb) = 1;
+                        spritesound(EXPANDERSHOOT,pi);
+                    }
+                }
+                else if( p->ammo_amount[SHRINKER_WEAPON] > 0)
+                {
+                    (*kb) = 1;
+                    spritesound(SHRINKER_FIRE,pi);
+                }
+                break;
+
+            case FREEZE_WEAPON:
+				if (VOLUMEONE) break;
+                if( p->ammo_amount[FREEZE_WEAPON] > 0 )
+                {
+                    (*kb) = 1;
+                    spritesound(CAT_FIRE,pi);
+                }
+                break;
+            case DEVISTATOR_WEAPON:
+				if (VOLUMEONE) break;
+				if( p->ammo_amount[DEVISTATOR_WEAPON] > 0 )
+                {
+                    (*kb) = 1;
+                    p->hbomb_hold_delay = !p->hbomb_hold_delay;
+                    spritesound(CAT_FIRE,pi);
+                }
+                break;
+
+            case RPG_WEAPON:
+                if ( p->ammo_amount[RPG_WEAPON] > 0)
+                    (*kb) = 1;
+                break;
+
+            case KNEE_WEAPON:
+                if(p->quick_kick == 0) (*kb) = 1;
+                break;
+        }
+    }
+    else if((*kb))
+    {
+        switch( p->curr_weapon )
+        {
+            case HANDBOMB_WEAPON:
+
+                if( (*kb) == 6 && (sb_snum&(1<<2)) )
+                {
+                    p->rapid_fire_hold = 1;
+                    break;
+                }
+                (*kb)++;
+                if((*kb)==12)
+                {
+                    p->ammo_amount[HANDBOMB_WEAPON]--;
+
+                    if(p->on_ground && (sb_snum&2) )
+                    {
+                        k = 15;
+                        i = ((p->horiz+p->horizoff-100)*20);
+                    }
+                    else
+                    {
+                        k = 140;
+                        i = -512-((p->horiz+p->horizoff-100)*20);
+                    }
+
+                    j = EGS(p->cursectnum,
+                        p->posx+(sintable[(p->ang+512)&2047]>>6),
+                        p->posy+(sintable[p->ang&2047]>>6),
+                        p->posz,HEAVYHBOMB,-16,9,9,
+                        p->ang,(k+(p->hbomb_hold_delay<<5)),i,pi,1);
+
+                    if(k == 15)
+                    {
+                        sprite[j].yvel = 3;
+                        sprite[j].z += (8<<8);
+                    }
+
+                    k = hits(pi);
+                    if( k < 512 )
+                    {
+                        sprite[j].ang += 1024;
+                        sprite[j].zvel /= 3;
+                        sprite[j].xvel /= 3;
+                    }
+
+                    p->hbomb_on = 1;
+
+                }
+                else if( (*kb) < 12 && (sb_snum&(1<<2)) )
+                    p->hbomb_hold_delay++;
+                else if( (*kb) > 19 )
+                {
+                    (*kb) = 0;
+                    p->curr_weapon = HANDREMOTE_WEAPON;
+                    p->last_weapon = -1;
+                    p->weapon_pos = 10;
+                }
+
+                break;
+
+
+            case HANDREMOTE_WEAPON:
+
+                (*kb)++;
+
+                if((*kb) == 2)
+                {
+                    p->hbomb_on = 0;
+                }
+
+                if((*kb) == 10)
+                {
+                    (*kb) = 0;
+                    if(p->ammo_amount[HANDBOMB_WEAPON] > 0)
+                        addweapon(p,HANDBOMB_WEAPON);
+                    else checkavailweapon(p);
+                }
+                break;
+
+            case PISTOL_WEAPON:
+                if( (*kb)==1)
+                {
+                    shoot(pi,SHOTSPARK1);
+                    spritesound(PISTOL_FIRE,pi);
+
+                    lastvisinc = totalclock+32;
+                    p->visibility = 0;
+                }
+                else if((*kb) == 2)
+                    spawn(pi,SHELL);
+
+                (*kb)++;
+
+                if((*kb) >= 5)
+                {
+                    if( p->ammo_amount[PISTOL_WEAPON] <= 0 || (p->ammo_amount[PISTOL_WEAPON]%12) )
+                    {
+                        (*kb)=0;
+                        checkavailweapon(p);
+                    }
+                    else
+                    {
+                        switch((*kb))
+                        {
+                            case 5:
+                                spritesound(EJECT_CLIP,pi);
+                                break;
+                            case 8:
+                                spritesound(INSERT_CLIP,pi);
+                                break;
+                        }
+                    }
+                }
+
+                if((*kb) == 27)
+                {
+                    (*kb) = 0;
+                    checkavailweapon(p);
+                }
+
+                break;
+
+            case SHOTGUN_WEAPON:
+
+                (*kb)++;
+
+                if(*kb == 4)
+                {
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+                    shoot(pi,SHOTGUN);
+
+                    p->ammo_amount[SHOTGUN_WEAPON]--;
+
+                    spritesound(SHOTGUN_FIRE,pi);
+
+                    lastvisinc = totalclock+32;
+                    p->visibility = 0;
+                }
+
+                switch(*kb)
+                {
+                    case 13:
+                        checkavailweapon(p);
+                        break;
+                    case 15:
+                        spritesound(SHOTGUN_COCK,pi);
+                        break;
+                    case 17:
+                    case 20:
+                        p->kickback_pic++;
+                        break;
+                    case 24:
+                        j = spawn(pi,SHOTGUNSHELL);
+                        sprite[j].ang += 1024;
+                        ssp(j,CLIPMASK0);
+                        sprite[j].ang += 1024;
+                        p->kickback_pic++;
+                        break;
+                    case 31:
+                        *kb = 0;
+                        return;
+                }
+                break;
+
+            case CHAINGUN_WEAPON:
+
+                (*kb)++;
+
+                if( *(kb) <= 12 )
+                {
+                    if( ((*(kb))%3) == 0 )
+                    {
+                        p->ammo_amount[CHAINGUN_WEAPON]--;
+
+                        if( (*(kb)%3) == 0 )
+                        {
+                            j = spawn(pi,SHELL);
+
+                            sprite[j].ang += 1024;
+                            sprite[j].ang &= 2047;
+                            sprite[j].xvel += 32;
+                            sprite[j].z += (3<<8);
+                            ssp(j,CLIPMASK0);
+                        }
+
+                        spritesound(CHAINGUN_FIRE,pi);
+                        shoot(pi,CHAINGUN);
+                        lastvisinc = totalclock+32;
+                        p->visibility = 0;
+                        checkavailweapon(p);
+
+                        if( ( sb_snum&(1<<2) ) == 0 )
+                        {
+                            *kb = 0;
+                            break;
+                        }
+                    }
+                }
+                else if((*kb) > 10)
+                {
+                    if( sb_snum&(1<<2) ) *kb = 1;
+                    else *kb = 0;
+                }
+
+                break;
+
+            case SHRINKER_WEAPON:
+            case GROW_WEAPON:
+
+                if(p->curr_weapon == GROW_WEAPON)
+                {
+                    if((*kb) > 3)
+                    {
+                        *kb = 0;
+                        if( screenpeek == snum ) pus = 1;
+                        p->ammo_amount[GROW_WEAPON]--;
+                        shoot(pi,GROWSPARK);
+
+                        p->visibility = 0;
+                        lastvisinc = totalclock+32;
+                        checkavailweapon(p);
+                    }
+                    else (*kb)++;
+                }
+                else
+                {
+                    if( (*kb) > 10)
+                    {
+                        (*kb) = 0;
+
+                        p->ammo_amount[SHRINKER_WEAPON]--;
+                        shoot(pi,SHRINKER);
+
+                        p->visibility = 0;
+                        lastvisinc = totalclock+32;
+                        checkavailweapon(p);
+                    }
+                    else (*kb)++;
+                }
+                break;
+
+            case DEVISTATOR_WEAPON:
+                if(*kb)
+                {
+                    (*kb)++;
+
+                    if( (*kb) & 1 )
+                    {
+                        p->visibility = 0;
+                        lastvisinc = totalclock+32;
+                        shoot(pi,RPG);
+                        p->ammo_amount[DEVISTATOR_WEAPON]--;
+                        checkavailweapon(p);
+                    }
+                    if((*kb) > 5) (*kb) = 0;
+                }
+                break;
+            case FREEZE_WEAPON:
+
+                if( (*kb) < 4 )
+                {
+                    (*kb)++;
+                    if( (*kb) == 3 )
+                    {
+                        p->ammo_amount[FREEZE_WEAPON]--;
+                        p->visibility = 0;
+                        lastvisinc = totalclock+32;
+                        shoot(pi,FREEZEBLAST);
+                        checkavailweapon(p);
+                    }
+                    if(s->xrepeat < 32)
+                        { *kb = 0; break; }
+                }
+                else
+                {
+                    if( sb_snum&(1<<2))
+                    {
+                        *kb = 1;
+                        spritesound(CAT_FIRE,pi);
+                    }
+                    else *kb = 0;
+                }
+                break;
+
+            case TRIPBOMB_WEAPON:
+                if(*kb < 4)
+                {
+                    p->posz = p->oposz;
+                    p->poszv = 0;
+                    if( (*kb) == 3 )
+                        shoot(pi,HANDHOLDINGLASER);
+                }
+                if((*kb) == 16)
+                {
+                    (*kb) = 0;
+                    checkavailweapon(p);
+                    p->weapon_pos = -9;
+                }
+                else (*kb)++;
+                break;
+            case KNEE_WEAPON:
+                (*kb)++;
+
+                if( (*kb) == 7) shoot(pi,KNEE);
+                else if( (*kb) == 14)
+                {
+                    if( sb_snum&(1<<2) )
+                        *kb = 1+(TRAND&3);
+                    else *kb = 0;
+                }
+
+                if(p->wantweaponfire >= 0)
+                    checkavailweapon(p);
+                break;
+
+            case RPG_WEAPON:
+                (*kb)++;
+                if( (*kb) == 4 )
+                {
+                    p->ammo_amount[RPG_WEAPON]--;
+                    lastvisinc = totalclock+32;
+                    p->visibility = 0;
+                    shoot(pi,RPG);
+                    checkavailweapon(p);
+                }
+                else if( *kb == 20 )
+                    *kb = 0;
+                break;
+        }
+    }
+}
+
+
+
+//UPDATE THIS FILE OVER THE OLD GETSPRITESCORE/COMPUTERGETINPUT FUNCTIONS
+static int getspritescore(long snum, long dapicnum)
+{
+    switch(dapicnum)
+    {
+        case FIRSTGUNSPRITE: return(5);
+        case CHAINGUNSPRITE: return(50);
+        case RPGSPRITE: return(200);
+        case FREEZESPRITE: return(25);
+        case SHRINKERSPRITE: return(80);
+        case HEAVYHBOMB: return(60);
+        case TRIPBOMBSPRITE: return(50);
+        case SHOTGUNSPRITE: return(120);
+        case DEVISTATORSPRITE: return(120);
+
+        case FREEZEAMMO: if (ps[snum].ammo_amount[FREEZE_WEAPON] < max_ammo_amount[FREEZE_WEAPON]) return(10); else return(0);
+        case AMMO: if (ps[snum].ammo_amount[SHOTGUN_WEAPON] < max_ammo_amount[SHOTGUN_WEAPON]) return(10); else return(0);
+        case BATTERYAMMO: if (ps[snum].ammo_amount[CHAINGUN_WEAPON] < max_ammo_amount[CHAINGUN_WEAPON]) return(20); else return(0);
+        case DEVISTATORAMMO: if (ps[snum].ammo_amount[DEVISTATOR_WEAPON] < max_ammo_amount[DEVISTATOR_WEAPON]) return(25); else return(0);
+        case RPGAMMO: if (ps[snum].ammo_amount[RPG_WEAPON] < max_ammo_amount[RPG_WEAPON]) return(50); else return(0);
+        case CRYSTALAMMO: if (ps[snum].ammo_amount[SHRINKER_WEAPON] < max_ammo_amount[SHRINKER_WEAPON]) return(10); else return(0);
+        case HBOMBAMMO: if (ps[snum].ammo_amount[HANDBOMB_WEAPON] < max_ammo_amount[HANDBOMB_WEAPON]) return(30); else return(0);
+        case SHOTGUNAMMO: if (ps[snum].ammo_amount[SHOTGUN_WEAPON] < max_ammo_amount[SHOTGUN_WEAPON]) return(25); else return(0);
+
+        case COLA: if (sprite[ps[snum].i].extra < 100) return(10); else return(0);
+        case SIXPAK: if (sprite[ps[snum].i].extra < 100) return(30); else return(0);
+        case FIRSTAID: if (ps[snum].firstaid_amount < 100) return(100); else return(0);
+        case SHIELD: if (ps[snum].shield_amount < 100) return(50); else return(0);
+        case STEROIDS: if (ps[snum].steroids_amount < 400) return(30); else return(0);
+        case AIRTANK: if (ps[snum].scuba_amount < 6400) return(30); else return(0);
+        case JETPACK: if (ps[snum].jetpack_amount < 1600) return(100); else return(0);
+        case HEATSENSOR: if (ps[snum].heat_amount < 1200) return(10); else return(0);
+        case ACCESSCARD: return(1);
+        case BOOTS: if (ps[snum].boot_amount < 200) return(50); else return(0);
+        case ATOMICHEALTH: if (sprite[ps[snum].i].extra < max_player_health) return(50); else return(0);
+        case HOLODUKE: if (ps[snum].holoduke_amount < 2400) return(30); else return(0);
+    }
+    return(0);
+}
+
+static long fdmatrix[12][12] =
+{
+ //KNEE PIST SHOT CHAIN RPG PIPE SHRI DEVI WALL FREE HAND EXPA
+   {  128,  -1,  -1,  -1, 128,  -1,  -1,  -1, 128,  -1, 128,  -1 },   //KNEE
+   { 1024,1024,1024,1024,2560, 128,2560,2560,1024,2560,2560,2560 },   //PIST
+   {  512, 512, 512, 512,2560, 128,2560,2560,1024,2560,2560,2560 },   //SHOT
+   {  512, 512, 512, 512,2560, 128,2560,2560,1024,2560,2560,2560 },   //CHAIN
+   { 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560 },   //RPG
+   {  512, 512, 512, 512,2048, 512,2560,2560, 512,2560,2560,2560 },   //PIPE
+   {  128, 128, 128, 128,2560, 128,2560,2560, 128, 128, 128, 128 },   //SHRI
+   { 1536,1536,1536,1536,2560,1536,1536,1536,1536,1536,1536,1536 },   //DEVI
+   {   -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1 },   //WALL
+   {  128, 128, 128, 128,2560, 128,2560,2560, 128, 128, 128, 128 },   //FREE
+   { 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560 },   //HAND
+   {  128, 128, 128, 128,2560, 128,2560,2560, 128, 128, 128, 128 }    //EXPA
+};
+
+static long goalx[MAXPLAYERS], goaly[MAXPLAYERS], goalz[MAXPLAYERS];
+static long goalsect[MAXPLAYERS], goalwall[MAXPLAYERS], goalsprite[MAXPLAYERS];
+static long goalplayer[MAXPLAYERS], clipmovecount[MAXPLAYERS];
+short searchsect[MAXSECTORS], searchparent[MAXSECTORS];
+char dashow2dsector[(MAXSECTORS+7)>>3];
+void computergetinput(long snum, input *syn)
+{
+    long i, j, k, l, x1, y1, z1, x2, y2, z2, x3, y3, z3, dx, dy;
+    long dist, daang, zang, fightdist, damyang, damysect;
+    long startsect, endsect, splc, send, startwall, endwall;
+    short dasect, dawall, daspr;
+    struct player_struct *p;
+    walltype *wal;
+
+    p = &ps[snum];
+    syn->fvel = 0;
+    syn->svel = 0;
+    syn->avel = 0;
+    syn->horz = 0;
+    syn->bits = 0;
+
+    z2 = y2 = x2 = 0;
+    x1 = sprite[p->i].x;
+    y1 = sprite[p->i].y;
+    z1 = sprite[p->i].z;
+    damyang = sprite[p->i].ang;
+    damysect = sprite[p->i].sectnum;
+    if ((numplayers >= 2) && (snum == myconnectindex))
+        { x1 = myx; y1 = myy; z1 = myz+PHEIGHT; damyang = myang; damysect = mycursectnum; }
+
+    if (!(numframes&7))
+    {
+        x2 = sprite[ps[goalplayer[snum]].i].x;
+        y2 = sprite[ps[goalplayer[snum]].i].y;
+        z2 = sprite[ps[goalplayer[snum]].i].z;
+		
+		if (!cansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[ps[goalplayer[snum]].i].sectnum))
+            goalplayer[snum] = snum;
+    }
+
+    if ((goalplayer[snum] == snum) || (ps[goalplayer[snum]].dead_flag != 0))
+    {
+        j = 0x7fffffff;
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+            if (i != snum)
+            {
+                dist = ksqrt((sprite[ps[i].i].x-x1)*(sprite[ps[i].i].x-x1)+(sprite[ps[i].i].y-y1)*(sprite[ps[i].i].y-y1));
+
+                x2 = sprite[ps[i].i].x;
+                y2 = sprite[ps[i].i].y;
+                z2 = sprite[ps[i].i].z;
+                if (!cansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[ps[i].i].sectnum))
+                    dist <<= 1;
+
+                if (dist < j) { j = dist; goalplayer[snum] = i; }
+            }
+    }
+
+    x2 = sprite[ps[goalplayer[snum]].i].x;
+    y2 = sprite[ps[goalplayer[snum]].i].y;
+    z2 = sprite[ps[goalplayer[snum]].i].z;
+
+    if (p->dead_flag) syn->bits |= (1<<29);
+    if ((p->firstaid_amount > 0) && (p->last_extra < max_player_health))
+        syn->bits |= (1<<16);
+
+    for(j=headspritestat[4];j>=0;j=nextspritestat[j])
+    {
+        switch (sprite[j].picnum)
+        {
+            case TONGUE: k = 4; break;
+            case FREEZEBLAST: k = 4; break;
+            case SHRINKSPARK: k = 16; break;
+            case RPG: k = 16; break;
+            default: k = 0; break;
+        }
+        if (k)
+        {
+            x3 = sprite[j].x;
+            y3 = sprite[j].y;
+            z3 = sprite[j].z;
+            for(l=0;l<=8;l++)
+            {
+                if (tmulscale11(x3-x1,x3-x1,y3-y1,y3-y1,(z3-z1)>>4,(z3-z1)>>4) < 3072)
+                {
+                    dx = sintable[(sprite[j].ang+512)&2047];
+                    dy = sintable[sprite[j].ang&2047];
+                    if ((x1-x3)*dy > (y1-y3)*dx) i = -k*512; else i = k*512;
+                    syn->fvel -= mulscale17(dy,i);
+                    syn->svel += mulscale17(dx,i);
+                }
+                if (l < 7)
+                {
+                    x3 += (mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047])<<2);
+                    y3 += (mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047])<<2);
+                    z3 += (sprite[j].zvel<<2);
+                }
+                else
+                {
+                    hitscan(sprite[j].x,sprite[j].y,sprite[j].z,sprite[j].sectnum,
+                     mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047]),
+                     mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047]),
+                     (long)sprite[j].zvel,
+                     &dasect,&dawall,&daspr,&x3,&y3,&z3,CLIPMASK1);
+                }
+            }
+        }
+    }
+
+    if ((ps[goalplayer[snum]].dead_flag == 0) &&
+        ((cansee(x1,y1,z1,damysect,x2,y2,z2,sprite[ps[goalplayer[snum]].i].sectnum)) ||
+         (cansee(x1,y1,z1-(24<<8),damysect,x2,y2,z2-(24<<8),sprite[ps[goalplayer[snum]].i].sectnum)) ||
+         (cansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[ps[goalplayer[snum]].i].sectnum))))
+    {
+        syn->bits |= (1<<2);
+
+        if ((p->curr_weapon == HANDBOMB_WEAPON) && (!(rand()&7)))
+            syn->bits &= ~(1<<2);
+
+        if (p->curr_weapon == TRIPBOMB_WEAPON)
+            syn->bits |= ((rand()%MAX_WEAPONS)<<8);
+
+        if (p->curr_weapon == RPG_WEAPON)
+        {
+            hitscan(x1,y1,z1-PHEIGHT,damysect,sintable[(damyang+512)&2047],sintable[damyang&2047],
+                (100-p->horiz-p->horizoff)*32,&dasect,&dawall,&daspr,&x3,&y3,&z3,CLIPMASK1);
+            if ((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1) < 2560*2560) syn->bits &= ~(1<<2);
+        }
+
+
+        fightdist = fdmatrix[p->curr_weapon][ps[goalplayer[snum]].curr_weapon];
+        if (fightdist < 128) fightdist = 128;
+        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (dist == 0) dist = 1;
+        daang = getangle(x2+(ps[goalplayer[snum]].posxv>>14)-x1,y2+(ps[goalplayer[snum]].posyv>>14)-y1);
+        zang = 100-((z2-z1)*8)/dist;
+        fightdist = max(fightdist,(klabs(z2-z1)>>4));
+
+        if (sprite[ps[goalplayer[snum]].i].yrepeat < 32)
+            { fightdist = 0; syn->bits &= ~(1<<2); }
+        if (sprite[ps[goalplayer[snum]].i].pal == 1)
+            { fightdist = 0; syn->bits &= ~(1<<2); }
+
+        if (dist < 256) syn->bits |= (1<<22);
+
+        x3 = x2+((x1-x2)*fightdist/dist);
+        y3 = y2+((y1-y2)*fightdist/dist);
+        syn->fvel += (x3-x1)*2047/dist;
+        syn->svel += (y3-y1)*2047/dist;
+
+            //Strafe attack
+        if (fightdist)
+        {
+            j = totalclock+snum*13468;
+            i = sintable[(j<<6)&2047];
+            i += sintable[((j+4245)<<5)&2047];
+            i += sintable[((j+6745)<<4)&2047];
+            i += sintable[((j+15685)<<3)&2047];
+            dx = sintable[(sprite[ps[goalplayer[snum]].i].ang+512)&2047];
+            dy = sintable[sprite[ps[goalplayer[snum]].i].ang&2047];
+            if ((x1-x2)*dy > (y1-y2)*dx) i += 8192; else i -= 8192;
+            syn->fvel += ((sintable[(daang+1024)&2047]*i)>>17);
+            syn->svel += ((sintable[(daang+512)&2047]*i)>>17);
+        }
+
+        syn->avel = min(max((((daang+1024-damyang)&2047)-1024)>>1,-127),127);
+        syn->horz = min(max((zang-p->horiz)>>1,-MAXHORIZ),MAXHORIZ);
+        syn->bits |= (1<<23);
+        return;
+    }
+
+    goalsect[snum] = -1;
+    if (goalsect[snum] < 0)
+    {
+        goalwall[snum] = -1;
+        startsect = sprite[p->i].sectnum;
+        endsect = sprite[ps[goalplayer[snum]].i].sectnum;
+
+        clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
+        searchsect[0] = startsect;
+        searchparent[0] = -1;
+        dashow2dsector[startsect>>3] |= (1<<(startsect&7));
+        for(splc=0,send=1;splc<send;splc++)
+        {
+            startwall = sector[searchsect[splc]].wallptr;
+            endwall = startwall + sector[searchsect[splc]].wallnum;
+            for(i=startwall,wal=&wall[startwall];i<endwall;i++,wal++)
+            {
+                j = wal->nextsector; if (j < 0) continue;
+
+                dx = ((wall[wal->point2].x+wal->x)>>1);
+                dy = ((wall[wal->point2].y+wal->y)>>1);
+                if ((getceilzofslope(j,dx,dy) > getflorzofslope(j,dx,dy)-(28<<8)) && ((sector[j].lotag < 15) || (sector[j].lotag > 22)))
+                    continue;
+                if (getflorzofslope(j,dx,dy) < getflorzofslope(searchsect[splc],dx,dy)-(72<<8))
+                    continue;
+                if ((dashow2dsector[j>>3]&(1<<(j&7))) == 0)
+                {
+                    dashow2dsector[j>>3] |= (1<<(j&7));
+                    searchsect[send] = (short)j;
+                    searchparent[send] = (short)splc;
+                    send++;
+                    if (j == endsect)
+                    {
+                        clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
+                        for(k=send-1;k>=0;k=searchparent[k])
+                            dashow2dsector[searchsect[k]>>3] |= (1<<(searchsect[k]&7));
+
+                        for(k=send-1;k>=0;k=searchparent[k])
+                            if (!searchparent[k]) break;
+
+                        goalsect[snum] = searchsect[k];
+                        startwall = sector[goalsect[snum]].wallptr;
+                        endwall = startwall+sector[goalsect[snum]].wallnum;
+                        x3 = y3 = 0;
+                        for(i=startwall;i<endwall;i++)
+                        {
+                            x3 += wall[i].x;
+                            y3 += wall[i].y;
+                        }
+                        x3 /= (endwall-startwall);
+                        y3 /= (endwall-startwall);
+
+                        startwall = sector[startsect].wallptr;
+                        endwall = startwall+sector[startsect].wallnum;
+                        l = 0; k = startwall;
+                        for(i=startwall;i<endwall;i++)
+                        {
+                            if (wall[i].nextsector != goalsect[snum]) continue;
+                            dx = wall[wall[i].point2].x-wall[i].x;
+                            dy = wall[wall[i].point2].y-wall[i].y;
+
+                            //if (dx*(y1-wall[i].y) <= dy*(x1-wall[i].x))
+                            //   if (dx*(y2-wall[i].y) >= dy*(x2-wall[i].x))
+                                    if ((x3-x1)*(wall[i].y-y1) <= (y3-y1)*(wall[i].x-x1))
+                                        if ((x3-x1)*(wall[wall[i].point2].y-y1) >= (y3-y1)*(wall[wall[i].point2].x-x1))
+                                            { k = i; break; }
+
+                            dist = ksqrt(dx*dx+dy*dy);
+                            if (dist > l) { l = dist; k = i; }
+                        }
+                        goalwall[snum] = k;
+                        daang = ((getangle(wall[wall[k].point2].x-wall[k].x,wall[wall[k].point2].y-wall[k].y)+1536)&2047);
+                        goalx[snum] = ((wall[k].x+wall[wall[k].point2].x)>>1)+(sintable[(daang+512)&2047]>>8);
+                        goaly[snum] = ((wall[k].y+wall[wall[k].point2].y)>>1)+(sintable[daang&2047]>>8);
+                        goalz[snum] = sector[goalsect[snum]].floorz-(32<<8);
+                        break;
+                    }
+                }
+            }
+
+            for(i=headspritesect[searchsect[splc]];i>=0;i=nextspritesect[i])
+                if (sprite[i].lotag == 7)
+                {
+                    j = sprite[sprite[i].owner].sectnum;
+                    if ((dashow2dsector[j>>3]&(1<<(j&7))) == 0)
+                    {
+                        dashow2dsector[j>>3] |= (1<<(j&7));
+                        searchsect[send] = (short)j;
+                        searchparent[send] = (short)splc;
+                        send++;
+                        if (j == endsect)
+                        {
+                            clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
+                            for(k=send-1;k>=0;k=searchparent[k])
+                                dashow2dsector[searchsect[k]>>3] |= (1<<(searchsect[k]&7));
+
+                            for(k=send-1;k>=0;k=searchparent[k])
+                                if (!searchparent[k]) break;
+
+                            goalsect[snum] = searchsect[k];
+                            startwall = sector[startsect].wallptr;
+                            endwall = startwall+sector[startsect].wallnum;
+                            l = 0; k = startwall;
+                            for(i=startwall;i<endwall;i++)
+                            {
+                                dx = wall[wall[i].point2].x-wall[i].x;
+                                dy = wall[wall[i].point2].y-wall[i].y;
+                                dist = ksqrt(dx*dx+dy*dy);
+                                if ((wall[i].nextsector == goalsect[snum]) && (dist > l))
+                                    { l = dist; k = i; }
+                            }
+                            goalwall[snum] = k;
+                            daang = ((getangle(wall[wall[k].point2].x-wall[k].x,wall[wall[k].point2].y-wall[k].y)+1536)&2047);
+                            goalx[snum] = ((wall[k].x+wall[wall[k].point2].x)>>1)+(sintable[(daang+512)&2047]>>8);
+                            goaly[snum] = ((wall[k].y+wall[wall[k].point2].y)>>1)+(sintable[daang&2047]>>8);
+                            goalz[snum] = sector[goalsect[snum]].floorz-(32<<8);
+                            break;
+                        }
+                    }
+                }
+            if (goalwall[snum] >= 0) break;
+        }
+    }
+
+    if ((goalsect[snum] < 0) || (goalwall[snum] < 0))
+    {
+        if (goalsprite[snum] < 0)
+        {
+            for(k=0;k<4;k++)
+            {
+                i = (rand()%numsectors);
+                for(j=headspritesect[i];j>=0;j=nextspritesect[j])
+                {
+                    if ((sprite[j].xrepeat <= 0) || (sprite[j].yrepeat <= 0)) continue;
+                    if (getspritescore(snum,sprite[j].picnum) <= 0) continue;
+                    if (cansee(x1,y1,z1-(32<<8),damysect,sprite[j].x,sprite[j].y,sprite[j].z-(4<<8),i))
+                        { goalx[snum] = sprite[j].x; goaly[snum] = sprite[j].y; goalz[snum] = sprite[j].z; goalsprite[snum] = j; break; }
+                }
+            }
+        }
+        x2 = goalx[snum];
+        y2 = goaly[snum];
+        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (!dist) return;
+        daang = getangle(x2-x1,y2-y1);
+        syn->fvel += (x2-x1)*2047/dist;
+        syn->svel += (y2-y1)*2047/dist;
+        syn->avel = min(max((((daang+1024-damyang)&2047)-1024)>>3,-127),127);
+    }
+    else
+        goalsprite[snum] = -1;
+
+    x3 = p->posx; y3 = p->posy; z3 = p->posz; dasect = p->cursectnum;
+    i = clipmove(&x3,&y3,&z3,&dasect,p->posxv,p->posyv,164L,4L<<8,4L<<8,CLIPMASK0);
+    if (!i)
+    {
+        x3 = p->posx; y3 = p->posy; z3 = p->posz+(24<<8); dasect = p->cursectnum;
+        i = clipmove(&x3,&y3,&z3,&dasect,p->posxv,p->posyv,164L,4L<<8,4L<<8,CLIPMASK0);
+    }
+    if (i)
+    {
+        clipmovecount[snum]++;
+
+        j = 0;
+        if ((i&0xc000) == 32768)  //Hit a wall (49152 for sprite)
+            if (wall[i&(MAXWALLS-1)].nextsector >= 0)
+            {
+                if (getflorzofslope(wall[i&(MAXWALLS-1)].nextsector,p->posx,p->posy) <= p->posz+(24<<8)) j |= 1;
+                if (getceilzofslope(wall[i&(MAXWALLS-1)].nextsector,p->posx,p->posy) >= p->posz-(24<<8)) j |= 2;
+            }
+        if ((i&0xc000) == 49152) j = 1;
+        if (j&1) if (clipmovecount[snum] == 4) syn->bits |= (1<<0);
+        if (j&2) syn->bits |= (1<<1);
+
+            //Strafe attack
+        daang = getangle(x2-x1,y2-y1);
+        if ((i&0xc000) == 32768)
+            daang = getangle(wall[wall[i&(MAXWALLS-1)].point2].x-wall[i&(MAXWALLS-1)].x,wall[wall[i&(MAXWALLS-1)].point2].y-wall[i&(MAXWALLS-1)].y);
+        j = totalclock+snum*13468;
+        i = sintable[(j<<6)&2047];
+        i += sintable[((j+4245)<<5)&2047];
+        i += sintable[((j+6745)<<4)&2047];
+        i += sintable[((j+15685)<<3)&2047];
+        syn->fvel += ((sintable[(daang+1024)&2047]*i)>>17);
+        syn->svel += ((sintable[(daang+512)&2047]*i)>>17);
+
+        if ((clipmovecount[snum]&31) == 2) syn->bits |= (1<<29);
+        if ((clipmovecount[snum]&31) == 17) syn->bits |= (1<<22);
+        if (clipmovecount[snum] > 32) { goalsect[snum] = -1; goalwall[snum] = -1; clipmovecount[snum] = 0; }
+
+        goalsprite[snum] = -1;
+    }
+    else
+        clipmovecount[snum] = 0;
+
+    if ((goalsect[snum] >= 0) && (goalwall[snum] >= 0))
+    {
+        x2 = goalx[snum];
+        y2 = goaly[snum];
+        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (!dist) return;
+        daang = getangle(x2-x1,y2-y1);
+        if ((goalwall[snum] >= 0) && (dist < 4096))
+            daang = ((getangle(wall[wall[goalwall[snum]].point2].x-wall[goalwall[snum]].x,wall[wall[goalwall[snum]].point2].y-wall[goalwall[snum]].y)+1536)&2047);
+        syn->fvel += (x2-x1)*2047/dist;
+        syn->svel += (y2-y1)*2047/dist;
+        syn->avel = min(max((((daang+1024-damyang)&2047)-1024)>>3,-127),127);
+    }
+}
+
--- /dev/null
+++ b/Game/src/premap.c
@@ -1,0 +1,1720 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+#include "cache1d.h"
+
+extern char everyothertime;
+short which_palookup = 9;
+
+
+static void tloadtile(short tilenume)
+{
+    gotpic[tilenume>>3] |= (1<<(tilenume&7));
+}
+
+void cachespritenum(short i)
+{
+    char maxc;
+    short j;
+
+    if(ud.monsters_off && badguy(&sprite[i])) return;
+
+    maxc = 1;
+
+    switch(PN)
+    {
+        case HYDRENT:
+            tloadtile(BROKEFIREHYDRENT);
+            for(j = TOILETWATER; j < (TOILETWATER+4); j++)
+                if(waloff[j] == 0) tloadtile(j);
+            break;
+        case TOILET:
+            tloadtile(TOILETBROKE);
+            for(j = TOILETWATER; j < (TOILETWATER+4); j++)
+                if(waloff[j] == 0) tloadtile(j);
+            break;
+        case STALL:
+            tloadtile(STALLBROKE);
+            for(j = TOILETWATER; j < (TOILETWATER+4); j++)
+                if(waloff[j] == 0) tloadtile(j);
+            break;
+        case RUBBERCAN:
+            maxc = 2;
+            break;
+        case TOILETWATER:
+            maxc = 4;
+            break;
+        case FEMPIC1:
+            maxc = 44;
+            break;
+        case LIZTROOP:
+        case LIZTROOPRUNNING:
+        case LIZTROOPSHOOT:
+        case LIZTROOPJETPACK:
+        case LIZTROOPONTOILET:
+        case LIZTROOPDUCKING:
+            for(j = LIZTROOP; j < (LIZTROOP+72); j++)
+                if(waloff[j] == 0)
+                    tloadtile(j);
+            for(j=HEADJIB1;j<LEGJIB1+3;j++)
+                if(waloff[j] == 0)
+                    tloadtile(j);
+            maxc = 0;
+            break;
+        case WOODENHORSE:
+            maxc = 5;
+            for(j = HORSEONSIDE; j < (HORSEONSIDE+4); j++)
+                if(waloff[j] == 0)
+                    tloadtile(j);
+            break;
+        case NEWBEAST:
+        case NEWBEASTSTAYPUT:
+            maxc = 90;
+            break;
+        case BOSS1:
+        case BOSS2:
+        case BOSS3:
+            maxc = 30;
+            break;
+        case OCTABRAIN:
+        case OCTABRAINSTAYPUT:
+        case COMMANDER:
+        case COMMANDERSTAYPUT:
+            maxc = 38;
+            break;
+        case RECON:
+            maxc = 13;
+            break;
+        case PIGCOP:
+        case PIGCOPDIVE:
+            maxc = 61;
+            break;
+        case SHARK:
+            maxc = 30;
+            break;
+        case LIZMAN:
+        case LIZMANSPITTING:
+        case LIZMANFEEDING:
+        case LIZMANJUMP:
+            for(j=LIZMANHEAD1;j<LIZMANLEG1+3;j++)
+                if(waloff[j] == 0)
+                    tloadtile(j);
+            maxc = 80;
+            break;
+        case APLAYER:
+            maxc = 0;
+            if(ud.multimode > 1)
+            {
+                maxc = 5;
+                for(j = 1420;j < 1420+106; j++)
+                    if(waloff[j] == -1)
+                        tloadtile(j);
+            }
+            break;
+        case ATOMICHEALTH:
+            maxc = 14;
+            break;
+        case DRONE:
+            maxc = 10;
+            break;
+        case EXPLODINGBARREL:
+        case SEENINE:
+        case OOZFILTER:
+            maxc = 3;
+            break;
+        case NUKEBARREL:
+        case CAMERA1:
+            maxc = 5;
+            break;
+    }
+
+    for(j = PN; j < (PN+maxc); j++)
+        if(waloff[j] == 0)
+            tloadtile(j);
+}
+
+void cachegoodsprites(void)
+{
+    short i;
+
+    if(ud.screen_size >= 8)
+    {
+        if(waloff[BOTTOMSTATUSBAR] == 0)
+            tloadtile(BOTTOMSTATUSBAR);
+        if( ud.multimode > 1)
+        {
+            if(waloff[FRAGBAR] == 0)
+                tloadtile(FRAGBAR);
+            for(i=MINIFONT;i<MINIFONT+63;i++)
+                if(waloff[i] == 0)
+                    tloadtile(i);
+        }
+    }
+
+    tloadtile(VIEWSCREEN);
+
+    for(i=STARTALPHANUM;i<ENDALPHANUM+1;i++)
+        if (waloff[i] == 0)
+            tloadtile(i);
+
+    for(i=FOOTPRINTS;i<FOOTPRINTS+3;i++)
+        if (waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = BIGALPHANUM; i < BIGALPHANUM+82; i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = BURNING; i < BURNING+14; i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = BURNING2; i < BURNING2+14; i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = CRACKKNUCKLES; i < CRACKKNUCKLES+4; i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = FIRSTGUN; i < FIRSTGUN+3 ; i++ )
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = EXPLOSION2; i < EXPLOSION2+21 ; i++ )
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    tloadtile(BULLETHOLE);
+
+    for( i = FIRSTGUNRELOAD; i < FIRSTGUNRELOAD+8 ; i++ )
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    tloadtile(FOOTPRINTS);
+
+    for( i = JIBS1; i < (JIBS5+5); i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = SCRAP1; i < (SCRAP1+19); i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+
+    for( i = SMALLSMOKE; i < (SMALLSMOKE+4); i++)
+        if(waloff[i] == 0)
+            tloadtile(i);
+}
+
+char getsound(unsigned short num)
+{
+    short fp;
+    long   l;
+
+    if(num >= NUM_SOUNDS || SoundToggle == 0) return 0;
+    if (FXDevice == NumSoundCards) return 0;
+
+    fp = TCkopen4load(sounds[num],0);
+    if(fp == -1) return 0;
+
+    l = kfilelength( fp );
+    soundsiz[num] = l;
+
+    if( (ud.level_number == 0 && ud.volume_number == 0 && (num == 189 || num == 232 || num == 99 || num == 233 || num == 17 ) ) ||
+        ( l < 12288 ) )
+    {
+        Sound[num].lock = 2;
+        allocache((long *)&Sound[num].ptr,l,&Sound[num].lock);
+        if(Sound[num].ptr != NULL)
+            kread( fp, Sound[num].ptr , l);
+    }
+    kclose( fp );
+    return 1;
+}
+
+void precachenecessarysounds(void)
+{
+    short i, j;
+
+    if (FXDevice == NumSoundCards) return;
+    j = 0;
+
+    for(i=0;i<NUM_SOUNDS;i++)
+        if(Sound[i].ptr == 0)
+        {
+            j++;
+            if( (j&7) == 0 )
+                getpackets();
+            getsound(i);
+        }
+}
+
+
+void cacheit(void)
+{
+    short i,j;
+
+    precachenecessarysounds();
+
+    cachegoodsprites();
+
+    for(i=0;i<numwalls;i++)
+        if( waloff[wall[i].picnum] == 0 )
+    {
+        if(waloff[wall[i].picnum] == 0)
+            tloadtile(wall[i].picnum);
+        if(wall[i].overpicnum >= 0 && waloff[wall[i].overpicnum] == 0 )
+            tloadtile(wall[i].overpicnum);
+    }
+
+    for(i=0;i<numsectors;i++)
+    {
+        if( waloff[sector[i].floorpicnum] == 0 )
+            tloadtile( sector[i].floorpicnum );
+        if( waloff[sector[i].ceilingpicnum] == 0 )
+        {
+            tloadtile( sector[i].ceilingpicnum );
+            if( waloff[sector[i].ceilingpicnum] == LA)
+            {
+                tloadtile(LA+1);
+                tloadtile(LA+2);
+            }
+        }
+
+        j = headspritesect[i];
+        while(j >= 0)
+        {
+            if(sprite[j].xrepeat != 0 && sprite[j].yrepeat != 0 && (sprite[j].cstat&32768) == 0)
+                if(waloff[sprite[j].picnum] == 0)
+                    cachespritenum(j);
+            j = nextspritesect[j];
+        }
+    }
+
+}
+
+void docacheit(void)
+{
+    long i,j;
+
+    j = 0;
+
+    for(i=0;i<MAXTILES;i++)
+        if( (gotpic[i>>3]&(1<<(i&7))) && waloff[i] == 0)
+    {
+        loadtile((short)i);
+        j++;
+        if((j&7) == 0) getpackets();
+    }
+
+    clearbufbyte(gotpic,sizeof(gotpic),0L);
+
+}
+
+
+
+void xyzmirror(short i,short wn)
+{
+    if (waloff[wn] == 0) loadtile(wn);
+	setviewtotile(wn,tilesizy[wn],tilesizx[wn]);
+
+	drawrooms(SX,SY,SZ,SA,100+sprite[i].shade,SECT);
+	display_mirror = 1; animatesprites(SX,SY,SA,65536L); display_mirror = 0;
+	drawmasks();
+
+	setviewback();
+	squarerotatetile(wn);
+}
+
+void vscrn(void)
+{
+     long ss, x1, x2, y1, y2;
+
+	 if(ud.screen_size < 0) ud.screen_size = 0;
+	 else if(ud.screen_size > 63) ud.screen_size = 64;
+
+     if(ud.screen_size == 0) flushperms();
+
+	 ss = max(ud.screen_size-8,0);
+
+	 x1 = scale(ss,xdim,160);
+	 x2 = xdim-x1;
+
+	 y1 = ss; y2 = 200;
+	 y1 += countfragbars();
+
+	 if (ud.screen_size >= 8) y2 -= (ss+34);
+
+	 y1 = scale(y1,ydim,200);
+	 y2 = scale(y2,ydim,200);
+
+	 setview(x1,y1,x2-1,y2-1);
+
+     pub = NUMPAGES;
+     pus = NUMPAGES;
+}
+
+int countfragbars(void)
+{
+	long i, j, y = 0;
+
+	if ( ud.screen_size > 0 && ud.coop != 1 && ud.multimode > 1)
+	{
+		j = 0;
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if(i > j) j = i;
+
+		if (j >= 1) y += 8;
+		if (j >= 4) y += 8;
+		if (j >= 8) y += 8;
+		if (j >= 12) y += 8;
+	}
+
+	return(y);
+}
+
+
+void pickrandomspot(short snum)
+{
+    struct player_struct *p;
+    short i;
+
+    p = &ps[snum];
+
+    if( ud.multimode > 1 && ud.coop != 1)
+        i = TRAND%numplayersprites;
+    else i = snum;
+
+    p->bobposx = p->oposx = p->posx = po[i].ox;
+    p->bobposy = p->oposy = p->posy = po[i].oy;
+    p->oposz = p->posz = po[i].oz;
+    p->ang = po[i].oa;
+    p->cursectnum = po[i].os;
+}
+
+void resetplayerstats(short snum)
+{
+    struct player_struct *p;
+    short i;
+
+    p = &ps[snum];
+
+    ud.show_help        = 0;
+    ud.showallmap       = 0;
+    p->dead_flag        = 0;
+    p->wackedbyactor    = -1;
+    p->falling_counter  = 0;
+    p->quick_kick       = 0;
+    p->subweapon        = 0;
+    p->last_full_weapon = 0;
+    p->ftq              = 0;
+    p->fta              = 0;
+    p->tipincs          = 0;
+    p->buttonpalette    = 0;
+    p->actorsqu         =-1;
+    p->invdisptime      = 0;
+    p->refresh_inventory= 0;
+    p->last_pissed_time = 0;
+    p->holster_weapon   = 0;
+    p->pycount          = 0;
+    p->pyoff            = 0;
+    p->opyoff           = 0;
+    p->loogcnt          = 0;
+    p->angvel           = 0;
+    p->weapon_sway      = 0;
+//    p->select_dir       = 0;
+    p->extra_extra8     = 0;
+    p->show_empty_weapon= 0;
+    p->dummyplayersprite=-1;
+    p->crack_time       = 0;
+    p->hbomb_hold_delay = 0;
+    p->transporter_hold = 0;
+    p->wantweaponfire  = -1;
+    p->hurt_delay       = 0;
+    p->footprintcount   = 0;
+    p->footprintpal     = 0;
+    p->footprintshade   = 0;
+    p->jumping_toggle   = 0;
+    p->ohoriz = p->horiz= 140;
+    p->horizoff         = 0;
+    p->bobcounter       = 0;
+    p->on_ground        = 0;
+    p->player_par       = 0;
+    p->return_to_center = 9;
+    p->airleft          = 15*26;
+    p->rapid_fire_hold  = 0;
+    p->toggle_key_flag  = 0;
+    p->access_spritenum = -1;
+    if(ud.multimode > 1 && ud.coop != 1 )
+        p->got_access = 7;
+    else p->got_access      = 0;
+    p->random_club_frame= 0;
+    pus = 1;
+    p->on_warping_sector = 0;
+    p->spritebridge      = 0;
+    p->palette = (char *) &palette[0];
+
+    if(p->steroids_amount < 400 )
+    {
+        p->steroids_amount = 0;
+        p->inven_icon = 0;
+    }
+    p->heat_on =            0;
+    p->jetpack_on =         0;
+    p->holoduke_on =       -1;
+
+    p->look_ang          = 512 - ((ud.level_number&1)<<10);
+
+    p->rotscrnang        = 0;
+    p->newowner          =-1;
+    p->jumping_counter   = 0;
+    p->hard_landing      = 0;
+    p->posxv             = 0;
+    p->posyv             = 0;
+    p->poszv             = 0;
+    fricxv            = 0;
+    fricyv            = 0;
+    p->somethingonplayer =-1;
+    p->one_eighty_count  = 0;
+    p->cheat_phase       = 0;
+
+    p->on_crane          = -1;
+
+    if(p->curr_weapon == PISTOL_WEAPON)
+        p->kickback_pic  = 5;
+    else p->kickback_pic = 0;
+
+    p->weapon_pos        = 6;
+    p->walking_snd_toggle= 0;
+    p->weapon_ang        = 0;
+
+    p->knuckle_incs      = 1;
+    p->fist_incs = 0;
+    p->knee_incs         = 0;
+    p->jetpack_on        = 0;
+    setpal(p);
+	p->weaponautoswitch	 = 0;
+	p->auto_aim          = 2;
+	p->fakeplayer        = 0;
+}
+
+
+
+void resetweapons(short snum)
+{
+    short  weapon;
+    struct player_struct *p;
+
+    p = &ps[snum];
+
+    for ( weapon = PISTOL_WEAPON; weapon < MAX_WEAPONS; weapon++ )
+        p->gotweapon[weapon] = 0;
+    for ( weapon = PISTOL_WEAPON; weapon < MAX_WEAPONS; weapon++ )
+        p->ammo_amount[weapon] = 0;
+
+    p->weapon_pos = 6;
+    p->kickback_pic = 5;
+    p->curr_weapon = PISTOL_WEAPON;
+    p->gotweapon[PISTOL_WEAPON] = 1;
+    p->gotweapon[KNEE_WEAPON] = 1;
+    p->ammo_amount[PISTOL_WEAPON] = 48;
+    p->gotweapon[HANDREMOTE_WEAPON] = 1;
+    p->last_weapon = -1;
+
+    p->show_empty_weapon= 0;
+    p->last_pissed_time = 0;
+    p->holster_weapon = 0;
+}
+
+void resetinventory(short snum)
+{
+    struct player_struct *p;
+    short i;
+
+    p = &ps[snum];
+
+    p->inven_icon       = 0;
+    p->boot_amount = 0;
+    p->scuba_on =           0;p->scuba_amount =         0;
+    p->heat_amount        = 0;p->heat_on = 0;
+    p->jetpack_on =         0;p->jetpack_amount =       0;
+    p->shield_amount =      max_armour_amount;
+    p->holoduke_on = -1;
+    p->holoduke_amount =    0;
+    p->firstaid_amount = 0;
+    p->steroids_amount = 0;
+    p->inven_icon = 0;
+}
+
+
+void resetprestat(short snum,char g)
+{
+    struct player_struct *p;
+    short i;
+
+    p = &ps[snum];
+
+    spriteqloc = 0;
+    for(i=0;i<spriteqamount;i++) spriteq[i] = -1;
+
+    p->hbomb_on          = 0;
+    p->cheat_phase       = 0;
+    p->pals_time         = 0;
+    p->toggle_key_flag   = 0;
+    p->secret_rooms      = 0;
+    p->max_secret_rooms  = 0;
+    p->actors_killed     = 0;
+    p->max_actors_killed = 0;
+    p->lastrandomspot = 0;
+    p->weapon_pos = 6;
+    p->kickback_pic = 5;
+    p->last_weapon = -1;
+    p->weapreccnt = 0;
+    p->show_empty_weapon= 0;
+    p->holster_weapon = 0;
+    p->last_pissed_time = 0;
+
+    p->one_parallax_sectnum = -1;
+    p->visibility = ud.const_visibility;
+
+    screenpeek              = myconnectindex;
+    numanimwalls            = 0;
+    numcyclers              = 0;
+    animatecnt              = 0;
+    parallaxtype            = 0;
+    randomseed              = 17L;
+    ud.pause_on             = 0;
+    ud.camerasprite         =-1;
+    ud.eog                  = 0;
+    tempwallptr             = 0;
+    camsprite               =-1;
+    earthquaketime          = 0;
+
+    numinterpolations = 0;
+    startofdynamicinterpolations = 0;
+
+    if( ( (g&MODE_EOL) != MODE_EOL && numplayers < 2) || (ud.coop != 1 && numplayers > 1) )
+    {
+        resetweapons(snum);
+        resetinventory(snum);
+    }
+    else if(p->curr_weapon == HANDREMOTE_WEAPON)
+    {
+        p->ammo_amount[HANDBOMB_WEAPON]++;
+        p->curr_weapon = HANDBOMB_WEAPON;
+    }
+
+    p->timebeforeexit   = 0;
+    p->customexitsound  = 0;
+
+}
+
+void setupbackdrop(short sky)
+{
+    short i;
+
+    for(i=0;i<MAXPSKYTILES;i++) pskyoff[i]=0;
+
+    if(parallaxyscale != 65536L)
+        parallaxyscale = 32768;
+
+    switch(sky)
+    {
+        case CLOUDYOCEAN:
+            parallaxyscale = 65536L;
+            break;
+        case MOONSKY1 :
+            pskyoff[6]=1; pskyoff[1]=2; pskyoff[4]=2; pskyoff[2]=3;
+            break;
+        case BIGORBIT1: // orbit
+            pskyoff[5]=1; pskyoff[6]=2; pskyoff[7]=3; pskyoff[2]=4;
+            break;
+        case LA:
+            parallaxyscale = 16384+1024;
+            pskyoff[0]=1; pskyoff[1]=2; pskyoff[2]=1; pskyoff[3]=3;
+            pskyoff[4]=4; pskyoff[5]=0; pskyoff[6]=2; pskyoff[7]=3;
+            break;
+   }
+
+   pskybits=3;
+}
+
+void prelevel(char g)
+{
+    short i, nexti, j, startwall, endwall, lotaglist;
+    short lotags[65];
+
+
+    clearbufbyte(show2dsector,sizeof(show2dsector),0L);
+    clearbufbyte(show2dwall,sizeof(show2dwall),0L);
+    clearbufbyte(show2dsprite,sizeof(show2dsprite),0L);
+
+    resetprestat(0,g);
+    numclouds = 0;
+
+    for(i=0;i<numsectors;i++)
+    {
+        sector[i].extra = 256;
+
+        switch(sector[i].lotag)
+        {
+            case 20:
+            case 22:
+                if( sector[i].floorz > sector[i].ceilingz)
+                    sector[i].lotag |= 32768;
+                continue;
+        }
+
+        if(sector[i].ceilingstat&1)
+        {
+            if(waloff[sector[i].ceilingpicnum] == 0)
+            {
+                if(sector[i].ceilingpicnum == LA)
+                    for(j=0;j<5;j++)
+                        if(waloff[sector[i].ceilingpicnum+j] == 0)
+                            tloadtile(sector[i].ceilingpicnum+j);
+            }
+            setupbackdrop(sector[i].ceilingpicnum);
+
+            if(sector[i].ceilingpicnum == CLOUDYSKIES && numclouds < 127)
+                clouds[numclouds++] = i;
+
+            if(ps[0].one_parallax_sectnum == -1)
+                ps[0].one_parallax_sectnum = i;
+        }
+
+        if(sector[i].lotag == 32767) //Found a secret room
+        {
+            ps[0].max_secret_rooms++;
+            continue;
+        }
+
+        if(sector[i].lotag == -1)
+        {
+            ps[0].exitx = wall[sector[i].wallptr].x;
+            ps[0].exity = wall[sector[i].wallptr].y;
+            continue;
+        }
+    }
+
+    i = headspritestat[0];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+
+        if(sprite[i].lotag == -1 && (sprite[i].cstat&16) )
+        {
+            ps[0].exitx = SX;
+            ps[0].exity = SY;
+        }
+        else switch(PN)
+        {
+            case GPSPEED:
+                sector[SECT].extra = SLT;
+                deletesprite(i);
+                break;
+
+            case CYCLER:
+                if(numcyclers >= MAXCYCLERS)
+                    gameexit("\nToo many cycling sectors.");
+                cyclers[numcyclers][0] = SECT;
+                cyclers[numcyclers][1] = SLT;
+                cyclers[numcyclers][2] = SS;
+                cyclers[numcyclers][3] = sector[SECT].floorshade;
+                cyclers[numcyclers][4] = SHT;
+                cyclers[numcyclers][5] = (SA == 1536);
+                numcyclers++;
+                deletesprite(i);
+                break;
+        }
+        i = nexti;
+    }
+
+    for(i=0;i < MAXSPRITES;i++)
+    {
+        if(sprite[i].statnum < MAXSTATUS)
+        {
+            if(PN == SECTOREFFECTOR && SLT == 14)
+                continue;
+            spawn(-1,i);
+        }
+    }
+
+    for(i=0;i < MAXSPRITES;i++)
+        if(sprite[i].statnum < MAXSTATUS)
+        {
+            if( PN == SECTOREFFECTOR && SLT == 14 )
+                spawn(-1,i);
+        }
+
+    lotaglist = 0;
+
+    i = headspritestat[0];
+    while(i >= 0)
+    {
+        switch(PN)
+        {
+            case DIPSWITCH:
+            case DIPSWITCH2:
+            case ACCESSSWITCH:
+            case PULLSWITCH:
+            case HANDSWITCH:
+            case SLOTDOOR:
+            case LIGHTSWITCH:
+            case SPACELIGHTSWITCH:
+            case SPACEDOORSWITCH:
+            case FRANKENSTINESWITCH:
+            case LIGHTSWITCH2:
+            case POWERSWITCH1:
+            case LOCKSWITCH1:
+            case POWERSWITCH2:
+                break;
+            case DIPSWITCH+1:
+            case DIPSWITCH2+1:
+            case PULLSWITCH+1:
+            case HANDSWITCH+1:
+            case SLOTDOOR+1:
+            case LIGHTSWITCH+1:
+            case SPACELIGHTSWITCH+1:
+            case SPACEDOORSWITCH+1:
+            case FRANKENSTINESWITCH+1:
+            case LIGHTSWITCH2+1:
+            case POWERSWITCH1+1:
+            case LOCKSWITCH1+1:
+            case POWERSWITCH2+1:
+                for(j=0;j<lotaglist;j++)
+                    if( SLT == lotags[j] )
+                        break;
+
+                if( j == lotaglist )
+                {
+                    lotags[lotaglist] = SLT;
+                    lotaglist++;
+                    if(lotaglist > 64)
+                        gameexit("\nToo many switches (64 max).");
+
+                    j = headspritestat[3];
+                    while(j >= 0)
+                    {
+                        if(sprite[j].lotag == 12 && sprite[j].hitag == SLT)
+                            hittype[j].temp_data[0] = 1;
+                        j = nextspritestat[j];
+                    }
+                }
+                break;
+        }
+        i = nextspritestat[i];
+    }
+
+    mirrorcnt = 0;
+
+    for( i = 0; i < numwalls; i++ )
+    {
+        walltype *wal;
+        wal = &wall[i];
+
+        if(wal->overpicnum == MIRROR && (wal->cstat&32) != 0)
+        {
+            j = wal->nextsector;
+
+            if(mirrorcnt > 63)
+                gameexit("\nToo many mirrors (64 max.)");
+            if ( (j >= 0) && sector[j].ceilingpicnum != MIRROR )
+            {
+                sector[j].ceilingpicnum = MIRROR;
+                sector[j].floorpicnum = MIRROR;
+                mirrorwall[mirrorcnt] = i;
+                mirrorsector[mirrorcnt] = j;
+                mirrorcnt++;
+                continue;
+            }
+        }
+
+        if(numanimwalls >= MAXANIMWALLS)
+            gameexit("\nToo many 'anim' walls (max 512.)");
+
+        animwall[numanimwalls].tag = 0;
+        animwall[numanimwalls].wallnum = 0;
+
+        switch(wal->overpicnum)
+        {
+            case FANSHADOW:
+            case FANSPRITE:
+                wall->cstat |= 65;
+                animwall[numanimwalls].wallnum = i;
+                numanimwalls++;
+                break;
+
+            case W_FORCEFIELD:
+                if(waloff[W_FORCEFIELD] == 0)
+                    for(j=0;j<3;j++)
+                        tloadtile(W_FORCEFIELD+j);
+            case W_FORCEFIELD+1:
+            case W_FORCEFIELD+2:
+                if(wal->shade > 31)
+                    wal->cstat = 0;
+                else wal->cstat |= 85+256;
+
+
+                if(wal->lotag && wal->nextwall >= 0)
+                    wall[wal->nextwall].lotag =
+                        wal->lotag;
+
+            case BIGFORCE:
+
+                animwall[numanimwalls].wallnum = i;
+                numanimwalls++;
+
+                continue;
+        }
+
+        wal->extra = -1;
+
+        switch(wal->picnum)
+        {
+            case WATERTILE2:
+                for(j=0;j<3;j++)
+                    if(waloff[wal->picnum+j] == 0)
+                        tloadtile(wal->picnum+j);
+                break;
+
+            case TECHLIGHT2:
+            case TECHLIGHT4:
+                if(waloff[wal->picnum] == 0)
+                    tloadtile(wal->picnum);
+                break;
+            case W_TECHWALL1:
+            case W_TECHWALL2:
+            case W_TECHWALL3:
+            case W_TECHWALL4:
+                animwall[numanimwalls].wallnum = i;
+//                animwall[numanimwalls].tag = -1;
+                numanimwalls++;
+                break;
+            case SCREENBREAK6:
+            case SCREENBREAK7:
+            case SCREENBREAK8:
+                if(waloff[SCREENBREAK6] == 0)
+                    for(j=SCREENBREAK6;j<SCREENBREAK9;j++)
+                        tloadtile(j);
+                animwall[numanimwalls].wallnum = i;
+                animwall[numanimwalls].tag = -1;
+                numanimwalls++;
+                break;
+
+            case FEMPIC1:
+            case FEMPIC2:
+            case FEMPIC3:
+
+                wal->extra = wal->picnum;
+                animwall[numanimwalls].tag = -1;
+                if(ud.lockout)
+                {
+                    if(wal->picnum == FEMPIC1)
+                        wal->picnum = BLANKSCREEN;
+                    else wal->picnum = SCREENBREAK6;
+                }
+
+                animwall[numanimwalls].wallnum = i;
+                animwall[numanimwalls].tag = wal->picnum;
+                numanimwalls++;
+                break;
+
+            case SCREENBREAK1:
+            case SCREENBREAK2:
+            case SCREENBREAK3:
+            case SCREENBREAK4:
+            case SCREENBREAK5:
+
+            case SCREENBREAK9:
+            case SCREENBREAK10:
+            case SCREENBREAK11:
+            case SCREENBREAK12:
+            case SCREENBREAK13:
+            case SCREENBREAK14:
+            case SCREENBREAK15:
+            case SCREENBREAK16:
+            case SCREENBREAK17:
+            case SCREENBREAK18:
+            case SCREENBREAK19:
+
+                animwall[numanimwalls].wallnum = i;
+                animwall[numanimwalls].tag = wal->picnum;
+                numanimwalls++;
+                break;
+        }
+    }
+
+    //Invalidate textures in sector behind mirror
+    for(i=0;i<mirrorcnt;i++)
+    {
+        startwall = sector[mirrorsector[i]].wallptr;
+        endwall = startwall + sector[mirrorsector[i]].wallnum;
+        for(j=startwall;j<endwall;j++)
+        {
+            wall[j].picnum = MIRROR;
+            wall[j].overpicnum = MIRROR;
+        }
+    }
+}
+
+
+void newgame(char vn,char ln,char sk)
+{
+    struct player_struct *p = &ps[0];
+    short i;
+
+    if(globalskillsound >= 0)
+        while(Sound[globalskillsound].lock>=200);
+    globalskillsound = -1;
+
+    waitforeverybody();
+    ready2send = 0;
+
+    if( ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1)
+        dobonus(1);
+
+	if (ud.showcinematics)
+		if( ln == 0 && vn == 3 && ud.multimode < 2 && ud.lockout == 0)
+		{
+			playmusic(&env_music_fn[1][0]);
+
+			flushperms();
+			setview(0,0,xdim-1,ydim-1);
+			clearview(0L);
+			nextpage();
+
+			playanm("vol41a.anm",6);
+			clearview(0L);
+			nextpage();
+			playanm("vol42a.anm",7);
+	//        clearview(0L);
+	  //      nextpage();
+			playanm("vol43a.anm",9);
+			clearview(0L);
+			nextpage();
+
+			FX_StopAllSounds();
+		}
+
+    show_shareware = 26*34;
+
+    ud.level_number =   ln;
+    ud.volume_number =  vn;
+    ud.player_skill =   sk;
+    ud.secretlevel =    0;
+    ud.from_bonus = 0;
+    parallaxyscale = 0;
+
+    ud.last_level = -1;
+    lastsavedpos = -1;
+    p->zoom            = 768;
+    p->gm              = 0;
+
+    if(ud.m_coop != 1)
+    {
+        p->curr_weapon = PISTOL_WEAPON;
+        p->gotweapon[PISTOL_WEAPON] = 1;
+        p->gotweapon[KNEE_WEAPON] = 1;
+        p->ammo_amount[PISTOL_WEAPON] = 48;
+        p->gotweapon[HANDREMOTE_WEAPON] = 1;
+        p->last_weapon = -1;
+    }
+
+    display_mirror =        0;
+
+    if(ud.multimode > 1 )
+    {
+        if(numplayers < 2)
+        {
+            connecthead = 0;
+            for(i=0;i<MAXPLAYERS;i++) connectpoint2[i] = i+1;
+            connectpoint2[ud.multimode-1] = -1;
+        }
+    }
+    else
+    {
+        connecthead = 0;
+        connectpoint2[0] = -1;
+    }
+}
+
+
+void resetpspritevars(char g)
+{
+    short i, j, nexti,circ;
+    long firstx,firsty;
+    spritetype *s;
+    char aimmode[MAXPLAYERS];
+    STATUSBARTYPE tsbar[MAXPLAYERS];
+
+	#define BOT_MAX_NAME 20
+	int bot_used[BOT_MAX_NAME] = { false };
+	char *bot_names[] = {	"* ELASTI", 
+							"* ^ZookeM^",
+							"* DOOM",
+							"* DRO",
+							"* NOX",
+							"* EXP",
+							"* SKIPPY",
+							"* BRYZIAN",
+							"* ALI-GUN",
+							"* ADDFAZ",
+							"* TURRICAN",
+							"* PODA",
+							"* EWOLF",
+							"* SOULIANE",
+							"* SPANATOR",
+							"* OVERLORD",
+							"* COWBOYUK",
+							"* JAKS",
+							"* BLUEDRAG",
+							"* MOE{GER}"};
+
+
+    EGS(ps[0].cursectnum,ps[0].posx,ps[0].posy,ps[0].posz,
+        APLAYER,0,0,0,ps[0].ang,0,0,0,10);
+
+    if(ud.recstat != 2) for(i=0;i<MAXPLAYERS;i++)
+    {
+        aimmode[i] = ps[i].aim_mode;
+        if(ud.multimode > 1 && ud.coop == 1 && ud.last_level >= 0)
+        {
+            for(j=0;j<MAX_WEAPONS;j++)
+            {
+                tsbar[i].ammo_amount[j] = ps[i].ammo_amount[j];
+                tsbar[i].gotweapon[j] = ps[i].gotweapon[j];
+            }
+
+            tsbar[i].shield_amount = ps[i].shield_amount;
+            tsbar[i].curr_weapon = ps[i].curr_weapon;
+            tsbar[i].inven_icon = ps[i].inven_icon;
+
+            tsbar[i].firstaid_amount = ps[i].firstaid_amount;
+            tsbar[i].steroids_amount = ps[i].steroids_amount;
+            tsbar[i].holoduke_amount = ps[i].holoduke_amount;
+            tsbar[i].jetpack_amount = ps[i].jetpack_amount;
+            tsbar[i].heat_amount = ps[i].heat_amount;
+            tsbar[i].scuba_amount = ps[i].scuba_amount;
+            tsbar[i].boot_amount = ps[i].boot_amount;
+        }
+    }
+
+    resetplayerstats(0); // reset a player 
+
+    for(i=1;i<MAXPLAYERS;i++) // reset all the others
+       memcpy(&ps[i],&ps[0],sizeof(ps[0]));
+
+	// FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v27/28/29/116/117/118. New: v30/v119.
+	if(numplayers<2 && !(g&MODE_DEMO))
+		memcpy(ud.wchoice[0],ud.mywchoice,sizeof(ud.mywchoice)); 
+
+	if(g&MODE_DEMO &&	(ud.playing_demo_rev == BYTEVERSION_27 ||
+						ud.playing_demo_rev == BYTEVERSION_28 || 
+						ud.playing_demo_rev == BYTEVERSION_29 ||
+						ud.playing_demo_rev == BYTEVERSION_116 ||
+						ud.playing_demo_rev == BYTEVERSION_117 ||
+						ud.playing_demo_rev == BYTEVERSION_118 ))
+		for(i=0; i<ud.multimode; i++)
+			memcpy(ud.wchoice[i],ud.mywchoice,sizeof(ud.mywchoice)); // assuming.... :-(
+
+	// FIX_00076: Added default names for bots + fixed a "killed <name>" bug in Fakeplayers with AI
+	if(!(g&MODE_DEMO) && numplayers<2 && ud.multimode > 1)
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if (i!=myconnectindex)
+			{
+				memcpy(ud.wchoice[0],ud.mywchoice,sizeof(ud.mywchoice));
+
+				//	add bot's names
+				do 
+				{ 
+					j = rand()%BOT_MAX_NAME; 
+				} 
+				while(bot_used[j]);
+
+				strcpy(ud.user_name[i], bot_names[j]);
+				bot_used[j] = true;
+				ps[i].fakeplayer = 1 + ud.playerai;  // = true if fakerplayer. (==2 if AI)
+			}
+
+    if(ud.recstat != 2) for(i=0;i<MAXPLAYERS;i++)
+    {
+        ps[i].aim_mode = aimmode[i];
+        if(ud.multimode > 1 && ud.coop == 1 && ud.last_level >= 0)
+        {
+            for(j=0;j<MAX_WEAPONS;j++)
+            {
+                ps[i].ammo_amount[j] = tsbar[i].ammo_amount[j];
+                ps[i].gotweapon[j] = tsbar[i].gotweapon[j];
+            }
+            ps[i].shield_amount = tsbar[i].shield_amount;
+            ps[i].curr_weapon = tsbar[i].curr_weapon;
+            ps[i].inven_icon = tsbar[i].inven_icon;
+
+            ps[i].firstaid_amount = tsbar[i].firstaid_amount;
+            ps[i].steroids_amount= tsbar[i].steroids_amount;
+            ps[i].holoduke_amount = tsbar[i].holoduke_amount;
+            ps[i].jetpack_amount = tsbar[i].jetpack_amount;
+            ps[i].heat_amount = tsbar[i].heat_amount;
+            ps[i].scuba_amount= tsbar[i].scuba_amount;
+            ps[i].boot_amount = tsbar[i].boot_amount;
+        }
+    }
+
+    numplayersprites = 0;
+    circ = 2048/ud.multimode;
+
+    which_palookup = 9;
+    j = connecthead;
+    i = headspritestat[10];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+        s = &sprite[i];
+
+        if( numplayersprites == MAXPLAYERS)
+            gameexit("\nToo many player sprites (max 16.)");
+
+        if(numplayersprites == 0)
+        {
+            firstx = ps[0].posx;
+            firsty = ps[0].posy;
+        }
+
+        po[numplayersprites].ox = s->x;
+        po[numplayersprites].oy = s->y;
+        po[numplayersprites].oz = s->z;
+        po[numplayersprites].oa = s->ang;
+        po[numplayersprites].os = s->sectnum;
+
+        numplayersprites++;
+        if(j >= 0)
+        {
+            s->owner = i;
+            s->shade = 0;
+            s->xrepeat = 42;
+            s->yrepeat = 36;
+            s->cstat = 1+256;
+            s->xoffset = 0;
+            s->clipdist = 64;
+
+            if( (g&MODE_EOL) != MODE_EOL || ps[j].last_extra == 0)
+            {
+                ps[j].last_extra = max_player_health;
+                s->extra = max_player_health;
+            }
+            else s->extra = ps[j].last_extra;
+
+            s->yvel = j;
+
+            if(s->pal == 0)
+            {
+                s->pal = ps[j].palookup = which_palookup;
+                which_palookup++;
+                if( which_palookup >= 17 ) which_palookup = 9;
+            }
+            else ps[j].palookup = s->pal;
+
+            ps[j].i = i;
+            ps[j].frag_ps = j;
+            hittype[i].owner = i;
+
+            hittype[i].bposx = ps[j].bobposx = ps[j].oposx = ps[j].posx =        s->x;
+            hittype[i].bposy = ps[j].bobposy = ps[j].oposy = ps[j].posy =        s->y;
+            hittype[i].bposz = ps[j].oposz = ps[j].posz =        s->z;
+            ps[j].oang  = ps[j].ang  =        s->ang;
+
+            updatesector(s->x,s->y,&ps[j].cursectnum);
+
+            j = connectpoint2[j];
+
+        }
+        else deletesprite(i);
+        i = nexti;
+    }
+}
+
+void clearfrags(void)
+{
+    short i;
+
+    for(i = 0;i<MAXPLAYERS;i++)
+        ps[i].frag = ps[i].fraggedself = 0;
+     clearbufbyte(&frags[0][0],(MAXPLAYERS*MAXPLAYERS)<<1,0L);
+}
+
+void resettimevars(void)
+{
+    vel = svel = angvel = horiz = 0;
+
+    totalclock = 0L;
+    cloudtotalclock = 0L;
+    ototalclock = 0L;
+    lockclock = 0L;
+    ready2send = 1;
+}
+
+
+void genspriteremaps(void)
+{
+    long j,fp;
+    signed char look_pos;
+    char *lookfn = "lookup.dat";
+    char numl;
+
+    fp = TCkopen4load(lookfn,0);
+    if(fp != -1)
+        kread(fp,(char *)&numl,1);
+    else
+        gameexit("\nERROR: File 'LOOKUP.DAT' not found.");
+
+    for(j=0;j < numl;j++)
+    {
+        kread(fp,(signed char *)&look_pos,1);
+        kread(fp,tempbuf,256);
+        makepalookup((long)look_pos,tempbuf,0,0,0,1);
+    }
+
+    kread(fp,&waterpal[0],768);
+    kread(fp,&slimepal[0],768);
+    kread(fp,&titlepal[0],768);
+    kread(fp,&drealms[0],768);
+    kread(fp,&endingpal[0],768);
+
+    palette[765] = palette[766] = palette[767] = 0;
+    slimepal[765] = slimepal[766] = slimepal[767] = 0;
+    waterpal[765] = waterpal[766] = waterpal[767] = 0;
+
+    kclose(fp);
+}
+
+void waitforeverybody()
+{
+    long i;
+
+    if (numplayers < 2) 
+	{
+		//printf("numplayers < 2 (num: %d)\n", numplayers);
+		return;
+	}
+#ifdef _DEBUG_NETWORKING_
+	else
+	{
+		printf("numplayers == %d\n", numplayers);
+	}
+#endif
+
+	printf("waitforeverybody()\n");
+
+    packbuf[0] = 250;
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+    {
+
+        if (i != myconnectindex)
+		{
+			sendpacket(i,packbuf,1);
+		}
+    }
+
+    playerreadyflag[myconnectindex]++;
+    do
+    {
+        _idle();  /* let input queue run... */
+        getpackets();
+        for(i=connecthead;i>=0;i=connectpoint2[i])
+        {
+            if (playerreadyflag[i] < playerreadyflag[myconnectindex])
+			{
+				break;
+			}
+        }
+    } while (i >= 0);
+
+}
+
+void dofrontscreens(void)
+{
+    long tincs,i,j;
+
+    if(ud.recstat != 2)
+    {
+        ps[myconnectindex].palette = palette;
+        for(j=0;j<63;j+=7) palto(0,0,0,j);
+        i = ud.screen_size;
+        ud.screen_size = 0;
+        vscrn();
+        clearview(0L);
+
+        rotatesprite(320<<15,200<<15,65536L,0,LOADSCREEN,0,0,2+8+64,0,0,xdim-1,ydim-1);
+
+        if( boardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0 )
+        {
+            menutext(160,90,0,0,"ENTERING USER MAP");
+            gametextpal(160,90+10,boardfilename,14,2);
+        }
+        else
+        {
+            menutext(160,90,0,0,"ENTERING");
+            menutext(160,90+16+8,0,0,level_names[(ud.volume_number*11) + ud.level_number]);
+        }
+
+        nextpage();
+
+        for(j=63;j>0;j-=7) palto(0,0,0,j);
+
+        KB_FlushKeyboardQueue();
+        ud.screen_size = i;
+    }
+    else
+    {
+        clearview(0L);
+        ps[myconnectindex].palette = palette;
+        palto(0,0,0,0);
+        rotatesprite(320<<15,200<<15,65536L,0,LOADSCREEN,0,0,2+8+64,0,0,xdim-1,ydim-1);
+        menutext(160,105,0,0,"LOADING...");
+        nextpage();
+    }
+}
+
+void clearfifo(void)
+{
+    syncvaltail = 0L;
+    syncvaltottail = 0L;
+    syncstat = 0;
+    bufferjitter = 1;
+    mymaxlag = otherminlag = 0;
+
+    movefifoplc = movefifosendplc = fakemovefifoplc = 0;
+    avgfvel = avgsvel = avgavel = avghorz = avgbits = 0;
+    otherminlag = mymaxlag = 0;
+
+    clearbufbyte(myminlag,MAXPLAYERS<<2,0L);
+    clearbufbyte(&loc,sizeof(input),0L);
+    clearbufbyte(&sync[0],sizeof(sync),0L);
+    clearbufbyte(inputfifo,sizeof(input)*MOVEFIFOSIZ*MAXPLAYERS,0L);
+
+    clearbuf(movefifoend,MAXPLAYERS,0L);
+    clearbuf(syncvalhead,MAXPLAYERS,0L);
+    clearbuf(myminlag,MAXPLAYERS,0L);
+
+//    clearbufbyte(playerquitflag,MAXPLAYERS,0x01);
+}
+
+void resetmys(void)
+{
+      myx = omyx = ps[myconnectindex].posx;
+      myy = omyy = ps[myconnectindex].posy;
+      myz = omyz = ps[myconnectindex].posz;
+      myxvel = myyvel = myzvel = 0;
+      myang = omyang = ps[myconnectindex].ang;
+      myhoriz = omyhoriz = ps[myconnectindex].horiz;
+      myhorizoff = omyhorizoff = ps[myconnectindex].horizoff;
+      mycursectnum = ps[myconnectindex].cursectnum;
+      myjumpingcounter = ps[myconnectindex].jumping_counter;
+      myjumpingtoggle = ps[myconnectindex].jumping_toggle;
+      myonground = ps[myconnectindex].on_ground;
+      myhardlanding = ps[myconnectindex].hard_landing;
+      myreturntocenter = ps[myconnectindex].return_to_center;
+}
+
+void enterlevel(char g)
+{
+    short i,j;
+    long l;
+    char levname[256];
+	char fulllevelfilename[512];
+
+
+	KB_ClearKeyDown(sc_Pause); // avoid entering in pause mode.
+	
+    if( (g&MODE_DEMO) != MODE_DEMO ) ud.recstat = ud.m_recstat;
+    ud.respawn_monsters = ud.m_respawn_monsters;
+    ud.respawn_items    = ud.m_respawn_items;
+    ud.respawn_inventory    = ud.m_respawn_inventory;
+    ud.monsters_off = ud.m_monsters_off;
+    ud.coop = ud.m_coop;
+    ud.marker = ud.m_marker;
+    ud.ffire = ud.m_ffire;
+
+    if( (g&MODE_DEMO) == 0 && ud.recstat == 2)
+        ud.recstat = 0;
+
+    FX_StopAllSounds();
+    clearsoundlocks();
+    FX_SetReverb(0);
+
+    i = ud.screen_size;
+    ud.screen_size = 0;
+    dofrontscreens();
+    vscrn();
+    ud.screen_size = i;
+
+if (!VOLUMEONE)
+{
+    if( boardfilename[0] != 0 && ud.m_level_number == 7 && ud.m_volume_number == 0 )
+    {
+		sprintf(fulllevelfilename, "%s\\%s",  game_dir, boardfilename);
+		if(!SafeFileExists(fulllevelfilename))
+		{
+			sprintf(fulllevelfilename, "%s", boardfilename);
+		}
+
+        if ( loadboard( fulllevelfilename,&ps[0].posx, &ps[0].posy, &ps[0].posz, &ps[0].ang,&ps[0].cursectnum ) == -1 )
+        {
+            sprintf(tempbuf,"User Map %s not found!\n",fulllevelfilename);
+            gameexit(tempbuf);
+        }
+    }
+    else
+    {
+		sprintf(fulllevelfilename, "%s\\%s",  game_dir, level_file_names[ (ud.volume_number*11)+ud.level_number]);
+		if(!SafeFileExists(fulllevelfilename))
+		{
+			sprintf(fulllevelfilename, "%s", level_file_names[ (ud.volume_number*11)+ud.level_number]);
+		}
+		printf("filename=%s\n",fulllevelfilename );
+		if ( loadboard(fulllevelfilename ,&ps[0].posx, &ps[0].posy, &ps[0].posz, &ps[0].ang,&ps[0].cursectnum ) == -1)
+        {
+			sprintf(tempbuf,"Internal Map %s not found! Not using the right grp file?\n",level_file_names[(ud.volume_number*11)+ud.level_number]);
+			gameexit(tempbuf);
+		}
+    }
+
+} else {
+
+    l = strlen(level_file_names[ (ud.volume_number*11)+ud.level_number]);
+    copybufbyte( level_file_names[ (ud.volume_number*11)+ud.level_number],&levname[0],l);
+    levname[l] = 0; 
+
+	printf("levname=%s\n",levname );
+
+    if ( (ud.volume_number > 1) || loadboard( levname,&ps[0].posx, &ps[0].posy, &ps[0].posz, &ps[0].ang,&ps[0].cursectnum ) == -1)
+    {
+        sprintf(tempbuf,"Internal Map %s not found in Shareware grp pack!\n",level_file_names[(ud.volume_number*11)+ud.level_number]);
+        gameexit(tempbuf);
+    }
+}
+
+    clearbufbyte(gotpic,sizeof(gotpic),0L);
+
+    prelevel(g);
+
+    allignwarpelevators();
+    resetpspritevars(g);
+
+#ifdef CHECK_XDUKE_REV
+	if((g&MODE_DEMO) != MODE_DEMO ) // don't check for demo
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+		{
+			// all the players must have been validated to have the same rev as we do
+			if(!ud.rev[i][0] && !ps[i].fakeplayer)
+				Error(EXIT_SUCCESS, "Your opponent [%s] is using an unknown version of xDuke."
+									"You are using v%d.%d\n",
+				ud.user_name[i], XDUKE_REV_X, XDUKE_REV_DOT_Y);
+		}
+#endif
+
+    cachedebug = 0;
+    automapping = 0;
+
+    if(ud.recstat != 2) MUSIC_StopSong();
+
+    cacheit();
+    docacheit();
+
+    if(ud.recstat != 2)
+    {
+        music_select = (ud.volume_number*11) + ud.level_number;
+        playmusic(&music_fn[0][music_select][0]);
+    }
+
+    if( (g&MODE_GAME) || (g&MODE_EOL) )
+        ps[myconnectindex].gm = MODE_GAME;
+    else if(g&MODE_RESTART)
+    {
+        if(ud.recstat == 2)
+            ps[myconnectindex].gm = MODE_DEMO;
+        else ps[myconnectindex].gm = MODE_GAME;
+    }
+
+    if( (ud.recstat == 1) && (g&MODE_RESTART) != MODE_RESTART )
+        opendemowrite();
+
+//if (VOLUMEONE)
+//    if(ud.level_number == 0 && ud.recstat != 2) FTA(40,&ps[myconnectindex]);
+
+#ifdef CHECK_XDUKE_REV
+	sprintf(fta_quotes[103],"xDuke v%d.%d", ud.rev[myconnectindex][2], ud.rev[myconnectindex][3]);
+	FTA(103,&ps[myconnectindex],1);
+#endif
+
+	if(ud.auto_aim==1 && ud.recstat != 2)
+	{
+		sprintf(fta_quotes[103],"Autoaim set to Bullet only");
+		FTA(103,&ps[myconnectindex],1);
+	}
+
+	if(nHostForceDisableAutoaim && ud.recstat != 2)
+	{
+		sprintf(fta_quotes[103],"Autoaim disabled by host");
+		FTA(103,&ps[myconnectindex],1);
+	}
+	
+
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        switch(sector[sprite[ps[i].i].sectnum].floorpicnum)
+        {
+            case HURTRAIL:
+            case FLOORSLIME:
+            case FLOORPLASMA:
+                resetweapons(i);
+                resetinventory(i);
+                ps[i].gotweapon[PISTOL_WEAPON] = 0;
+                ps[i].ammo_amount[PISTOL_WEAPON] = 0;
+                ps[i].curr_weapon = KNEE_WEAPON;
+                ps[i].kickback_pic = 0;
+                break;
+        }
+
+      //PREMAP.C - replace near the my's at the end of the file
+
+     resetmys();
+
+     ps[myconnectindex].palette = palette;
+     palto(0,0,0,0);
+
+     setpal(&ps[myconnectindex]);
+     flushperms();
+
+     everyothertime = 0;
+     global_random = 0;
+
+     ud.last_level = ud.level_number+1;
+
+     clearfifo();
+
+     for(i=numinterpolations-1;i>=0;i--) bakipos[i] = *curipos[i];
+
+     restorepalette = 1;
+
+     flushpackets();
+     waitforeverybody();
+
+     palto(0,0,0,0);
+     vscrn();
+     clearview(0L);
+     drawbackground();
+
+     clearbufbyte(playerquitflag,MAXPLAYERS,0x01010101);
+     ps[myconnectindex].over_shoulder_on = 0;
+
+     clearfrags();
+
+     resettimevars();  // Here we go
+
+	 if(numplayers > 1) 
+	 {
+		buf[0] = 132;	// xDuke TAG ID
+        buf[1] = mapCRC & 0xFF;
+		buf[2] = (mapCRC>>8) & 0xFF;
+
+		for(i=connecthead;i>=0;i=connectpoint2[i])
+			if( i != myconnectindex )
+				sendpacket(i,&buf[0],3);
+	 }
+
+	 ud.mapCRC[myconnectindex] = mapCRC;
+
+}
+
+/*
+Duke Nukem V
+
+Layout:
+
+      Settings:
+        Suburbs
+          Duke inflitrating neighborhoods inf. by aliens
+        Death Valley:
+          Sorta like a western.  Bull-skulls halb buried in the sand
+          Military compound:  Aliens take over nuke-missle silo, duke
+            must destroy.
+          Abondend Aircraft field
+        Vegas:
+          Blast anything bright!  Alien lights camoflauged.
+          Alien Drug factory. The Blue Liquid
+        Mountainal Cave:
+          Interior cave battles.
+        Jungle:
+          Trees, canopee, animals, a mysterious hole in the earth
+        Penetencury:
+          Good use of spotlights:
+      Inventory:
+        Wood,
+        Metal,
+        Torch,
+        Rope,
+        Plastique,
+        Cloth,
+        Wiring,
+        Glue,
+        Cigars,
+        Food,
+        Duck Tape,
+        Nails,
+        Piping,
+        Petrol,
+        Uranium,
+        Gold,
+        Prism,
+        Power Cell,
+
+        Hand spikes (Limited usage, they become dull)
+        Oxygent     (Oxygen mixed with stimulant)
+
+
+      Player Skills:
+        R-Left,R-Right,Foward,Back
+        Strafe, Jump, Double Flip Jump for distance
+        Help, Escape
+        Fire/Use
+        Use Menu
+
+Programming:
+     Images: Polys
+     Actors:
+       Multi-Object sections for change (head,arms,legs,torsoe,all change)
+       Facial expressions.  Pal lookup per poly?
+
+     struct imagetype
+        {
+            int *itable; // AngX,AngY,AngZ,Xoff,Yoff,Zoff;
+            int *idata;
+            struct imagetype *prev, *next;
+        }
+
+*/
--- /dev/null
+++ b/Game/src/rts.c
@@ -1,0 +1,239 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+
+//=============
+// STATICS
+//=============
+
+int32 numlumps;
+static void  **lumpcache;
+static lumpinfo_t *lumpinfo;              // location of each lump on disk
+static boolean RTS_Started = false;
+
+char lumplockbyte[11];
+
+/*
+============================================================================
+
+                                                LUMP BASED ROUTINES
+
+============================================================================
+*/
+
+/*
+====================
+=
+= RTS_AddFile
+=
+= All files are optional, but at least one file must be found
+= Files with a .rts extension are wadlink files with multiple lumps
+= Other files are single lumps with the base filename for the lump name
+=
+====================
+*/
+
+void RTS_AddFile (char *filename)
+   {
+   wadinfo_t  header;
+   lumpinfo_t *lump_p;
+   int32     i;
+   int32      handle, length;
+   int32      startlump;
+   filelump_t *fileinfo;
+
+//
+// read the entire file in
+//      FIXME: shared opens
+
+   handle = SafeOpenRead( filename, filetype_binary );
+
+   startlump = numlumps;
+
+   // WAD file
+   printf("    Adding %s.\n",filename);
+   SafeRead( handle, &header, sizeof( header ) );
+   if (strncmp(header.identification,"IWAD",4))
+      Error (EXIT_FAILURE, "RTS file %s doesn't have IWAD id\n",filename);
+   header.numlumps = IntelLong(header.numlumps);
+   header.infotableofs = IntelLong(header.infotableofs);
+   length = header.numlumps*sizeof(filelump_t);
+   fileinfo = alloca (length);
+   if (!fileinfo)
+      Error (EXIT_FAILURE, "RTS file could not allocate header info on stack");
+   lseek (handle, header.infotableofs, SEEK_SET);
+   SafeRead (handle, fileinfo, length);
+   numlumps += header.numlumps;
+
+//
+// Fill in lumpinfo
+//
+   SafeRealloc((void **)&lumpinfo,numlumps*sizeof(lumpinfo_t));
+   lump_p = &lumpinfo[startlump];
+
+   for (i=startlump ; i<numlumps ; i++,lump_p++, fileinfo++)
+      {
+      lump_p->handle = handle;
+      lump_p->position = IntelLong(fileinfo->filepos);
+      lump_p->size = IntelLong(fileinfo->size);
+      strncpy (lump_p->name, fileinfo->name, 8);
+      }
+   }
+
+/*
+====================
+=
+= RTS_Init
+=
+= Files with a .rts extension are idlink files with multiple lumps
+=
+====================
+*/
+
+void RTS_Init (char *filename)
+   {
+   int32 length;
+   //
+   // open all the files, load headers, and count lumps
+   //
+   numlumps = 0;
+   lumpinfo = SafeMalloc(5);   // will be realloced as lumps are added
+
+   printf("RTS Manager Started.\n");
+   if (SafeFileExists(filename))
+      RTS_AddFile (filename);
+
+   if (!numlumps) return;
+
+   //
+   // set up caching
+   //
+   length = (numlumps) * sizeof( *lumpcache );
+   lumpcache = SafeMalloc(length);
+   memset(lumpcache,0,length);
+   RTS_Started = true;
+   }
+
+
+/*
+====================
+=
+= RTS_NumSounds
+=
+====================
+*/
+
+int32 RTS_NumSounds (void)
+   {
+   return numlumps-1;
+   }
+
+/*
+====================
+=
+= RTS_SoundLength
+=
+= Returns the buffer size needed to load the given lump
+=
+====================
+*/
+
+int32 RTS_SoundLength (int32 lump)
+   {
+   lump++;
+   if (lump >= numlumps)
+      Error (EXIT_FAILURE, "RTS_SoundLength: %i >= numlumps",lump);
+   return lumpinfo[lump].size;
+   }
+
+/*
+====================
+=
+= RTS_GetSoundName
+=
+====================
+*/
+
+char * RTS_GetSoundName (int32 i)
+   {
+   i++;
+   if (i>=numlumps)
+      Error (EXIT_FAILURE, "RTS_GetSoundName: %i >= numlumps",i);
+   return &(lumpinfo[i].name[0]);
+   }
+
+/*
+====================
+=
+= RTS_ReadLump
+=
+= Loads the lump into the given buffer, which must be >= RTS_SoundLength()
+=
+====================
+*/
+void RTS_ReadLump (int32 lump, void *dest)
+   {
+   lumpinfo_t *l;
+
+   if (lump >= numlumps)
+      Error (EXIT_FAILURE, "RTS_ReadLump: %i >= numlumps",lump);
+   if (lump < 0)
+      Error (EXIT_FAILURE, "RTS_ReadLump: %i < 0",lump);
+   l = lumpinfo+lump;
+   lseek (l->handle, l->position, SEEK_SET);
+   SafeRead(l->handle,dest,l->size);
+   }
+
+/*
+====================
+=
+= RTS_GetSound
+=
+====================
+*/
+void *RTS_GetSound (int32 lump)
+{
+   lump++;
+   if ((uint32)lump >= numlumps)
+      Error (EXIT_FAILURE, "RTS_GetSound: %i >= %i\n",lump,numlumps);
+
+   if (lumpcache[lump] == NULL)
+   {
+      lumplockbyte[lump] = 200;
+      allocache((long *)&lumpcache[lump],(long)RTS_SoundLength(lump-1),&lumplockbyte[lump]);
+      RTS_ReadLump(lump, lumpcache[lump]);
+   }
+   else
+   {
+      if (lumplockbyte[lump] < 200)
+         lumplockbyte[lump] = 200;
+      else
+         lumplockbyte[lump]++;
+   }
+   return lumpcache[lump];
+}
+
--- /dev/null
+++ b/Game/src/rts.h
@@ -1,0 +1,84 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+//***************************************************************************
+//
+//    RTS.H
+//
+//***************************************************************************
+
+#ifndef __rts_public__
+#define __rts_public__
+
+/*
+====================
+=
+= RTS_Init
+=
+= Files with a .rts extension are idlink files with multiple lumps
+=
+====================
+*/
+
+void RTS_Init (char *filename);
+/*
+====================
+=
+= RTS_NumSounds
+=
+====================
+*/
+
+int32 RTS_NumSounds (void);
+/*
+====================
+=
+= RTS_SoundLength
+=
+= Returns the buffer size needed to load the given lump
+=
+====================
+*/
+
+int32 RTS_SoundLength (int32 lump);
+/*
+====================
+=
+= RTS_GetSoundName
+=
+====================
+*/
+
+char * RTS_GetSoundName (int32 i);
+/*
+====================
+=
+= RTS_GetSound
+=
+====================
+*/
+void *RTS_GetSound (int32 lump);
+#endif
--- /dev/null
+++ b/Game/src/scriplib.c
@@ -1,0 +1,1062 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "duke3d.h"
+#include "scriplib.h"
+
+/* #define DEBUG_SCRIPLIB */
+
+#define MAX_SCRIPTS 5
+
+typedef enum {
+	SCRIPTFLAG_UNKNOWN,
+	SCRIPTFLAG_CATEGORY,
+	SCRIPTFLAG_ONESTRING,
+	SCRIPTFLAG_TWOSTRING,
+	SCRIPTFLAG_HEX,
+	SCRIPTFLAG_DECIMAL,
+    SCRIPTFLAG_FLOAT
+} scriptflag_t;
+
+typedef struct scriptnode_s {
+	struct scriptnode_s *child;
+	struct scriptnode_s *sibling;
+	char *key;
+	scriptflag_t type;
+	union {
+		char *string[2];
+		int number;
+        float floatnumber;
+	} data;
+} scriptnode_t;
+
+static int script_nexthandle = 0;
+static int script_numscriptsopen = 0;
+
+static scriptnode_t *script_headnode[MAX_SCRIPTS];
+
+/* Utility functions */
+static scriptnode_t *SCRIPT_constructnode (void)
+{
+	scriptnode_t *s;
+
+	s = (scriptnode_t *) malloc (sizeof (scriptnode_t));
+	if (s != NULL)
+	{
+		s->child = NULL;
+		s->sibling = NULL;
+		s->key = NULL;
+		s->data.string[0] = NULL;
+		s->data.string[1] = NULL;
+		s->type = SCRIPTFLAG_UNKNOWN;
+	}
+	return s;
+}
+
+static void SCRIPT_freenode (scriptnode_t *node)
+{
+	assert (node != NULL);
+
+	if (node->type == SCRIPTFLAG_ONESTRING) {
+		free (node->data.string[0]);
+	} else if (node->type == SCRIPTFLAG_TWOSTRING) {
+		free (node->data.string[0]);
+		free (node->data.string[1]);
+	}
+
+	free (node->key);
+	free (node->sibling);
+	free (node->child);
+	free (node);
+}
+
+static void SCRIPT_writenode (scriptnode_t *node, FILE *fp)
+{
+	switch (node->type)
+	{
+		case SCRIPTFLAG_UNKNOWN:
+			return;
+			break;
+		case SCRIPTFLAG_CATEGORY:
+			fprintf (fp, "\n[%s]\n", node->key);
+			break;
+		case SCRIPTFLAG_ONESTRING:
+			fprintf (fp, "%s = \"%s\"\n", node->key, node->data.string[0]);
+			break;
+		case SCRIPTFLAG_TWOSTRING:
+			fprintf (fp, "%s = \"%s\" \"%s\"\n", node->key, node->data.string[0], node->data.string[1]);
+			break;
+		case SCRIPTFLAG_HEX:
+			fprintf (fp, "%s = 0x%X\n", node->key, node->data.number);
+			break;
+		case SCRIPTFLAG_DECIMAL:
+			fprintf (fp, "%s = %d\n", node->key, node->data.number);
+			break;
+		case SCRIPTFLAG_FLOAT:
+			fprintf (fp, "%s = %ff\n", node->key, node->data.floatnumber);
+			break;
+	}
+}
+
+static void SCRIPT_recursivewrite (scriptnode_t *node, FILE *fp)
+{
+	if (node == NULL) return;
+
+	SCRIPT_writenode (node, fp);
+
+	/* Free dependant nodes first */
+	if (node->child) {
+		SCRIPT_recursivewrite (node->child, fp);
+	}
+
+	if (node->sibling) {
+		SCRIPT_recursivewrite (node->sibling, fp);
+	}
+}
+
+static void SCRIPT_recursivefree (scriptnode_t *node)
+{
+	assert (node != NULL);
+
+	/* Free dependant nodes first */
+	if (node->child) {
+		SCRIPT_recursivefree (node->child);
+		node->child = NULL;
+	}
+
+	if (node->sibling) {
+		SCRIPT_recursivefree (node->sibling);
+		node->sibling = NULL;
+	}
+
+	SCRIPT_freenode (node);
+	node = NULL;
+}
+
+static void SCRIPT_addsibling (scriptnode_t *node, scriptnode_t *sibling)
+{
+	assert (node != NULL);
+	assert (sibling != NULL);
+	
+	/* printf ("addsib: %p, %p, %p\n", node, node->sibling, sibling); */
+
+	if (node->sibling == NULL) {
+		node->sibling = sibling;
+	} else {
+		SCRIPT_addsibling (node->sibling, sibling);
+	}
+}
+
+static void SCRIPT_addchild (scriptnode_t *parent, scriptnode_t *child)
+{
+	assert (parent != NULL);
+	assert (child != NULL);
+
+	if (parent->child == NULL) {
+		parent->child = child;
+	} else {
+		SCRIPT_addsibling (parent->child, child);
+	}
+}
+
+static char *SCRIPT_copystring (char * s)
+{
+	char *ret;
+
+	ret = (char *) malloc (strlen (s)+1);
+	if (ret != NULL)
+	{
+		strcpy (ret, s);
+	}
+	return ret;
+}
+
+static int SCRIPT_getnexttoken (char *buffer, char* token, int start)
+{
+	int iterator = start;
+
+	if (buffer[start] == '\0') {
+		token[0] = '\0';
+		return start;
+	}
+
+	while (iterator < 128 && !isspace (buffer[iterator]))
+	{
+		token[iterator-start] = buffer[iterator];
+		iterator++;
+	}
+
+	token[iterator-start] = '\0';
+
+	/* Trim off any extra whitespace */
+	while (iterator < 127 && isspace(buffer[iterator+1]))
+		iterator++;
+
+	return ++iterator;
+}
+
+/* Fills in a scriptnode with the interpreted contents of a line */
+static void SCRIPT_parseline (char *curline, scriptnode_t *node)
+{
+	char token[128];
+	int i;
+
+	/* Needs to handle 5 cases: */
+	/* 	key = someint */
+	/* 	key = 0xsomehexnum */
+	/* 	key = ~ */
+	/* 	key = "onestring" */
+	/* 	key = "two" "strings" */
+
+	assert (node != NULL);
+	assert (curline != NULL);
+
+	i = SCRIPT_getnexttoken (curline, token, 0);
+	node->key = SCRIPT_copystring (token);
+
+	i = SCRIPT_getnexttoken (curline, token, i);
+	/* Sanity check... this token should be "=" */
+	if (strcmp (token, "=")) {
+		/* Error state, free the memory allocated */
+		SCRIPT_recursivefree (node);
+		return;
+	}
+
+	/* This is where the real fun begins... */
+	/* we can begin to determine which of the 6 */
+	/* possibilities the node is now */
+	i = SCRIPT_getnexttoken (curline, token, i);
+
+	if (!strncmp (token, "0x", 2)) {
+		/* Found a hex digit! */
+		node->type = SCRIPTFLAG_HEX;
+		node->data.number = strtol (token, NULL, 16);
+	}else if (token[strlen(token) - 1] == 'f') {
+		/* Found a float */
+		node->type = SCRIPTFLAG_FLOAT;
+		node->data.floatnumber = atof(token);
+	}else if (isdigit (token[0])) {
+		/* Found a number! */
+		node->type = SCRIPTFLAG_DECIMAL;
+		node->data.number = atoi (token); 
+	}else if (token[0] == '-' && isdigit (token[1])) {
+		/* Found a negative number! */
+		node->type = SCRIPTFLAG_DECIMAL;
+		node->data.number = atoi (token); 
+		//- FIX_00067: *.cfg parser fails to read negative values (as it could be for the Midi selection)
+		// was node->data.number = 0; Can't find a reason for this.
+	}else if (token[0] == '~') {
+		/* Found a ... who knows */
+		node->type = SCRIPTFLAG_DECIMAL;
+		node->data.number = -1;
+	}else if (token[0] == '"') {
+		char workbuf[128];
+		int r;
+
+		/* Found one of possibly two strings */
+		strcpy (workbuf, token);
+		while (token != NULL && workbuf[strlen(workbuf)-1] != '"')
+		{
+			i = SCRIPT_getnexttoken (curline, token, i);
+			strcat (workbuf, " ");
+			strcat (workbuf, token);
+		}
+		r = sscanf(workbuf, "\"%[^\"]\"", workbuf);
+		if (r == 0) workbuf[0] = '\0';
+
+		node->type = SCRIPTFLAG_ONESTRING;
+		node->data.string[0] = SCRIPT_copystring (workbuf);
+		/* Check for a second string */
+		i = SCRIPT_getnexttoken (curline, token, i);
+		if (token[0] == '"') {
+			strcpy (workbuf, token);
+			while (token != NULL && workbuf[strlen(workbuf)-1] != '"')
+			{
+				i = SCRIPT_getnexttoken (curline, token, i);
+				strcat (workbuf, " ");
+				strcat (workbuf, token);
+			}
+			r = sscanf(workbuf, "\"%[^\"]\"", workbuf);
+			if (r == 0) workbuf[0] = '\0';
+
+			node->type = SCRIPTFLAG_TWOSTRING;
+			node->data.string[1] = SCRIPT_copystring (workbuf);
+		}
+	} else {
+		/* Error state! */
+		SCRIPT_recursivefree (node);
+	}
+}
+
+static scriptnode_t *SCRIPT_findinchildren (scriptnode_t *parent, char *s)
+{
+	scriptnode_t *cur = parent;
+
+	if (cur == NULL) return NULL;
+	cur = cur->child;
+	if (cur == NULL) return NULL;
+	while (cur != NULL)
+	{
+		if (!strcmp(cur->key, s))
+			break;
+		cur = cur->sibling;
+	}
+
+	return cur;
+}
+
+/*
+==============
+=
+= SCRIPT_Init
+=
+==============
+*/
+int32 SCRIPT_Init( char * name )
+{
+	STUBBED("Init");
+	
+	return -1;
+}
+
+
+/*
+==============
+=
+= SCRIPT_Free
+=
+==============
+*/
+void SCRIPT_Free( int32 scripthandle )
+{
+	/* STUBBED("Free"); */
+	if (scripthandle == -1) return;
+
+	SCRIPT_recursivefree (script_headnode[scripthandle]);
+	script_numscriptsopen--;
+}
+
+
+/*
+==============
+=
+= SCRIPT_Parse
+=
+==============
+*/
+
+int32 SCRIPT_Parse ( char *data, int32 length, char * name )
+{
+	STUBBED("Parse");
+	
+	return -1;
+}
+
+
+/*
+==============
+=
+= SCRIPT_Load
+=
+==============
+*/
+
+int32 SCRIPT_Load ( char * filename )
+{
+	FILE *fp;
+	char curline[128];
+	scriptnode_t *headnode = NULL;
+	scriptnode_t *cur_subsection = NULL;
+
+	if (script_numscriptsopen == MAX_SCRIPTS) return -1;
+
+	/* The main program does not check for any sort of */
+	/* error in loading, so each SCRIPT_ function needs */
+	/* to check if the handle is -1 before doing anything */
+	fp = fopen (filename, "r");
+
+	if (fp == NULL) return -1;
+
+	/* Start loading the script */
+	/* Loads and parse the entire file into a tree */
+	script_numscriptsopen++;
+
+	/* script_nexthandle is the current handle until we increment it */
+	script_headnode[script_nexthandle] = SCRIPT_constructnode ();
+	headnode = script_headnode[script_nexthandle];
+
+	memset (curline, 0, 128);
+	while (fgets (curline, 128, fp))
+	{
+		/* Skip comments */
+		if (curline[0] == ';') continue;
+
+		/* Parse line */
+		/* We have two options... it starts with a [, making it */
+		/* a new subsection (child of headnode) or it starts with */
+		/* a letter, making it a child of a subsection. */
+		if (curline[0] == '[')
+		{
+			scriptnode_t *node;
+			int i;
+			
+			/* Remove [] manually */
+			for (i = 0; i < 127; i++)
+				curline[i] = curline[i+1];
+			for (i = 127; i >= 0; i--)
+			{
+				if (curline[i] == ']') {
+					curline[i] = '\0';
+					break;
+				} else {
+					curline[i] = '\0';
+				}
+			}
+
+			/* Insert into head */
+			node = SCRIPT_constructnode ();
+			node->type = SCRIPTFLAG_CATEGORY;
+			node->key = SCRIPT_copystring (curline);
+			SCRIPT_addchild (headnode, node);
+			cur_subsection = node;
+			/* printf ("Working in section \"%s\"\n", node->key); */
+		} else if (isalpha (curline[0])) {
+			scriptnode_t *node;
+
+			/* Ignore if not under a subsection */
+			if (cur_subsection == NULL)
+				continue;
+
+			node = SCRIPT_constructnode ();
+			
+			/* TODO: Parse line here */
+			SCRIPT_parseline (curline, node);
+			if (node != NULL)
+			{
+				/* printf ("Adding node with key \"%s\"\n", node->key); */
+				SCRIPT_addchild (cur_subsection, node);
+			}
+		}
+		memset (curline, 0, 128);
+	}
+
+	fclose (fp);
+
+	return script_nexthandle++;	/* postincrement is important here */
+}
+
+/*
+==============
+=
+= SCRIPT_Save
+=
+==============
+*/
+void SCRIPT_Save (int32 scripthandle, char * filename)
+{
+	FILE *fp;
+	scriptnode_t *head;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return;
+
+	fp = fopen (filename, "w");
+	if (fp == NULL) return;
+
+	head = script_headnode[scripthandle];
+	SCRIPT_recursivewrite (head, fp);
+
+	fclose (fp);
+
+}
+
+
+/*
+==============
+=
+= SCRIPT_NumberSections
+=
+==============
+*/
+
+int32 SCRIPT_NumberSections( int32 scripthandle )
+{
+	STUBBED("NumberSections");
+	
+	return -1;
+}
+
+/*
+==============
+=
+= SCRIPT_Section
+=
+==============
+*/
+
+char * SCRIPT_Section( int32 scripthandle, int32 which )
+{
+	STUBBED("Section");
+	
+	return NULL;
+}
+
+/*
+==============
+=
+= SCRIPT_NumberEntries
+=
+==============
+*/
+
+int32 SCRIPT_NumberEntries( int32 scripthandle, char * sectionname )
+{
+	scriptnode_t *node = NULL;
+	int32 entries = 0;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return 0;
+
+	node = script_headnode[scripthandle];
+	node = SCRIPT_findinchildren(node, sectionname);
+	if(!node) return 0;
+
+	for(node=node->child; node ; node=node->sibling)
+	{
+		++entries;
+	}
+
+	return entries;
+}
+
+
+/*
+==============
+=
+= SCRIPT_Entry
+=
+==============
+*/
+char * SCRIPT_Entry( int32 scripthandle, char * sectionname, int32 which )
+{
+	scriptnode_t *node = NULL;
+	int32 entrynum = 0;
+	char* val = NULL;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return "";
+
+	node = script_headnode[scripthandle];
+	node = SCRIPT_findinchildren(node,sectionname);
+	if(!node) return "";
+
+	for(node=node->child; node ; node=node->sibling, ++entrynum)
+	{
+		if(entrynum == which)
+		{
+			val = node->key;
+			break;
+		}
+	}
+
+	return val;
+}
+
+
+/*
+==============
+=
+= SCRIPT_GetRaw
+=
+==============
+*/
+char * SCRIPT_GetRaw(int32 scripthandle, char * sectionname, char * entryname)
+{
+	STUBBED("GetRaw");
+	
+	return NULL;
+}
+
+/*
+==============
+=
+= SCRIPT_GetString
+=
+==============
+*/
+void SCRIPT_GetString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * dest
+   )
+{
+    scriptnode_t *cur;
+
+	/* STUBBED("GetString"); */
+	if (scripthandle == -1) return;
+
+	cur = script_headnode[scripthandle];
+
+	cur = SCRIPT_findinchildren (cur, sectionname);
+	cur = SCRIPT_findinchildren (cur, entryname);
+
+	if (cur != NULL && cur->type == SCRIPTFLAG_ONESTRING)
+	{
+		strcpy (dest, cur->data.string[0]);
+#ifdef DEBUG_SCRIPLIB
+		printf ("GetString: value for %s:%s is %s\n", sectionname, entryname, dest);
+#endif
+	}
+}
+
+/*
+==============
+=
+= SCRIPT_GetDoubleString
+=
+==============
+*/
+void SCRIPT_GetDoubleString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * dest1,
+   char * dest2
+   )
+{
+    scriptnode_t *cur;
+
+	/* STUBBED("GetDoubleString"); */
+	if (scripthandle == -1) return;
+
+	cur = script_headnode[scripthandle];
+
+	cur = SCRIPT_findinchildren (cur, sectionname);
+	cur = SCRIPT_findinchildren (cur, entryname);
+
+	if (cur != NULL && cur->type == SCRIPTFLAG_TWOSTRING)
+	{
+		strcpy (dest1, cur->data.string[0]);
+		strcpy (dest2, cur->data.string[1]);
+#ifdef DEBUG_SCRIPLIB
+		printf ("GetDoubleString: value for %s:%s is %s %s\n", sectionname, entryname, dest1, dest2);
+#endif
+	}
+}
+
+/*
+==============
+=
+= SCRIPT_GetNumber
+=
+==============
+*/
+boolean SCRIPT_GetNumber
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   int32 * number
+   )
+{
+	scriptnode_t *cur;
+
+	/* STUBBED("GetNumber"); */
+	if (scripthandle == -1) return false;
+
+	cur = script_headnode[scripthandle];
+
+	cur = SCRIPT_findinchildren (cur, sectionname);
+	cur = SCRIPT_findinchildren (cur, entryname);
+
+	if (cur != NULL && cur->type == SCRIPTFLAG_DECIMAL)
+	{
+		*number = cur->data.number;
+#ifdef DEBUG_SCRIPLIB
+		printf ("GetNumber: value for %s:%s is %ld\n", sectionname, entryname, *number);
+#endif
+	}
+
+	return (cur != NULL) ? true : false;
+}
+
+/*
+==============
+=
+= SCRIPT_GetBoolean
+=
+==============
+*/
+void SCRIPT_GetBoolean
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   boolean * bool
+   )
+{
+	STUBBED("GetBoolean");
+}
+
+
+/*
+==============
+=
+= SCRIPT_GetFloat
+=
+==============
+*/
+boolean SCRIPT_GetFloat
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   float * floatnumber
+   )
+{
+	scriptnode_t *cur;
+
+	if (scripthandle == -1) return false;
+
+	cur = script_headnode[scripthandle];
+
+	cur = SCRIPT_findinchildren (cur, sectionname);
+	cur = SCRIPT_findinchildren (cur, entryname);
+
+	if (cur != NULL && cur->type == SCRIPTFLAG_FLOAT)
+	{
+		*floatnumber = cur->data.floatnumber;
+#ifdef DEBUG_SCRIPLIB
+		printf ("GetFloat: value for %s:%s is %f\n", sectionname, entryname, *number);
+#endif
+	}
+
+	return (cur != NULL) ? true : false;
+}
+
+
+/*
+==============
+=
+= SCRIPT_GetDouble
+=
+==============
+*/
+
+void SCRIPT_GetDouble
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   double * number
+   )
+{
+	STUBBED("GetDouble");
+}
+
+
+
+/*
+==============
+=
+= SCRIPT_PutComment
+=
+==============
+*/
+void SCRIPT_PutComment( int32 scripthandle, char * sectionname, char * comment )
+{
+	STUBBED("PutComment");
+}
+
+/*
+==============
+=
+= SCRIPT_PutEOL
+=
+==============
+*/
+void SCRIPT_PutEOL( int32 scripthandle, char * sectionname )
+{
+	STUBBED("PutEOL");
+}
+
+/*
+==============
+=
+= SCRIPT_PutMultiComment
+=
+==============
+*/
+void SCRIPT_PutMultiComment
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * comment,
+   ...
+   )
+{
+	STUBBED("PutMultiComment");
+}
+
+/*
+==============
+=
+= SCRIPT_PutSection
+=
+==============
+*/
+void SCRIPT_PutSection( int32 scripthandle, char * sectionname )
+{
+	STUBBED("PutSection");
+}
+
+/*
+==============
+=
+= SCRIPT_PutRaw
+=
+==============
+*/
+void SCRIPT_PutRaw
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * raw
+   )
+{
+	STUBBED("PutRaw");
+}
+
+/*
+==============
+=
+= SCRIPT_PutString
+=
+==============
+*/
+void SCRIPT_PutString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * string
+   )
+{
+	scriptnode_t *head;
+	scriptnode_t *section;
+	scriptnode_t *node;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return;
+
+	head = script_headnode[scripthandle];
+
+	/* We're screwed if there's no head */
+	if (head == NULL) return;
+
+	section = SCRIPT_findinchildren (head, sectionname);
+	if (section == NULL)
+	{
+		/* Add the section if it does not exist */
+		section = SCRIPT_constructnode ();
+		section->type = SCRIPTFLAG_CATEGORY;
+		section->key = SCRIPT_copystring (sectionname);
+		SCRIPT_addchild (head, section);
+	}
+
+	node = SCRIPT_findinchildren (section, entryname);
+	if (node == NULL)
+	{
+		/* Add the section if it does not exist */
+		node = SCRIPT_constructnode ();
+		node->type = SCRIPTFLAG_ONESTRING;
+		node->key = SCRIPT_copystring (entryname);
+		SCRIPT_addchild (section, node);
+	} else {
+		free (node->data.string[0]);
+	}
+
+	node->data.string[0] = SCRIPT_copystring (string);
+}
+
+/*
+==============
+=
+= SCRIPT_PutDoubleString
+=
+==============
+*/
+void SCRIPT_PutDoubleString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * string1,
+   char * string2
+   )
+{
+	scriptnode_t *head;
+	scriptnode_t *section;
+	scriptnode_t *node;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return;
+
+	head = script_headnode[scripthandle];
+
+	/* We're screwed if there's no head */
+	if (head == NULL) return;
+
+	section = SCRIPT_findinchildren (head, sectionname);
+	if (section == NULL)
+	{
+		/* Add the section if it does not exist */
+		section = SCRIPT_constructnode ();
+		section->type = SCRIPTFLAG_CATEGORY;
+		section->key = SCRIPT_copystring (sectionname);
+		SCRIPT_addchild (head, section);
+	}
+
+	node = SCRIPT_findinchildren (section, entryname);
+	if (node == NULL)
+	{
+		/* Add the section if it does not exist */
+		node = SCRIPT_constructnode ();
+		node->type = SCRIPTFLAG_TWOSTRING;
+		node->key = SCRIPT_copystring (entryname);
+		SCRIPT_addchild (section, node);
+	} else {
+		free (node->data.string[0]);
+		free (node->data.string[1]);
+	}
+
+	node->data.string[0] = SCRIPT_copystring (string1);
+	node->data.string[1] = SCRIPT_copystring (string2);
+}
+
+/*
+==============
+=
+= SCRIPT_PutNumber
+=
+==============
+*/
+void SCRIPT_PutNumber
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   int32 number,
+   boolean hexadecimal,
+   boolean defaultvalue
+   )
+{
+	/* DDOI - I don't know what "defaultvalue" is for so it's ignored */
+	scriptnode_t *head;
+	scriptnode_t *section;
+	scriptnode_t *node;
+
+	if(scripthandle >= MAX_SCRIPTS || scripthandle < 0)
+		return;
+
+	head = script_headnode[scripthandle];
+
+	/* We're screwed if there's no head */
+	if (head == NULL) return;
+
+	section = SCRIPT_findinchildren (head, sectionname);
+	if (section == NULL)
+	{
+		/* Add the section if it does not exist */
+		section = SCRIPT_constructnode ();
+		section->type = SCRIPTFLAG_CATEGORY;
+		section->key = SCRIPT_copystring (sectionname);
+		SCRIPT_addchild (head, section);
+	}
+
+	node = SCRIPT_findinchildren (section, entryname);
+	if (node == NULL)
+	{
+		/* Add the section if it does not exist */
+		node = SCRIPT_constructnode ();
+		node->key = SCRIPT_copystring (entryname);
+		SCRIPT_addchild (section, node);
+	}
+
+	if (hexadecimal)
+		node->type = SCRIPTFLAG_HEX;
+	else
+		node->type = SCRIPTFLAG_DECIMAL;
+	node->data.number = number;
+}
+
+/*
+==============
+=
+= SCRIPT_PutBoolean
+=
+==============
+*/
+void SCRIPT_PutBoolean
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   boolean bool
+   )
+{
+	STUBBED("PutBoolean");
+}
+
+/*
+==============
+=
+= SCRIPT_PutDouble
+=
+==============
+*/
+
+void SCRIPT_PutDouble
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   double number,
+   boolean defaultvalue
+   )
+{
+	STUBBED("PutDouble");
+}
--- /dev/null
+++ b/Game/src/scriplib.h
@@ -1,0 +1,372 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+// scriplib.h
+
+#ifndef _scriplib_public
+#define _scriplib_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+==============
+=
+= SCRIPT_Init
+=
+==============
+*/
+int32 SCRIPT_Init( char * name );
+
+
+/*
+==============
+=
+= SCRIPT_Free
+=
+==============
+*/
+void SCRIPT_Free( int32 scripthandle );
+
+/*
+==============
+=
+= SCRIPT_Parse
+=
+==============
+*/
+
+int32 SCRIPT_Parse ( char *data, int32 length, char * name );
+
+
+/*
+==============
+=
+= SCRIPT_Load
+=
+==============
+*/
+
+int32 SCRIPT_Load ( char * filename );
+
+/*
+==============
+=
+= SCRIPT_Save
+=
+==============
+*/
+void SCRIPT_Save (int32 scripthandle, char * filename);
+
+
+/*
+==============
+=
+= SCRIPT_NumberSections
+=
+==============
+*/
+
+int32 SCRIPT_NumberSections( int32 scripthandle );
+
+/*
+==============
+=
+= SCRIPT_Section
+=
+==============
+*/
+
+char * SCRIPT_Section( int32 scripthandle, int32 which );
+
+/*
+==============
+=
+= SCRIPT_NumberEntries
+=
+==============
+*/
+
+int32 SCRIPT_NumberEntries( int32 scripthandle, char * sectionname );
+
+/*
+==============
+=
+= SCRIPT_Entry
+=
+==============
+*/
+
+char * SCRIPT_Entry( int32 scripthandle, char * sectionname, int32 which );
+
+
+/*
+==============
+=
+= SCRIPT_GetRaw
+=
+==============
+*/
+char * SCRIPT_GetRaw(int32 scripthandle, char * sectionname, char * entryname);
+
+/*
+==============
+=
+= SCRIPT_GetString
+=
+==============
+*/
+void SCRIPT_GetString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * dest
+   );
+
+/*
+==============
+=
+= SCRIPT_GetDoubleString
+=
+==============
+*/
+void SCRIPT_GetDoubleString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * dest1,
+   char * dest2
+   );
+
+/*
+==============
+=
+= SCRIPT_GetNumber
+=
+==============
+*/
+boolean SCRIPT_GetNumber
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   int32 * number
+   );
+
+/*
+==============
+=
+= SCRIPT_GetBoolean
+=
+==============
+*/
+void SCRIPT_GetBoolean
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   boolean * bool
+   );
+
+/*
+==============
+=
+= SCRIPT_GetFloat
+=
+==============
+*/
+
+boolean SCRIPT_GetFloat
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   float * floatnumber
+   );
+
+
+/*
+==============
+=
+= SCRIPT_GetDouble
+=
+==============
+*/
+
+void SCRIPT_GetDouble
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   double * number
+   );
+
+
+
+/*
+==============
+=
+= SCRIPT_PutComment
+=
+==============
+*/
+void SCRIPT_PutComment( int32 scripthandle, char * sectionname, char * comment );
+
+/*
+==============
+=
+= SCRIPT_PutEOL
+=
+==============
+*/
+void SCRIPT_PutEOL( int32 scripthandle, char * sectionname );
+
+/*
+==============
+=
+= SCRIPT_PutMultiComment
+=
+==============
+*/
+void SCRIPT_PutMultiComment
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * comment,
+   ...
+   );
+
+/*
+==============
+=
+= SCRIPT_PutSection
+=
+==============
+*/
+void SCRIPT_PutSection( int32 scripthandle, char * sectionname );
+
+/*
+==============
+=
+= SCRIPT_PutRaw
+=
+==============
+*/
+void SCRIPT_PutRaw
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * raw
+   );
+
+/*
+==============
+=
+= SCRIPT_PutString
+=
+==============
+*/
+void SCRIPT_PutString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * string
+   );
+
+/*
+==============
+=
+= SCRIPT_PutDoubleString
+=
+==============
+*/
+void SCRIPT_PutDoubleString
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   char * string1,
+   char * string2
+   );
+
+/*
+==============
+=
+= SCRIPT_PutNumber
+=
+==============
+*/
+void SCRIPT_PutNumber
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   int32 number,
+   boolean hexadecimal,
+   boolean defaultvalue
+   );
+
+/*
+==============
+=
+= SCRIPT_PutBoolean
+=
+==============
+*/
+void SCRIPT_PutBoolean
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   boolean bool
+   );
+
+/*
+==============
+=
+= SCRIPT_PutDouble
+=
+==============
+*/
+
+void SCRIPT_PutDouble
+   (
+   int32 scripthandle,
+   char * sectionname,
+   char * entryname,
+   double number,
+   boolean defaultvalue
+   );
+
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/sector.c
@@ -1,0 +1,3248 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#include "duke3d.h"
+
+// PRIMITIVE
+
+
+char haltsoundhack;
+short callsound(short sn,short whatsprite)
+{
+    short i;
+
+    if(haltsoundhack)
+    {
+        haltsoundhack = 0;
+        return -1;
+    }
+
+    i = headspritesect[sn];
+    while(i >= 0)
+    {
+        if( PN == MUSICANDSFX && SLT < 1000 )
+        {
+            if(whatsprite == -1) whatsprite = i;
+
+            if(T1 == 0)
+            {
+                if( (soundm[SLT]&16) == 0)
+                {
+                    if(SLT)
+                    {
+                        spritesound(SLT,whatsprite);
+                        if(SHT && SLT != SHT && SHT < NUM_SOUNDS)
+                            stopsound(SHT);
+                    }
+
+                    if( (sector[SECT].lotag&0xff) != 22)
+                        T1 = 1;
+                }
+            }
+            else if(SHT < NUM_SOUNDS)
+            {
+                if(SHT) spritesound(SHT,whatsprite);
+                if( (soundm[SLT]&1) || ( SHT && SHT != SLT ) )
+                    stopsound(SLT);
+                T1 = 0;
+            }
+            return SLT;
+        }
+        i = nextspritesect[i];
+    }
+    return -1;
+}
+
+
+short check_activator_motion( short lotag )
+{
+    short i, j;
+    spritetype *s;
+
+    i = headspritestat[8];
+    while ( i >= 0 )
+    {
+        if ( sprite[i].lotag == lotag )
+        {
+            s = &sprite[i];
+
+            for ( j = animatecnt-1; j >= 0; j-- )
+                if ( s->sectnum == animatesect[j] )
+                    return( 1 );
+
+            j = headspritestat[3];
+            while ( j >= 0 )
+            {
+                if(s->sectnum == sprite[j].sectnum)
+                    switch(sprite[j].lotag)
+                    {
+                        case 11:
+                        case 30:
+                            if ( hittype[j].temp_data[4] )
+                                return( 1 );
+                            break;
+                        case 20:
+                        case 31:
+                        case 32:
+                        case 18:
+                            if ( hittype[j].temp_data[0] )
+                                return( 1 );
+                            break;
+                    }
+
+                j = nextspritestat[j];
+            }
+        }
+        i = nextspritestat[i];
+    }
+    return( 0 );
+}
+
+char isadoorwall(short dapic)
+{
+    switch(dapic)
+    {
+        case DOORTILE1:
+        case DOORTILE2:
+        case DOORTILE3:
+        case DOORTILE4:
+        case DOORTILE5:
+        case DOORTILE6:
+        case DOORTILE7:
+        case DOORTILE8:
+        case DOORTILE9:
+        case DOORTILE10:
+        case DOORTILE11:
+        case DOORTILE12:
+        case DOORTILE14:
+        case DOORTILE15:
+        case DOORTILE16:
+        case DOORTILE17:
+        case DOORTILE18:
+        case DOORTILE19:
+        case DOORTILE20:
+        case DOORTILE21:
+        case DOORTILE22:
+        case DOORTILE23:
+            return 1;
+    }
+    return 0;
+}
+
+
+char isanunderoperator(short lotag)
+{
+    switch(lotag&0xff)
+    {
+        case 15:
+        case 16:
+        case 17:
+        case 18:
+        case 19:
+        case 22:
+        case 26:
+            return 1;
+    }
+    return 0;
+}
+
+char isanearoperator(short lotag)
+{
+    switch(lotag&0xff)
+    {
+        case 9:
+        case 15:
+        case 16:
+        case 17:
+        case 18:
+        case 19:
+        case 20:
+        case 21:
+        case 22:
+        case 23:
+        case 25:
+        case 26:
+        case 29://Toothed door
+            return 1;
+    }
+    return 0;
+}
+
+short checkcursectnums(short sect)
+{
+    short i;
+    for(i=connecthead;i>=0;i=connectpoint2[i])
+        if( sprite[ps[i].i].sectnum == sect ) return i;
+    return -1;
+}
+
+long ldist(spritetype *s1,spritetype *s2)
+{
+    long vx,vy;
+    vx = s1->x - s2->x;
+    vy = s1->y - s2->y;
+    return(FindDistance2D(vx,vy) + 1);
+}
+
+long dist(spritetype *s1,spritetype *s2)
+{
+    long vx,vy,vz;
+    vx = s1->x - s2->x;
+    vy = s1->y - s2->y;
+    vz = s1->z - s2->z;
+    return(FindDistance3D(vx,vy,vz>>4));
+}
+
+short findplayer(spritetype *s,long *d)
+{
+    short j, closest_player;
+    long x, closest;
+
+    if(ud.multimode < 2)
+    {
+        *d = klabs(ps[myconnectindex].oposx-s->x) + klabs(ps[myconnectindex].oposy-s->y) + ((klabs(ps[myconnectindex].oposz-s->z+(28<<8)))>>4);
+        return myconnectindex;
+    }
+
+    closest = 0x7fffffff;
+    closest_player = 0;
+
+    for(j=connecthead;j>=0;j=connectpoint2[j])
+    {
+        x = klabs(ps[j].oposx-s->x) + klabs(ps[j].oposy-s->y) + ((klabs(ps[j].oposz-s->z+(28<<8)))>>4);
+        if( x < closest && sprite[ps[j].i].extra > 0 )
+        {
+            closest_player = j;
+            closest = x;
+        }
+    }
+
+    *d = closest;
+    return closest_player;
+}
+
+short findotherplayer(short p,long *d)
+{
+    short j, closest_player;
+    long x, closest;
+
+    closest = 0x7fffffff;
+    closest_player = p;
+
+    for(j=connecthead;j>=0;j=connectpoint2[j])
+        if(p != j && sprite[ps[j].i].extra > 0)
+    {
+        x = klabs(ps[j].oposx-ps[p].posx) + klabs(ps[j].oposy-ps[p].posy) + (klabs(ps[j].oposz-ps[p].posz)>>4);
+
+        if( x < closest )
+        {
+            closest_player = j;
+            closest = x;
+        }
+    }
+
+    *d = closest;
+    return closest_player;
+}
+
+
+
+void doanimations(void)
+{
+	long i, j, a, p, v, dasect;
+
+	for(i=animatecnt-1;i>=0;i--)
+	{
+		a = *animateptr[i];
+		v = animatevel[i]*TICSPERFRAME;
+		dasect = animatesect[i];
+
+        if (a == animategoal[i])
+        {
+            stopinterpolation(animateptr[i]);
+
+            animatecnt--;
+            animateptr[i] = animateptr[animatecnt];
+            animategoal[i] = animategoal[animatecnt];
+            animatevel[i] = animatevel[animatecnt];
+            animatesect[i] = animatesect[animatecnt];
+            if( sector[animatesect[i]].lotag == 18 || sector[animatesect[i]].lotag == 19 )
+                if(animateptr[i] == &sector[animatesect[i]].ceilingz)
+                    continue;
+
+           if( (sector[dasect].lotag&0xff) != 22 )
+                callsound(dasect,-1);
+
+            continue;
+        }
+
+        if (v > 0) { a = min(a+v,animategoal[i]); }
+              else { a = max(a+v,animategoal[i]); }
+
+        if( animateptr[i] == &sector[animatesect[i]].floorz)
+        {
+            for(p=connecthead;p>=0;p=connectpoint2[p])
+                if (ps[p].cursectnum == dasect)
+                    if ((sector[dasect].floorz-ps[p].posz) < (64<<8))
+                        if (sprite[ps[p].i].owner >= 0)
+            {
+                ps[p].posz += v;
+                ps[p].poszv = 0;
+                if (p == myconnectindex)
+                {
+                    myz += v;
+                    myzvel = 0;
+                    myzbak[((movefifoplc-1)&(MOVEFIFOSIZ-1))] = ps[p].posz;
+                }
+            }
+
+            for(j=headspritesect[dasect];j>=0;j=nextspritesect[j])
+                if (sprite[j].statnum != 3)
+                {
+                    hittype[j].bposz = sprite[j].z;
+                    sprite[j].z += v;
+                    hittype[j].floorz = sector[dasect].floorz+v;
+                }
+        }
+
+		*animateptr[i] = a;
+	}
+}
+
+long getanimationgoal(long *animptr)
+{
+	long i, j;
+
+	j = -1;
+    for(i=animatecnt-1;i>=0;i--)
+        if (animptr == (long *)animateptr[i])
+		{
+			j = i;
+			break;
+		}
+	return(j);
+}
+
+static long setanimation(short animsect,long *animptr, long thegoal, long thevel)
+{
+	long i, j;
+
+	if (animatecnt >= MAXANIMATES-1)
+		return(-1);
+
+	j = animatecnt;
+    for(i=0;i<animatecnt;i++)
+		if (animptr == animateptr[i])
+		{
+			j = i;
+			break;
+		}
+
+    animatesect[j] = animsect;
+	animateptr[j] = animptr;
+	animategoal[j] = thegoal;
+    if (thegoal >= *animptr)
+       animatevel[j] = thevel;
+    else
+       animatevel[j] = -thevel;
+    
+    if (j == animatecnt) animatecnt++;
+
+    setinterpolation(animptr);
+
+    return(j);
+}
+
+
+
+
+void animatecamsprite(void)
+{
+    short i;
+
+    if(camsprite <= 0) return;
+
+    i = camsprite;
+
+    if(T1 >= 11)
+    {
+        T1 = 0;
+
+        if(ps[screenpeek].newowner >= 0)
+            OW = ps[screenpeek].newowner;
+
+        else if(OW >= 0 && dist(&sprite[ps[screenpeek].i],&sprite[i]) < 2048)
+            xyzmirror(OW,PN);
+    }
+    else T1++;
+}
+
+void animatewalls(void)
+{
+    long i, j, p, t;
+
+    for(p=0;p < numanimwalls ;p++)
+//    for(p=numanimwalls-1;p>=0;p--)
+    {
+        i = animwall[p].wallnum;
+        j = wall[i].picnum;
+
+            switch(j)
+            {
+                case SCREENBREAK1:
+                case SCREENBREAK2:
+                case SCREENBREAK3:
+                case SCREENBREAK4:
+                case SCREENBREAK5:
+
+                case SCREENBREAK9:
+                case SCREENBREAK10:
+                case SCREENBREAK11:
+                case SCREENBREAK12:
+                case SCREENBREAK13:
+                case SCREENBREAK14:
+                case SCREENBREAK15:
+                case SCREENBREAK16:
+                case SCREENBREAK17:
+                case SCREENBREAK18:
+                case SCREENBREAK19:
+
+                    if( (TRAND&255) < 16)
+                    {
+                        animwall[p].tag = wall[i].picnum;
+                        wall[i].picnum = SCREENBREAK6;
+                    }
+
+                    continue;
+
+                case SCREENBREAK6:
+                case SCREENBREAK7:
+                case SCREENBREAK8:
+
+                    if(animwall[p].tag >= 0 && wall[i].extra != FEMPIC2 && wall[i].extra != FEMPIC3 )
+                        wall[i].picnum = animwall[p].tag;
+                    else
+                    {
+                        wall[i].picnum++;
+                        if(wall[i].picnum == (SCREENBREAK6+3) )
+                            wall[i].picnum = SCREENBREAK6;
+                    }
+                    continue;
+
+            }
+
+        if(wall[i].cstat&16)
+            switch(wall[i].overpicnum)
+        {
+            case W_FORCEFIELD:
+            case W_FORCEFIELD+1:
+            case W_FORCEFIELD+2:
+
+                t = animwall[p].tag;
+
+                if(wall[i].cstat&254)
+                {
+                    wall[i].xpanning -= t>>10; // sintable[(t+512)&2047]>>12;
+                    wall[i].ypanning -= t>>10; // sintable[t&2047]>>12;
+
+                    if(wall[i].extra == 1)
+                    {
+                        wall[i].extra = 0;
+                        animwall[p].tag = 0;
+                    }
+                    else
+                        animwall[p].tag+=128;
+
+                    if( animwall[p].tag < (128<<4) )
+                    {
+                        if( animwall[p].tag&128 )
+                            wall[i].overpicnum = W_FORCEFIELD;
+                        else wall[i].overpicnum = W_FORCEFIELD+1;
+                    }
+                    else
+                    {
+                        if( (TRAND&255) < 32 )
+                            animwall[p].tag = 128<<(TRAND&3);
+                        else wall[i].overpicnum = W_FORCEFIELD+1;
+                    }
+                }
+
+                break;
+        }
+    }
+}
+
+char activatewarpelevators(short s,short d) //Parm = sectoreffectornum
+{
+    short i, sn;
+
+    sn = sprite[s].sectnum;
+
+    // See if the sector exists
+
+    i = headspritestat[3];
+    while(i >= 0)
+    {
+        if( SLT == 17 )
+            if( SHT == sprite[s].hitag )
+                if( (klabs(sector[sn].floorz-hittype[s].temp_data[2]) > SP) ||
+                    (sector[SECT].hitag == (sector[sn].hitag-d) ) )
+                        break;
+        i = nextspritestat[i];
+    }
+
+    if(i==-1)
+    {
+        d = 0;
+        return 1; // No find
+    }
+    else
+    {
+        if(d == 0)
+            spritesound(ELEVATOR_OFF,s);
+        else spritesound(ELEVATOR_ON,s);
+    }
+
+
+    i = headspritestat[3];
+    while(i >= 0)
+    {
+        if( SLT == 17 )
+            if( SHT == sprite[s].hitag )
+            {
+                T1 = d;
+                T2 = d; //Make all check warp
+            }
+        i = nextspritestat[i];
+    }
+    return 0;
+}
+
+
+
+void operatesectors(short sn,short ii)
+{
+    long j=0, l, q, startwall, endwall;
+    short i;
+    char sect_error;
+    sectortype *sptr;
+
+    sect_error = 0;
+    sptr = &sector[sn];
+
+    switch(sptr->lotag&(0xffff-49152))
+    {
+
+        case 30:
+            j = sector[sn].hitag;
+            if( hittype[j].tempang == 0 ||
+                hittype[j].tempang == 256)
+                    callsound(sn,ii);
+            if(sprite[j].extra == 1)
+                sprite[j].extra = 3;
+            else sprite[j].extra = 1;
+            break;
+
+        case 31:
+
+            j = sector[sn].hitag;
+            if(hittype[j].temp_data[4] == 0)
+                hittype[j].temp_data[4] = 1;
+            
+            callsound(sn,ii);
+            break;
+
+        case 26: //The split doors
+            i = getanimationgoal(&sptr->ceilingz);
+            if(i == -1) //if the door has stopped
+            {
+                haltsoundhack = 1;
+                sptr->lotag &= 0xff00;
+                sptr->lotag |= 22;
+                operatesectors(sn,ii);
+                sptr->lotag &= 0xff00;
+                sptr->lotag |= 9;
+                operatesectors(sn,ii);
+                sptr->lotag &= 0xff00;
+                sptr->lotag |= 26;
+            }
+            return;
+
+        case 9:
+        {
+            long dax,day,dax2,day2,sp;
+            long wallfind[2];
+
+            startwall = sptr->wallptr;
+            endwall = startwall+sptr->wallnum-1;
+
+            sp = sptr->extra>>4;
+
+            //first find center point by averaging all points
+            dax = 0L, day = 0L;
+            for(i=startwall;i<=endwall;i++)
+            {
+                dax += wall[i].x;
+                day += wall[i].y;
+            }
+            dax /= (endwall-startwall+1);
+            day /= (endwall-startwall+1);
+
+            //find any points with either same x or same y coordinate
+            //  as center (dax, day) - should be 2 points found.
+            wallfind[0] = -1;
+            wallfind[1] = -1;
+            for(i=startwall;i<=endwall;i++)
+                if ((wall[i].x == dax) || (wall[i].y == day))
+                {
+                    if (wallfind[0] == -1)
+                        wallfind[0] = i;
+                    else wallfind[1] = i;
+                }
+
+            for(j=0;j<2;j++)
+            {
+                if ((wall[wallfind[j]].x == dax) && (wall[wallfind[j]].y == day))
+                {
+                    //find what direction door should open by averaging the
+                    //  2 neighboring points of wallfind[0] & wallfind[1].
+                    i = wallfind[j]-1; if (i < startwall) i = endwall;
+                    dax2 = ((wall[i].x+wall[wall[wallfind[j]].point2].x)>>1)-wall[wallfind[j]].x;
+                    day2 = ((wall[i].y+wall[wall[wallfind[j]].point2].y)>>1)-wall[wallfind[j]].y;
+                    if (dax2 != 0)
+                    {
+                        dax2 = wall[wall[wall[wallfind[j]].point2].point2].x;
+                        dax2 -= wall[wall[wallfind[j]].point2].x;
+                        setanimation(sn,&wall[wallfind[j]].x,wall[wallfind[j]].x+dax2,sp);
+                        setanimation(sn,&wall[i].x,wall[i].x+dax2,sp);
+                        setanimation(sn,&wall[wall[wallfind[j]].point2].x,wall[wall[wallfind[j]].point2].x+dax2,sp);
+                        callsound(sn,ii);
+                    }
+                    else if (day2 != 0)
+                    {
+                        day2 = wall[wall[wall[wallfind[j]].point2].point2].y;
+                        day2 -= wall[wall[wallfind[j]].point2].y;
+                        setanimation(sn,&wall[wallfind[j]].y,wall[wallfind[j]].y+day2,sp);
+                        setanimation(sn,&wall[i].y,wall[i].y+day2,sp);
+                        setanimation(sn,&wall[wall[wallfind[j]].point2].y,wall[wall[wallfind[j]].point2].y+day2,sp);
+                        callsound(sn,ii);
+                    }
+                }
+                else
+                {
+                    i = wallfind[j]-1; if (i < startwall) i = endwall;
+                    dax2 = ((wall[i].x+wall[wall[wallfind[j]].point2].x)>>1)-wall[wallfind[j]].x;
+                    day2 = ((wall[i].y+wall[wall[wallfind[j]].point2].y)>>1)-wall[wallfind[j]].y;
+                    if (dax2 != 0)
+                    {
+                        setanimation(sn,&wall[wallfind[j]].x,dax,sp);
+                        setanimation(sn,&wall[i].x,dax+dax2,sp);
+                        setanimation(sn,&wall[wall[wallfind[j]].point2].x,dax+dax2,sp);
+                        callsound(sn,ii);
+                    }
+                    else if (day2 != 0)
+                    {
+                        setanimation(sn,&wall[wallfind[j]].y,day,sp);
+                        setanimation(sn,&wall[i].y,day+day2,sp);
+                        setanimation(sn,&wall[wall[wallfind[j]].point2].y,day+day2,sp);
+                        callsound(sn,ii);
+                    }
+                }
+            }
+
+        }
+        return;
+
+        case 15://Warping elevators
+
+            if(sprite[ii].picnum != APLAYER) return;
+//            if(ps[sprite[ii].yvel].select_dir == 1) return;
+
+            i = headspritesect[sn];
+            while(i >= 0)
+            {
+                if(PN==SECTOREFFECTOR && SLT == 17 ) break;
+                i = nextspritesect[i];
+            }
+
+            if(sprite[ii].sectnum == sn)
+            {
+                if( activatewarpelevators(i,-1) )
+                    activatewarpelevators(i,1);
+                else if( activatewarpelevators(i,1) )
+                    activatewarpelevators(i,-1);
+                return;
+            }
+            else
+            {
+                if(sptr->floorz > SZ)
+                    activatewarpelevators(i,-1);
+                else
+                    activatewarpelevators(i,1);
+            }
+
+            return;
+
+        case 16:
+        case 17:
+
+            i = getanimationgoal(&sptr->floorz);
+
+            if(i == -1)
+            {
+                i = nextsectorneighborz(sn,sptr->floorz,1,1);
+                if( i == -1 )
+                {
+                    i = nextsectorneighborz(sn,sptr->floorz,1,-1);
+                    if( i == -1 ) return;
+                    j = sector[i].floorz;
+                    setanimation(sn,&sptr->floorz,j,sptr->extra);
+                }
+                else
+                {
+                    j = sector[i].floorz;
+                    setanimation(sn,&sptr->floorz,j,sptr->extra);
+                }
+                callsound(sn,ii);
+            }
+
+            return;
+
+        case 18:
+        case 19:
+
+            i = getanimationgoal(&sptr->floorz);
+
+            if(i==-1)
+            {
+                i = nextsectorneighborz(sn,sptr->floorz,1,-1);
+                if(i==-1) i = nextsectorneighborz(sn,sptr->floorz,1,1);
+                if(i==-1) return;
+                j = sector[i].floorz;
+                q = sptr->extra;
+                l = sptr->ceilingz-sptr->floorz;
+                setanimation(sn,&sptr->floorz,j,q);
+                setanimation(sn,&sptr->ceilingz,j+l,q);
+                callsound(sn,ii);
+            }
+            return;
+
+        case 29:
+
+            if(sptr->lotag&0x8000)
+                j = sector[nextsectorneighborz(sn,sptr->ceilingz,1,1)].floorz;
+            else
+                j = sector[nextsectorneighborz(sn,sptr->ceilingz,-1,-1)].ceilingz;
+
+            i = headspritestat[3]; //Effectors
+            while(i >= 0)
+            {
+                if( (SLT == 22) &&
+                    (SHT == sptr->hitag) )
+                {
+                    sector[SECT].extra = -sector[SECT].extra;
+
+                    T1 = sn;
+                    T2 = 1;
+                }
+                i = nextspritestat[i];
+            }
+
+            sptr->lotag ^= 0x8000;
+
+            setanimation(sn,&sptr->ceilingz,j,sptr->extra);
+
+            callsound(sn,ii);
+
+            return;
+
+        case 20:
+
+            REDODOOR:
+
+            if(sptr->lotag&0x8000)
+            {
+                i = headspritesect[sn];
+                while(i >= 0)
+                {
+                    if(sprite[i].statnum == 3 && SLT==9)
+                    {
+                        j = SZ;
+                        break;
+                    }
+                    i = nextspritesect[i];
+                }
+                if(i==-1)
+                    j = sptr->floorz;
+            }
+            else
+            {
+                j = nextsectorneighborz(sn,sptr->ceilingz,-1,-1);
+
+                if(j >= 0) j = sector[j].ceilingz;
+                else
+                {
+                    sptr->lotag |= 32768;
+                    goto REDODOOR;
+                }
+            }
+
+            sptr->lotag ^= 0x8000;
+
+            setanimation(sn,&sptr->ceilingz,j,sptr->extra);
+            callsound(sn,ii);
+
+            return;
+
+        case 21:
+            i = getanimationgoal(&sptr->floorz);
+            if (i >= 0)
+            {
+                if (animategoal[sn] == sptr->ceilingz)
+                    animategoal[i] = sector[nextsectorneighborz(sn,sptr->ceilingz,1,1)].floorz;
+                else animategoal[i] = sptr->ceilingz;
+                j = animategoal[i];
+            }
+            else
+            {
+                if (sptr->ceilingz == sptr->floorz)
+                    j = sector[nextsectorneighborz(sn,sptr->ceilingz,1,1)].floorz;
+                else j = sptr->ceilingz;
+
+                sptr->lotag ^= 0x8000;
+
+                if(setanimation(sn,&sptr->floorz,j,sptr->extra) >= 0)
+                    callsound(sn,ii);
+            }
+            return;
+
+        case 22:
+
+            // REDODOOR22:
+
+            if ( (sptr->lotag&0x8000) )
+            {
+                q = (sptr->ceilingz+sptr->floorz)>>1;
+                j = setanimation(sn,&sptr->floorz,q,sptr->extra);
+                j = setanimation(sn,&sptr->ceilingz,q,sptr->extra);
+            }
+            else
+            {
+                q = sector[nextsectorneighborz(sn,sptr->floorz,1,1)].floorz;
+                j = setanimation(sn,&sptr->floorz,q,sptr->extra);
+                q = sector[nextsectorneighborz(sn,sptr->ceilingz,-1,-1)].ceilingz;
+                j = setanimation(sn,&sptr->ceilingz,q,sptr->extra);
+            }
+
+            sptr->lotag ^= 0x8000;
+
+            callsound(sn,ii);
+
+            return;
+
+        case 23: //Swingdoor
+
+            j = -1;
+            q = 0;
+
+            i = headspritestat[3];
+            while(i >= 0)
+            {
+                if( SLT == 11 && SECT == sn && !T5)
+                {
+                    j = i;
+                    break;
+                }
+                i = nextspritestat[i];
+            }
+			
+#pragma message ( "Why would this ever be -1?" )
+			if(i < 0)
+			{
+				return;
+			}//FIXME: CRASH HERE (the "l = sector[SECT].lotag&0x8000; " code)
+
+            l = sector[SECT].lotag&0x8000; 
+
+            if(j >= 0)
+            {
+                i = headspritestat[3];
+                while(i >= 0)
+                {
+                    if( l == (sector[SECT].lotag&0x8000) && SLT == 11 && sprite[j].hitag == SHT && !T5 )
+                    {
+                        if(sector[SECT].lotag&0x8000) sector[SECT].lotag &= 0x7fff;
+                        else sector[SECT].lotag |= 0x8000;
+                        T5 = 1;
+                        T4 = -T4;
+                        if(q == 0)
+                        {
+                            callsound(sn,i);
+                            q = 1;
+                        }
+                    }
+                    i = nextspritestat[i];
+                }
+            }
+            return;
+
+        case 25: //Subway type sliding doors
+
+            j = headspritestat[3];
+            while(j >= 0)//Find the sprite
+            {
+                if( (sprite[j].lotag) == 15 && sprite[j].sectnum == sn )
+                    break; //Found the sectoreffector.
+                j = nextspritestat[j];
+            }
+
+            if(j < 0)
+                return;
+
+            i = headspritestat[3];
+            while(i >= 0)
+            {
+                if( SHT==sprite[j].hitag )
+                {
+                    if( SLT == 15 )
+                    {
+                        sector[SECT].lotag ^= 0x8000; // Toggle the open or close
+                        SA += 1024;
+                        if(T5) callsound(SECT,i);
+                        callsound(SECT,i);
+                        if(sector[SECT].lotag&0x8000) T5 = 1;
+                        else T5 = 2;
+                    }
+                }
+                i = nextspritestat[i];
+            }
+            return;
+
+        case 27:  //Extended bridge
+
+            j = headspritestat[3];
+            while(j >= 0)
+            {
+                if( (sprite[j].lotag&0xff)==20 && sprite[j].sectnum == sn) //Bridge
+                {
+
+                    sector[sn].lotag ^= 0x8000;
+                    if(sector[sn].lotag&0x8000) //OPENING
+                        hittype[j].temp_data[0] = 1;
+                    else hittype[j].temp_data[0] = 2;
+                    callsound(sn,ii);
+                    break;
+                }
+                j = nextspritestat[j];
+            }
+            return;
+
+
+        case 28:
+            //activate the rest of them
+
+            j = headspritesect[sn];
+            while(j >= 0)
+            {
+                if(sprite[j].statnum==3 && (sprite[j].lotag&0xff)==21)
+                    break; //Found it
+                j = nextspritesect[j];
+            }
+
+            j = sprite[j].hitag;
+
+            l = headspritestat[3];
+            while(l >= 0)
+            {
+                if( (sprite[l].lotag&0xff)==21 && !hittype[l].temp_data[0] &&
+                    (sprite[l].hitag) == j )
+                    hittype[l].temp_data[0] = 1;
+                l = nextspritestat[l];
+            }
+            callsound(sn,ii);
+
+            return;
+    }
+}
+
+
+
+void operaterespawns(short low)
+{
+    short i, j, nexti;
+
+    i = headspritestat[11];
+    while(i >= 0)
+    {
+        nexti = nextspritestat[i];
+        if(SLT == low) switch(PN)
+        {
+            case RESPAWN:
+                if( badguypic(SHT) && ud.monsters_off ) break;
+
+                j = spawn(i,TRANSPORTERSTAR);
+                sprite[j].z -= (32<<8);
+
+                sprite[i].extra = 66-12;   // Just a way to killit
+                break;
+        }
+        i = nexti;
+    }
+}
+
+void operateactivators(short low,short snum)
+{
+    short i, j, k, *p, nexti;
+    walltype *wal;
+
+    for(i=numcyclers-1;i>=0;i--)
+    {
+        p = &cyclers[i][0];
+
+        if(p[4] == low)
+        {
+            p[5] = !p[5];
+
+            sector[p[0]].floorshade = sector[p[0]].ceilingshade = p[3];
+            wal = &wall[sector[p[0]].wallptr];
+            for(j=sector[p[0]].wallnum;j > 0;j--,wal++)
+                wal->shade = p[3];
+        }
+    }
+
+    i = headspritestat[8];
+    k = -1;
+    while(i >= 0)
+    {
+        if(sprite[i].lotag == low)
+        {
+            if( sprite[i].picnum == ACTIVATORLOCKED )
+            {
+                if(sector[SECT].lotag&16384)
+                    sector[SECT].lotag &= 65535-16384;
+                else
+                    sector[SECT].lotag |= 16384;
+
+                if(snum >= 0)
+                {
+                    if(sector[SECT].lotag&16384)
+                        FTA(4,&ps[snum],0);
+                    else FTA(8,&ps[snum],0);
+                }
+            }
+            else
+            {
+                switch(SHT)
+                {
+                    case 0:
+                        break;
+                    case 1:
+                        if(sector[SECT].floorz != sector[SECT].ceilingz)
+                        {
+                            i = nextspritestat[i];
+                            continue;
+                        }
+                        break;
+                    case 2:
+                        if(sector[SECT].floorz == sector[SECT].ceilingz)
+                        {
+                            i = nextspritestat[i];
+                            continue;
+                        }
+                        break;
+                }
+
+                if( sector[sprite[i].sectnum].lotag < 3 )
+                {
+                    j = headspritesect[sprite[i].sectnum];
+                    while(j >= 0)
+                    {
+                        if( sprite[j].statnum == 3 ) switch(sprite[j].lotag)
+                        {
+                            case 36:
+                            case 31:
+                            case 32:
+                            case 18:
+                                hittype[j].temp_data[0] = 1-hittype[j].temp_data[0];
+                                callsound(SECT,j);
+                                break;
+                        }
+                        j = nextspritesect[j];
+                    }
+                }
+
+                if( k == -1 && (sector[SECT].lotag&0xff) == 22 )
+                    k = callsound(SECT,i);
+
+                operatesectors(SECT,i);
+            }
+        }
+        i = nextspritestat[i];
+     }
+
+    operaterespawns(low);
+}
+
+void operatemasterswitches(short low)
+{
+    short i;
+
+    i = headspritestat[6];
+    while(i >= 0)
+    {
+        if( PN == MASTERSWITCH && SLT == low && SP == 0 )
+            SP = 1;
+        i = nextspritestat[i];
+    }
+}
+
+
+
+void operateforcefields(short s, short low)
+{
+    short i, p;
+
+    for(p=numanimwalls;p>=0;p--)
+    {
+        i = animwall[p].wallnum;
+
+        if(low == wall[i].lotag || low == -1)
+            switch(wall[i].overpicnum)
+        {
+            case W_FORCEFIELD  :
+            case W_FORCEFIELD+1:
+            case W_FORCEFIELD+2:
+            case BIGFORCE:
+
+                animwall[p].tag = 0;
+
+                if( wall[i].cstat )
+                {
+                    wall[i].cstat   = 0;
+
+                    if( s >= 0 && sprite[s].picnum == SECTOREFFECTOR &&
+                        sprite[s].lotag == 30)
+                            wall[i].lotag = 0;
+                }
+                else
+                    wall[i].cstat = 85;
+                break;
+        }
+    }
+}
+
+
+char checkhitswitch(short snum,long w,char switchtype)
+{
+    char switchpal;
+    short i, x, lotag,hitag,picnum,correctdips,numdips;
+    long sx,sy;
+
+    if(w < 0) return 0;
+    correctdips = 1;
+    numdips = 0;
+
+    if(switchtype == 1) // A wall sprite
+    {
+        lotag = sprite[w].lotag; if(lotag == 0) return 0;
+        hitag = sprite[w].hitag;
+        sx = sprite[w].x;
+        sy = sprite[w].y;
+        picnum = sprite[w].picnum;
+        switchpal = sprite[w].pal;
+    }
+    else
+    {
+        lotag = wall[w].lotag; if(lotag == 0) return 0;
+        hitag = wall[w].hitag;
+        sx = wall[w].x;
+        sy = wall[w].y;
+        picnum = wall[w].picnum;
+        switchpal = wall[w].pal;
+    }
+
+    switch(picnum)
+    {
+        case DIPSWITCH:
+        case DIPSWITCH+1:
+        case TECHSWITCH:
+        case TECHSWITCH+1:
+        case ALIENSWITCH:
+        case ALIENSWITCH+1:
+            break;
+        case ACCESSSWITCH:
+        case ACCESSSWITCH2:
+            if(ps[snum].access_incs == 0)
+            {
+                if( switchpal == 0 )
+                {
+                    if( (ps[snum].got_access&1) )
+                        ps[snum].access_incs = 1;
+                    else FTA(70,&ps[snum],1);
+                }
+
+                else if( switchpal == 21 )
+                {
+                    if( ps[snum].got_access&2 )
+                        ps[snum].access_incs = 1;
+                    else FTA(71,&ps[snum],1);
+                }
+
+                else if( switchpal == 23 )
+                {
+                    if( ps[snum].got_access&4 )
+                        ps[snum].access_incs = 1;
+                    else FTA(72,&ps[snum],1);
+                }
+
+                if( ps[snum].access_incs == 1 )
+                {
+                    if(switchtype == 0)
+                        ps[snum].access_wallnum = w;
+                    else
+                        ps[snum].access_spritenum = w;
+                }
+
+                return 0;
+            }
+        case DIPSWITCH2:
+        case DIPSWITCH2+1:
+        case DIPSWITCH3:
+        case DIPSWITCH3+1:
+        case MULTISWITCH:
+        case MULTISWITCH+1:
+        case MULTISWITCH+2:
+        case MULTISWITCH+3:
+        case PULLSWITCH:
+        case PULLSWITCH+1:
+        case HANDSWITCH:
+        case HANDSWITCH+1:
+        case SLOTDOOR:
+        case SLOTDOOR+1:
+        case LIGHTSWITCH:
+        case LIGHTSWITCH+1:
+        case SPACELIGHTSWITCH:
+        case SPACELIGHTSWITCH+1:
+        case SPACEDOORSWITCH:
+        case SPACEDOORSWITCH+1:
+        case FRANKENSTINESWITCH:
+        case FRANKENSTINESWITCH+1:
+        case LIGHTSWITCH2:
+        case LIGHTSWITCH2+1:
+        case POWERSWITCH1:
+        case POWERSWITCH1+1:
+        case LOCKSWITCH1:
+        case LOCKSWITCH1+1:
+        case POWERSWITCH2:
+        case POWERSWITCH2+1:
+            if( check_activator_motion( lotag ) ) return 0;
+            break;
+        default:
+            if( isadoorwall(picnum) == 0 ) return 0;
+            break;
+    }
+
+    i = headspritestat[0];
+    while(i >= 0)
+    {
+        if( lotag == SLT ) switch(PN)
+        {
+            case DIPSWITCH:
+            case TECHSWITCH:
+            case ALIENSWITCH:
+                if( switchtype == 1 && w == i ) PN++;
+                else if( SHT == 0 ) correctdips++;
+                numdips++;
+                break;
+            case TECHSWITCH+1:
+            case DIPSWITCH+1:
+            case ALIENSWITCH+1:
+                if( switchtype == 1 && w == i ) PN--;
+                else if( SHT == 1 ) correctdips++;
+                numdips++;
+                break;
+            case MULTISWITCH:
+            case MULTISWITCH+1:
+            case MULTISWITCH+2:
+            case MULTISWITCH+3:
+                sprite[i].picnum++;
+                if( sprite[i].picnum > (MULTISWITCH+3) )
+                    sprite[i].picnum = MULTISWITCH;
+                break;
+            case ACCESSSWITCH:
+            case ACCESSSWITCH2:
+            case SLOTDOOR:
+            case LIGHTSWITCH:
+            case SPACELIGHTSWITCH:
+            case SPACEDOORSWITCH:
+            case FRANKENSTINESWITCH:
+            case LIGHTSWITCH2:
+            case POWERSWITCH1:
+            case LOCKSWITCH1:
+            case POWERSWITCH2:
+            case HANDSWITCH:
+            case PULLSWITCH:
+            case DIPSWITCH2:
+            case DIPSWITCH3:
+                sprite[i].picnum++;
+                break;
+            case PULLSWITCH+1:
+            case HANDSWITCH+1:
+            case LIGHTSWITCH2+1:
+            case POWERSWITCH1+1:
+            case LOCKSWITCH1+1:
+            case POWERSWITCH2+1:
+            case SLOTDOOR+1:
+            case LIGHTSWITCH+1:
+            case SPACELIGHTSWITCH+1:
+            case SPACEDOORSWITCH+1:
+            case FRANKENSTINESWITCH+1:
+            case DIPSWITCH2+1:
+            case DIPSWITCH3+1:
+                sprite[i].picnum--;
+                break;
+        }
+        i = nextspritestat[i];
+    }
+
+    for(i=0;i<numwalls;i++)
+    {
+        x = i;
+        if(lotag == wall[x].lotag)
+            switch(wall[x].picnum)
+        {
+            case DIPSWITCH:
+            case TECHSWITCH:
+            case ALIENSWITCH:
+                if( switchtype == 0 && i == w ) wall[x].picnum++;
+                else if( wall[x].hitag == 0 ) correctdips++;
+                numdips++;
+                break;
+            case DIPSWITCH+1:
+            case TECHSWITCH+1:
+            case ALIENSWITCH+1:
+                if( switchtype == 0 && i == w ) wall[x].picnum--;
+                else if( wall[x].hitag == 1 ) correctdips++;
+                numdips++;
+                break;
+            case MULTISWITCH:
+            case MULTISWITCH+1:
+            case MULTISWITCH+2:
+            case MULTISWITCH+3:
+                wall[x].picnum++;
+                if(wall[x].picnum > (MULTISWITCH+3) )
+                    wall[x].picnum = MULTISWITCH;
+                break;
+            case ACCESSSWITCH:
+            case ACCESSSWITCH2:
+            case SLOTDOOR:
+            case LIGHTSWITCH:
+            case SPACELIGHTSWITCH:
+            case SPACEDOORSWITCH:
+            case LIGHTSWITCH2:
+            case POWERSWITCH1:
+            case LOCKSWITCH1:
+            case POWERSWITCH2:
+            case PULLSWITCH:
+            case HANDSWITCH:
+            case DIPSWITCH2:
+            case DIPSWITCH3:
+                wall[x].picnum++;
+                break;
+            case HANDSWITCH+1:
+            case PULLSWITCH+1:
+            case LIGHTSWITCH2+1:
+            case POWERSWITCH1+1:
+            case LOCKSWITCH1+1:
+            case POWERSWITCH2+1:
+            case SLOTDOOR+1:
+            case LIGHTSWITCH+1:
+            case SPACELIGHTSWITCH+1:
+            case SPACEDOORSWITCH+1:
+            case DIPSWITCH2+1:
+            case DIPSWITCH3+1:
+                wall[x].picnum--;
+                break;
+        }
+    }
+
+    if(lotag == (short) 65535)
+    {
+        ps[myconnectindex].gm = MODE_EOL;
+        if(ud.from_bonus)
+        {
+            ud.level_number = ud.from_bonus;
+            ud.m_level_number = ud.level_number;
+            ud.from_bonus = 0;
+        }
+        else
+        {
+            ud.level_number++;
+            if( (ud.volume_number && ud.level_number > 10 ) || ( ud.volume_number == 0 && ud.level_number > 5 ) )
+                ud.level_number = 0;
+            ud.m_level_number = ud.level_number;
+        }
+        return 1;
+    }
+
+    switch(picnum)
+    {
+        default:
+            if(isadoorwall(picnum) == 0) break;
+        case DIPSWITCH:
+        case DIPSWITCH+1:
+        case TECHSWITCH:
+        case TECHSWITCH+1:
+        case ALIENSWITCH:
+        case ALIENSWITCH+1:
+            if( picnum == DIPSWITCH  || picnum == DIPSWITCH+1 ||
+                picnum == ALIENSWITCH || picnum == ALIENSWITCH+1 ||
+                picnum == TECHSWITCH || picnum == TECHSWITCH+1 )
+            {
+                if( picnum == ALIENSWITCH || picnum == ALIENSWITCH+1)
+                {
+                    if(switchtype == 1)
+                        xyzsound(ALIEN_SWITCH1,w,sx,sy,ps[snum].posz);
+                    else xyzsound(ALIEN_SWITCH1,ps[snum].i,sx,sy,ps[snum].posz);
+                }
+                else
+                {
+                    if(switchtype == 1)
+                        xyzsound(SWITCH_ON,w,sx,sy,ps[snum].posz);
+                    else xyzsound(SWITCH_ON,ps[snum].i,sx,sy,ps[snum].posz);
+                }
+                if(numdips != correctdips) break;
+                xyzsound(END_OF_LEVEL_WARN,ps[snum].i,sx,sy,ps[snum].posz);
+            }
+        case DIPSWITCH2:
+        case DIPSWITCH2+1:
+        case DIPSWITCH3:
+        case DIPSWITCH3+1:
+        case MULTISWITCH:
+        case MULTISWITCH+1:
+        case MULTISWITCH+2:
+        case MULTISWITCH+3:
+        case ACCESSSWITCH:
+        case ACCESSSWITCH2:
+        case SLOTDOOR:
+        case SLOTDOOR+1:
+        case LIGHTSWITCH:
+        case LIGHTSWITCH+1:
+        case SPACELIGHTSWITCH:
+        case SPACELIGHTSWITCH+1:
+        case SPACEDOORSWITCH:
+        case SPACEDOORSWITCH+1:
+        case FRANKENSTINESWITCH:
+        case FRANKENSTINESWITCH+1:
+        case LIGHTSWITCH2:
+        case LIGHTSWITCH2+1:
+        case POWERSWITCH1:
+        case POWERSWITCH1+1:
+        case LOCKSWITCH1:
+        case LOCKSWITCH1+1:
+        case POWERSWITCH2:
+        case POWERSWITCH2+1:
+        case HANDSWITCH:
+        case HANDSWITCH+1:
+        case PULLSWITCH:
+        case PULLSWITCH+1:
+
+                if( picnum == MULTISWITCH || picnum == (MULTISWITCH+1) ||
+                    picnum == (MULTISWITCH+2) || picnum == (MULTISWITCH+3) )
+                        lotag += picnum-MULTISWITCH;
+
+                x = headspritestat[3];
+                while(x >= 0)
+                {
+                   if( ((sprite[x].hitag) == lotag) )
+                   {
+                       switch(sprite[x].lotag)
+                       {
+                           case 12:
+                                sector[sprite[x].sectnum].floorpal = 0;
+                                hittype[x].temp_data[0]++;
+                                if(hittype[x].temp_data[0] == 2)
+                                   hittype[x].temp_data[0]++;
+
+                                break;
+                           case 24:
+                           case 34:
+                           case 25:
+                               hittype[x].temp_data[4] = !hittype[x].temp_data[4];
+                               if(hittype[x].temp_data[4])
+                                   FTA(15,&ps[snum],1);
+                               else FTA(2,&ps[snum],1);
+                               break;
+                           case 21:
+                               FTA(2,&ps[screenpeek],1);
+                               break;
+                       }
+                   }
+                   x = nextspritestat[x];
+                }
+
+                operateactivators(lotag,snum);
+                operateforcefields(ps[snum].i,lotag);
+                operatemasterswitches(lotag);
+
+                if( picnum == DIPSWITCH || picnum == DIPSWITCH+1 ||
+                    picnum == ALIENSWITCH || picnum == ALIENSWITCH+1 ||
+                    picnum == TECHSWITCH || picnum == TECHSWITCH+1 ) return 1;
+
+                if( hitag == 0 && isadoorwall(picnum) == 0 )
+                {
+                    if(switchtype == 1)
+                        xyzsound(SWITCH_ON,w,sx,sy,ps[snum].posz);
+                    else xyzsound(SWITCH_ON,ps[snum].i,sx,sy,ps[snum].posz);
+                }
+                else if(hitag != 0)
+                {
+                    if(switchtype == 1 && (soundm[hitag]&4) == 0)
+                        xyzsound(hitag,w,sx,sy,ps[snum].posz);
+                    else spritesound(hitag,ps[snum].i);
+                }
+
+               return 1;
+    }
+    return 0;
+}
+
+
+void activatebysector(short sect,short j)
+{
+    short i,didit;
+
+    didit = 0;
+
+    i = headspritesect[sect];
+    while(i >= 0)
+    {
+        if(PN == ACTIVATOR)
+        {
+            operateactivators(SLT,-1);
+            didit = 1;
+//            return;
+        }
+        i = nextspritesect[i];
+    }
+
+    if(didit == 0)
+        operatesectors(sect,j);
+}
+
+void breakwall(short newpn,short spr,short dawallnum)
+{
+    wall[dawallnum].picnum = newpn;
+    spritesound(VENT_BUST,spr);
+    spritesound(GLASS_HEAVYBREAK,spr);
+    lotsofglass(spr,dawallnum,10);
+}
+
+void checkhitwall(short spr,short dawallnum,long x,long y,long z,short atwith)
+{
+    short j, i, sn = -1, darkestwall;
+    signed char nfloors,nceilings;
+    short nextj;
+    walltype *wal;
+    spritetype *s;
+
+    wal = &wall[dawallnum];
+
+    if(wal->overpicnum == MIRROR)
+    {
+        switch(atwith)
+        {
+            case HEAVYHBOMB:
+            case RADIUSEXPLOSION:
+            case RPG:
+            case HYDRENT:
+            case SEENINE:
+            case OOZFILTER:
+            case EXPLODINGBARREL:
+                lotsofglass(spr,dawallnum,70);
+                wal->cstat &= ~16;
+                wal->overpicnum = MIRRORBROKE;
+                spritesound(GLASS_HEAVYBREAK,spr);
+                return;
+        }
+    }
+
+   if( ( (wal->cstat&16) || wal->overpicnum == BIGFORCE ) && wal->nextsector >= 0 )
+       if( sector[wal->nextsector].floorz > z )
+           if( sector[wal->nextsector].floorz-sector[wal->nextsector].ceilingz )
+               switch(wal->overpicnum)
+    {
+        case W_FORCEFIELD:
+        case W_FORCEFIELD+1:
+        case W_FORCEFIELD+2:
+            wal->extra = 1; // tell the forces to animate
+        case BIGFORCE:
+            updatesector(x,y,&sn);
+            if( sn < 0 ) return;
+
+            if(atwith == -1)
+                i = EGS(sn,x,y,z,FORCERIPPLE,-127,8,8,0,0,0,spr,5);
+            else
+            {
+                if(atwith == CHAINGUN)
+                    i = EGS(sn,x,y,z,FORCERIPPLE,-127,16+sprite[spr].xrepeat,16+sprite[spr].yrepeat,0,0,0,spr,5);
+                else i = EGS(sn,x,y,z,FORCERIPPLE,-127,32,32,0,0,0,spr,5);
+            }
+
+            CS |= 18+128;
+            SA = getangle(wal->x-wall[wal->point2].x,
+                wal->y-wall[wal->point2].y)-512;
+
+            spritesound(SOMETHINGHITFORCE,i);
+
+            return;
+
+        case FANSPRITE:
+            wal->overpicnum = FANSPRITEBROKE;
+            wal->cstat &= 65535-65;
+            if(wal->nextwall >= 0)
+            {
+                wall[wal->nextwall].overpicnum = FANSPRITEBROKE;
+                wall[wal->nextwall].cstat &= 65535-65;
+            }
+            spritesound(VENT_BUST,spr);
+            spritesound(GLASS_BREAKING,spr);
+            return;
+
+        case GLASS:
+            updatesector(x,y,&sn); if( sn < 0 ) return;
+            wal->overpicnum=GLASS2;
+            lotsofglass(spr,dawallnum,10);
+            wal->cstat = 0;
+
+            if(wal->nextwall >= 0)
+                wall[wal->nextwall].cstat = 0;
+
+            i = EGS(sn,x,y,z,SECTOREFFECTOR,0,0,0,ps[0].ang,0,0,spr,3);
+            SLT = 128; T2 = 5; T3 = dawallnum;
+            spritesound(GLASS_BREAKING,i);
+            return;
+        case STAINGLASS1:
+            updatesector(x,y,&sn); if( sn < 0 ) return;
+            lotsofcolourglass(spr,dawallnum,80);
+            wal->cstat = 0;
+            if(wal->nextwall >= 0)
+                wall[wal->nextwall].cstat = 0;
+            spritesound(VENT_BUST,spr);
+            spritesound(GLASS_BREAKING,spr);
+            return;
+    }
+
+    switch(wal->picnum)
+    {
+            case COLAMACHINE:
+            case VENDMACHINE:
+                breakwall(wal->picnum+2,spr,dawallnum);
+                spritesound(VENT_BUST,spr);
+                return;
+
+            case OJ:
+            case FEMPIC2:
+            case FEMPIC3:
+
+            case SCREENBREAK6:
+            case SCREENBREAK7:
+            case SCREENBREAK8:
+
+            case SCREENBREAK1:
+            case SCREENBREAK2:
+            case SCREENBREAK3:
+            case SCREENBREAK4:
+            case SCREENBREAK5:
+
+            case SCREENBREAK9:
+            case SCREENBREAK10:
+            case SCREENBREAK11:
+            case SCREENBREAK12:
+            case SCREENBREAK13:
+            case SCREENBREAK14:
+            case SCREENBREAK15:
+            case SCREENBREAK16:
+            case SCREENBREAK17:
+            case SCREENBREAK18:
+            case SCREENBREAK19:
+            case BORNTOBEWILDSCREEN:
+
+                lotsofglass(spr,dawallnum,30);
+                wal->picnum=W_SCREENBREAK+(TRAND%3);
+                spritesound(GLASS_HEAVYBREAK,spr);
+                return;
+
+            case W_TECHWALL5:
+            case W_TECHWALL6:
+            case W_TECHWALL7:
+            case W_TECHWALL8:
+            case W_TECHWALL9:
+                breakwall(wal->picnum+1,spr,dawallnum);
+                return;
+            case W_MILKSHELF:
+                breakwall(W_MILKSHELFBROKE,spr,dawallnum);
+                return;
+
+            case W_TECHWALL10:
+                breakwall(W_HITTECHWALL10,spr,dawallnum);
+                return;
+
+            case W_TECHWALL1:
+            case W_TECHWALL11:
+            case W_TECHWALL12:
+            case W_TECHWALL13:
+            case W_TECHWALL14:
+                breakwall(W_HITTECHWALL1,spr,dawallnum);
+                return;
+
+            case W_TECHWALL15:
+                breakwall(W_HITTECHWALL15,spr,dawallnum);
+                return;
+
+            case W_TECHWALL16:
+                breakwall(W_HITTECHWALL16,spr,dawallnum);
+                return;
+
+            case W_TECHWALL2:
+                breakwall(W_HITTECHWALL2,spr,dawallnum);
+                return;
+
+            case W_TECHWALL3:
+                breakwall(W_HITTECHWALL3,spr,dawallnum);
+                return;
+
+            case W_TECHWALL4:
+                breakwall(W_HITTECHWALL4,spr,dawallnum);
+                return;
+
+            case ATM:
+                wal->picnum = ATMBROKE;
+                lotsofmoney(&sprite[spr],1+(TRAND&7));
+                spritesound(GLASS_HEAVYBREAK,spr);
+                break;
+
+            case WALLLIGHT1:
+            case WALLLIGHT2:
+            case WALLLIGHT3:
+            case WALLLIGHT4:
+            case TECHLIGHT2:
+            case TECHLIGHT4:
+
+                if( rnd(128) )
+                    spritesound(GLASS_HEAVYBREAK,spr);
+                else spritesound(GLASS_BREAKING,spr);
+                lotsofglass(spr,dawallnum,30);
+
+                if(wal->picnum == WALLLIGHT1)
+                    wal->picnum = WALLLIGHTBUST1;
+
+                if(wal->picnum == WALLLIGHT2)
+                    wal->picnum = WALLLIGHTBUST2;
+
+                if(wal->picnum == WALLLIGHT3)
+                    wal->picnum = WALLLIGHTBUST3;
+
+                if(wal->picnum == WALLLIGHT4)
+                    wal->picnum = WALLLIGHTBUST4;
+
+                if(wal->picnum == TECHLIGHT2)
+                    wal->picnum = TECHLIGHTBUST2;
+
+                if(wal->picnum == TECHLIGHT4)
+                    wal->picnum = TECHLIGHTBUST4;
+
+                if(!wal->lotag) return;
+
+                sn = wal->nextsector;
+                if(sn < 0) return;
+                darkestwall = 0;
+
+                wal = &wall[sector[sn].wallptr];
+                for(i=sector[sn].wallnum;i > 0;i--,wal++)
+                    if(wal->shade > darkestwall)
+                        darkestwall=wal->shade;
+
+                j = TRAND&1;
+                i= headspritestat[3];
+                while(i >= 0)
+                {
+                    if(SHT == wall[dawallnum].lotag && SLT == 3 )
+                    {
+                        T3 = j;
+                        T4 = darkestwall;
+                        T5 = 1;
+                    }
+                    i = nextspritestat[i];
+                }
+                break;
+    }
+}
+
+
+void checkplayerhurt(struct player_struct *p,short j)
+{
+    if( (j&49152) == 49152 )
+    {
+        j &= (MAXSPRITES-1);
+
+        switch(sprite[j].picnum)
+        {
+            case CACTUS:
+                if(p->hurt_delay < 8 )
+                {
+                    sprite[p->i].extra -= 5;
+
+                    p->hurt_delay = 16;
+                    p->pals_time = 32;
+                    p->pals[0] = 32;
+                    p->pals[1] = 0;
+                    p->pals[2] = 0;
+                    spritesound(DUKE_LONGTERM_PAIN,p->i);
+                }
+                break;
+        }
+        return;
+    }
+
+    if( (j&49152) != 32768) return;
+    j &= (MAXWALLS-1);
+
+    if( p->hurt_delay > 0 ) p->hurt_delay--;
+    else if( wall[j].cstat&85 ) switch(wall[j].overpicnum)
+    {
+        case W_FORCEFIELD:
+        case W_FORCEFIELD+1:
+        case W_FORCEFIELD+2:
+             sprite[p->i].extra -= 5;
+
+             p->hurt_delay = 16;
+             p->pals_time = 32;
+             p->pals[0] = 32;
+             p->pals[1] = 0;
+             p->pals[2] = 0;
+
+             p->posxv = -(sintable[(p->ang+512)&2047]<<8);
+             p->posyv = -(sintable[(p->ang)&2047]<<8);
+             spritesound(DUKE_LONGTERM_PAIN,p->i);
+
+             checkhitwall(p->i,j,
+                 p->posx+(sintable[(p->ang+512)&2047]>>9),
+                 p->posy+(sintable[p->ang&2047]>>9),
+                 p->posz,-1);
+
+            break;
+
+        case BIGFORCE:
+            p->hurt_delay = 26;
+            checkhitwall(p->i,j,
+                p->posx+(sintable[(p->ang+512)&2047]>>9),
+                p->posy+(sintable[p->ang&2047]>>9),
+                p->posz,-1);
+            break;
+
+    }
+}
+
+
+char checkhitceiling(short sn)
+{
+    short i, j, q, darkestwall, darkestceiling;
+    signed char nfloors,nceilings;
+    walltype *wal;
+
+    switch(sector[sn].ceilingpicnum)
+    {
+        case WALLLIGHT1:
+        case WALLLIGHT2:
+        case WALLLIGHT3:
+        case WALLLIGHT4:
+        case TECHLIGHT2:
+        case TECHLIGHT4:
+
+                ceilingglass(ps[myconnectindex].i,sn,10);
+                spritesound(GLASS_BREAKING,ps[screenpeek].i);
+
+                if(sector[sn].ceilingpicnum == WALLLIGHT1)
+                    sector[sn].ceilingpicnum = WALLLIGHTBUST1;
+
+                if(sector[sn].ceilingpicnum == WALLLIGHT2)
+                    sector[sn].ceilingpicnum = WALLLIGHTBUST2;
+
+                if(sector[sn].ceilingpicnum == WALLLIGHT3)
+                    sector[sn].ceilingpicnum = WALLLIGHTBUST3;
+
+                if(sector[sn].ceilingpicnum == WALLLIGHT4)
+                    sector[sn].ceilingpicnum = WALLLIGHTBUST4;
+
+                if(sector[sn].ceilingpicnum == TECHLIGHT2)
+                    sector[sn].ceilingpicnum = TECHLIGHTBUST2;
+
+                if(sector[sn].ceilingpicnum == TECHLIGHT4)
+                    sector[sn].ceilingpicnum = TECHLIGHTBUST4;
+
+
+                if(!sector[sn].hitag)
+                {
+                    i = headspritesect[sn];
+                    while(i >= 0)
+                    {
+                        if( PN == SECTOREFFECTOR && SLT == 12 )
+                        {
+                            j = headspritestat[3];
+                            while(j >= 0)
+                            {
+                                if( sprite[j].hitag == SHT )
+                                    hittype[j].temp_data[3] = 1;
+                                j = nextspritestat[j];
+                            }
+                            break;
+                        }
+                        i = nextspritesect[i];
+                    }
+                }
+
+                i = headspritestat[3];
+                j = TRAND&1;
+                while(i >= 0)
+                {
+                    if(SHT == (sector[sn].hitag) && SLT == 3 )
+                    {
+                        T3 = j;
+                        T5 = 1;
+                    }
+                    i = nextspritestat[i];
+                }
+
+                return 1;
+    }
+
+    return 0;
+}                                     
+
+void checkhitsprite(short i,short sn)
+{
+    short j, k, l, nextj, p;
+    spritetype *s;
+
+    i &= (MAXSPRITES-1);
+
+    switch(PN)
+    {
+        case OCEANSPRITE1:
+        case OCEANSPRITE2:
+        case OCEANSPRITE3:
+        case OCEANSPRITE4:
+        case OCEANSPRITE5:
+            spawn(i,SMALLSMOKE);
+            deletesprite(i);
+            break;
+        case QUEBALL:
+        case STRIPEBALL:
+            if(sprite[sn].picnum == QUEBALL || sprite[sn].picnum == STRIPEBALL)
+            {
+                sprite[sn].xvel = (sprite[i].xvel>>1)+(sprite[i].xvel>>2);
+                sprite[sn].ang -= (SA<<1)+1024;
+                SA = getangle(SX-sprite[sn].x,SY-sprite[sn].y)-512;
+                if(Sound[POOLBALLHIT].num < 2)
+                    spritesound(POOLBALLHIT,i);
+            }
+            else
+            {
+                if( TRAND&3 )
+                {
+                    sprite[i].xvel = 164;
+                    sprite[i].ang = sprite[sn].ang;
+                }
+                else
+                {
+                    lotsofglass(i,-1,3);
+                    deletesprite(i);
+                }
+            }
+            break;
+        case TREE1:
+        case TREE2:
+        case TIRE:
+        case CONE:
+        case BOX:
+            switch(sprite[sn].picnum)
+            {
+                case RADIUSEXPLOSION:
+                case RPG:
+                case FIRELASER:
+                case HYDRENT:
+                case HEAVYHBOMB:
+                    if(T1 == 0)
+                    {
+                        CS &= ~257;
+                        T1 = 1;
+                        spawn(i,BURNING);
+                    }
+                    break;
+            }
+            break;
+        case CACTUS:
+//        case CACTUSBROKE:
+            switch(sprite[sn].picnum)
+            {
+                case RADIUSEXPLOSION:
+                case RPG:
+                case FIRELASER:
+                case HYDRENT:
+                case HEAVYHBOMB:
+                    for(k=0;k<64;k++)
+                    {
+                        j = EGS(SECT,SX,SY,SZ-(TRAND%(48<<8)),SCRAP3+(TRAND&3),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(sprite[i].zvel>>2),i,5);
+                        sprite[j].pal = 8;
+                    }
+
+                    if(PN == CACTUS)
+                        PN = CACTUSBROKE;
+                    CS &= ~257;
+             //       else deletesprite(i);
+                    break;
+            }
+            break;
+
+        case HANGLIGHT:
+        case GENERICPOLE2:
+            for(k=0;k<6;k++)
+                EGS(SECT,SX,SY,SZ-(8<<8),SCRAP1+(TRAND&15),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(sprite[i].zvel>>2),i,5);
+            spritesound(GLASS_HEAVYBREAK,i);
+            deletesprite(i);
+            break;
+
+
+        case FANSPRITE:
+            PN = FANSPRITEBROKE;
+            CS &= (65535-257);
+            if( sector[SECT].floorpicnum == FANSHADOW )
+                sector[SECT].floorpicnum = FANSHADOWBROKE;
+
+            spritesound(GLASS_HEAVYBREAK,i);
+            s = &sprite[i];
+            for(j=0;j<16;j++) RANDOMSCRAP;
+
+            break;
+        case WATERFOUNTAIN:
+        case WATERFOUNTAIN+1:
+        case WATERFOUNTAIN+2:
+        case WATERFOUNTAIN+3:
+            PN = WATERFOUNTAINBROKE;
+            spawn(i,TOILETWATER);
+            break;
+        case SATELITE:
+        case FUELPOD:
+        case SOLARPANNEL:
+        case ANTENNA:
+            if(sprite[sn].extra != *actorscrptr[SHOTSPARK1] )
+            {
+                for(j=0;j<15;j++)
+                    EGS(SECT,SX,SY,sector[SECT].floorz-(12<<8)-(j<<9),SCRAP1+(TRAND&15),-8,64,64,
+                        TRAND&2047,(TRAND&127)+64,-(TRAND&511)-256,i,5);
+                spawn(i,EXPLOSION2);
+                deletesprite(i);
+            }
+            break;
+        case BOTTLE1:
+        case BOTTLE2:
+        case BOTTLE3:
+        case BOTTLE4:
+        case BOTTLE5:
+        case BOTTLE6:
+        case BOTTLE8:
+        case BOTTLE10:
+        case BOTTLE11:
+        case BOTTLE12:
+        case BOTTLE13:
+        case BOTTLE14:
+        case BOTTLE15:
+        case BOTTLE16:
+        case BOTTLE17:
+        case BOTTLE18:
+        case BOTTLE19:
+        case WATERFOUNTAINBROKE:
+        case DOMELITE:
+        case SUSHIPLATE1:
+        case SUSHIPLATE2:
+        case SUSHIPLATE3:
+        case SUSHIPLATE4:
+        case SUSHIPLATE5:
+        case WAITTOBESEATED:
+        case VASE:
+        case STATUEFLASH:
+        case STATUE:
+            if(PN == BOTTLE10)
+                lotsofmoney(&sprite[i],4+(TRAND&3));
+            else if(PN == STATUE || PN == STATUEFLASH)
+            {
+                lotsofcolourglass(i,-1,40);
+                spritesound(GLASS_HEAVYBREAK,i);
+            }
+            else if(PN == VASE)
+                lotsofglass(i,-1,40);
+
+            spritesound(GLASS_BREAKING,i);
+            SA = TRAND&2047;
+            lotsofglass(i,-1,8);
+            deletesprite(i);
+            break;
+        case FETUS:
+            PN = FETUSBROKE;
+            spritesound(GLASS_BREAKING,i);
+            lotsofglass(i,-1,10);
+            break;
+        case FETUSBROKE:
+            for(j=0;j<48;j++)
+            {
+                shoot(i,BLOODSPLAT1);
+                SA += 333;
+            }
+            spritesound(GLASS_HEAVYBREAK,i);
+            spritesound(SQUISHED,i);
+        case BOTTLE7:
+            spritesound(GLASS_BREAKING,i);
+            lotsofglass(i,-1,10);
+            deletesprite(i);
+            break;
+        case HYDROPLANT:
+            PN = BROKEHYDROPLANT;
+            spritesound(GLASS_BREAKING,i);
+            lotsofglass(i,-1,10);
+            break;
+
+        case FORCESPHERE:
+            sprite[i].xrepeat = 0;
+            hittype[OW].temp_data[0] = 32;
+            hittype[OW].temp_data[1] = !hittype[OW].temp_data[1];
+            hittype[OW].temp_data[2] ++;
+            spawn(i,EXPLOSION2);
+            break;
+
+        case BROKEHYDROPLANT:
+            if(CS&1)
+            {
+                spritesound(GLASS_BREAKING,i);
+                SZ += 16<<8;
+                CS = 0;
+                lotsofglass(i,-1,5);
+            }
+            break;
+
+        case TOILET:
+            PN = TOILETBROKE;
+            CS |= (TRAND&1)<<2;
+            CS &= ~257;
+            spawn(i,TOILETWATER);
+            spritesound(GLASS_BREAKING,i);
+            break;
+
+        case STALL:
+            PN = STALLBROKE;
+            CS |= (TRAND&1)<<2;
+            CS &= ~257;
+            spawn(i,TOILETWATER);
+            spritesound(GLASS_HEAVYBREAK,i);
+            break;
+
+        case HYDRENT:
+            PN = BROKEFIREHYDRENT;
+            spawn(i,TOILETWATER);
+
+//            for(k=0;k<5;k++)
+  //          {
+    //            j = EGS(SECT,SX,SY,SZ-(TRAND%(48<<8)),SCRAP3+(TRAND&3),-8,48,48,TRAND&2047,(TRAND&63)+64,-(TRAND&4095)-(sprite[i].zvel>>2),i,5);
+      //          sprite[j].pal = 2;
+        //    }
+            spritesound(GLASS_HEAVYBREAK,i);
+            break;
+
+        case GRATE1:
+            PN = BGRATE1;
+            CS &= (65535-256-1);
+            spritesound(VENT_BUST,i);
+            break;
+
+        case CIRCLEPANNEL:
+            PN = CIRCLEPANNELBROKE;
+            CS &= (65535-256-1);
+            spritesound(VENT_BUST,i);
+            break;
+        case PANNEL1:
+        case PANNEL2:
+            PN = BPANNEL1;
+            CS &= (65535-256-1);
+            spritesound(VENT_BUST,i);
+            break;
+        case PANNEL3:
+            PN = BPANNEL3;
+            CS &= (65535-256-1);
+            spritesound(VENT_BUST,i);
+            break;
+        case PIPE1:
+        case PIPE2:
+        case PIPE3:
+        case PIPE4:
+        case PIPE5:
+        case PIPE6:
+            switch(PN)
+            {
+                case PIPE1:PN=PIPE1B;break;
+                case PIPE2:PN=PIPE2B;break;
+                case PIPE3:PN=PIPE3B;break;
+                case PIPE4:PN=PIPE4B;break;
+                case PIPE5:PN=PIPE5B;break;
+                case PIPE6:PN=PIPE6B;break;
+            }
+
+            j = spawn(i,STEAM);
+            sprite[j].z = sector[SECT].floorz-(32<<8);
+            break;
+
+        case MONK:
+        case LUKE:
+        case INDY:
+        case JURYGUY:
+            spritesound(SLT,i);
+            spawn(i,SHT);
+        case SPACEMARINE:
+            sprite[i].extra -= sprite[sn].extra;
+            if(sprite[i].extra > 0) break;
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT1);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT2);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT3);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT4);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT1);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT2);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT3);
+            SA = TRAND&2047;
+            shoot(i,BLOODSPLAT4);
+            guts(&sprite[i],JIBS1,1,myconnectindex);
+            guts(&sprite[i],JIBS2,2,myconnectindex);
+            guts(&sprite[i],JIBS3,3,myconnectindex);
+            guts(&sprite[i],JIBS4,4,myconnectindex);
+            guts(&sprite[i],JIBS5,1,myconnectindex);
+            guts(&sprite[i],JIBS3,6,myconnectindex);
+            sound(SQUISHED);
+            deletesprite(i);
+            break;
+        case CHAIR1:
+        case CHAIR2:
+            PN = BROKENCHAIR;
+            CS = 0;
+            break;
+        case CHAIR3:
+        case MOVIECAMERA:
+        case SCALE:
+        case VACUUM:
+        case CAMERALIGHT:
+        case IVUNIT:
+        case POT1:
+        case POT2:
+        case POT3:
+        case TRIPODCAMERA:
+            spritesound(GLASS_HEAVYBREAK,i);
+            s = &sprite[i];
+            for(j=0;j<16;j++) RANDOMSCRAP;
+            deletesprite(i);
+            break;
+        case PLAYERONWATER:
+            i = OW;
+        default:
+            if( (sprite[i].cstat&16) && SHT == 0 && SLT == 0 && sprite[i].statnum == 0)
+                break;
+
+            if( ( sprite[sn].picnum == FREEZEBLAST || sprite[sn].owner != i ) && sprite[i].statnum != 4)
+            {
+                if( badguy(&sprite[i]) == 1)
+                {
+                    if(sprite[sn].picnum == RPG) sprite[sn].extra <<= 1;
+
+                    if( (PN != DRONE) && (PN != ROTATEGUN) && (PN != COMMANDER) && (PN < GREENSLIME || PN > GREENSLIME+7) )
+                        if(sprite[sn].picnum != FREEZEBLAST )
+                            if( actortype[PN] == 0 )
+                    {
+                        j = spawn(sn,JIBS6);
+                        if(sprite[sn].pal == 6)
+                            sprite[j].pal = 6;
+                        sprite[j].z += (4<<8);
+                        sprite[j].xvel = 16;
+                        sprite[j].xrepeat = sprite[j].yrepeat = 24;
+                        sprite[j].ang += 32-(TRAND&63);
+                    }
+
+                    j = sprite[sn].owner;
+
+                    if( j >= 0 && sprite[j].picnum == APLAYER && PN != ROTATEGUN && PN != DRONE )
+                        if( ps[sprite[j].yvel].curr_weapon == SHOTGUN_WEAPON )
+                    {
+                        shoot(i,BLOODSPLAT3);
+                        shoot(i,BLOODSPLAT1);
+                        shoot(i,BLOODSPLAT2);
+                        shoot(i,BLOODSPLAT4);
+                    }
+
+                    if( PN != TANK && PN != BOSS1 && PN != BOSS4 && PN != BOSS2 && PN != BOSS3 && PN != RECON && PN != ROTATEGUN )
+                    {
+                        if( (sprite[i].cstat&48) == 0 )
+                            SA = (sprite[sn].ang+1024)&2047;
+                        sprite[i].xvel = -(sprite[sn].extra<<2);
+                        j = SECT;
+                        pushmove(&SX,&SY,&SZ,&j,128L,(4L<<8),(4L<<8),CLIPMASK0);
+                        if(j != SECT && j >= 0 && j < MAXSECTORS)
+                            changespritesect(i,j);
+                    }
+
+                    if(sprite[i].statnum == 2)
+                    {
+                        changespritestat(i,1);
+                        hittype[i].timetosleep = SLEEPTIME;
+                    }
+                    if( ( RX < 24 || PN == SHARK) && sprite[sn].picnum == SHRINKSPARK) return;
+                }
+
+                if( sprite[i].statnum != 2 )
+                {
+                    if( sprite[sn].picnum == FREEZEBLAST && ( (PN == APLAYER && sprite[i].pal == 1 ) || ( freezerhurtowner == 0 && sprite[sn].owner == i ) ) )
+                        return;
+
+                    hittype[i].picnum = sprite[sn].picnum;
+                    hittype[i].extra += sprite[sn].extra;
+                    hittype[i].ang = sprite[sn].ang;
+                    hittype[i].owner = sprite[sn].owner;
+                }
+
+                if(sprite[i].statnum == 10)
+                {
+                    p = sprite[i].yvel;
+                    if(ps[p].newowner >= 0)
+                    {
+                        ps[p].newowner = -1;
+                        ps[p].posx = ps[p].oposx;
+                        ps[p].posy = ps[p].oposy;
+                        ps[p].posz = ps[p].oposz;
+                        ps[p].ang = ps[p].oang;
+
+                        updatesector(ps[p].posx,ps[p].posy,&ps[p].cursectnum);
+                        setpal(&ps[p]);
+
+                        j = headspritestat[1];
+                        while(j >= 0)
+                        {
+                            if(sprite[j].picnum==CAMERA1) sprite[j].yvel = 0;
+                            j = nextspritestat[j];
+                        }
+                    }
+
+                    if( RX < 24 && sprite[sn].picnum == SHRINKSPARK)
+                        return;
+
+                    if( sprite[hittype[i].owner].picnum != APLAYER)
+                        if(ud.player_skill >= 3)
+                            sprite[sn].extra += (sprite[sn].extra>>1);
+                }
+
+            }
+            break;
+    }
+}
+
+
+void allignwarpelevators(void)
+{
+    short i, j;
+
+    i = headspritestat[3];
+    while(i >= 0)
+    {
+        if( SLT == 17 && SS > 16)
+        {
+            j = headspritestat[3];
+            while(j >= 0)
+            {
+                if( (sprite[j].lotag) == 17 && i != j &&
+                    (SHT) == (sprite[j].hitag) )
+                {
+                    sector[sprite[j].sectnum].floorz =
+                        sector[SECT].floorz;
+                    sector[sprite[j].sectnum].ceilingz =
+                        sector[SECT].ceilingz;
+                }
+
+                j = nextspritestat[j];
+            }
+        }
+        i = nextspritestat[i];
+    }
+}
+
+
+
+
+void cheatkeys(short snum)
+{
+    short i, k;
+    char dainv;
+    unsigned long sb_snum, j;
+    struct player_struct *p;
+	char playing_old_demo = 0;
+
+    sb_snum = sync[snum].bits;
+    p = &ps[snum];
+
+    if(p->cheat_phase == 1) return;
+
+    i = p->aim_mode;
+    p->aim_mode = (sb_snum>>23)&1;
+    if(p->aim_mode < i)
+        p->return_to_center = 9;
+
+    if( (sb_snum&(1<<22)) && p->quick_kick == 0)
+        if( !PLUTOPAK || p->curr_weapon != KNEE_WEAPON || p->kickback_pic == 0 )
+		// FIX_00066: Removed the QuickKick restrictions for 1.3/1.3d (like 1.3d dos version behavior)
+    {
+        p->quick_kick = 14;
+        FTA(80,p,0);
+    }
+		
+	// FIX_00040: Preventing multi keypress locks
+	playing_old_demo =	ud.playing_demo_rev == BYTEVERSION_27     ||
+						ud.playing_demo_rev == BYTEVERSION_28     || 
+						ud.playing_demo_rev == BYTEVERSION_116    || 
+						ud.playing_demo_rev == BYTEVERSION_117;
+
+	if(!playing_old_demo)
+	{
+		// A more efficient toggle (make old demos going oos):
+		j = sb_snum & ((15<<8)|(1<<12)|(1<<15)|(1<<16)|(1<<22)|(1<<19)|(1<<20)|(1<<21)|(1<<24)| \
+						(1<<25)|(1<<27)|(1<<28)|(1<<29)|(1<<30)|(1<<31));
+		sb_snum = j & ~p->interface_toggle_flag;
+		p->interface_toggle_flag = j;
+	}
+
+    if( playing_old_demo && !(sb_snum&((15<<8)|(1<<12)|(1<<15)|(1<<16)|(1<<22)|(1<<19)|(1<<20)|(1<<21)|(1<<24)|(1<<25)|(1<<27)|(1<<28)|(1<<29)|(1<<30)|(1<<31))) )
+        p->interface_toggle_flag = 0;
+    else if((p->interface_toggle_flag == 0 && ( sb_snum&(1<<17) ) == 0) && playing_old_demo ||
+		(sb_snum && ( sync[snum].bits&(1<<17) ) == 0) && !playing_old_demo)
+    {
+		if(playing_old_demo)
+			p->interface_toggle_flag = 1;
+
+        if( sb_snum&(1<<21) )
+        {
+            KB_ClearKeyDown( sc_Pause );
+            ud.pause_on = !ud.pause_on;
+            if( ud.pause_on == 1 && sb_snum&(1<<5) ) ud.pause_on = 2;
+            if(ud.pause_on)
+            {
+                MUSIC_Pause();
+                FX_StopAllSounds();
+                clearsoundlocks();
+            }
+            else
+            {
+                if(MusicToggle) MUSIC_Continue();
+                pub = NUMPAGES;
+                pus = NUMPAGES;
+            }
+        }
+
+        if(ud.pause_on) return;
+        
+        if(sprite[p->i].extra <= 0) return;
+
+        if( sb_snum&(1<<30) && p->newowner == -1 )
+        {
+            switch(p->inven_icon)
+            {
+                case 4: sb_snum |= (1<<25);break;
+                case 3: sb_snum |= (1<<24);break;
+                case 5: sb_snum |= (1<<15);break;
+                case 1: sb_snum |= (1<<16);break;
+                case 2: sb_snum |= (1<<12);break;
+            }
+        }
+
+        if( sb_snum&(1<<15) && p->heat_amount > 0 )
+        {
+            p->heat_on = !p->heat_on;
+            setpal(p);
+            p->inven_icon = 5;
+            spritesound(NITEVISION_ONOFF,p->i);
+            FTA(106+(!p->heat_on),p,0);
+        }
+
+        if( (sb_snum&(1<<12)) )
+        {
+            if(p->steroids_amount == 400 )
+            {
+                p->steroids_amount--;
+                spritesound(DUKE_TAKEPILLS,p->i);
+                p->inven_icon = 2;
+                FTA(12,p,0);
+            }
+            return;
+        }
+
+        if(p->newowner == -1)
+            if( sb_snum&(1<<20) || sb_snum&(1<<27) || p->refresh_inventory)
+        {
+            p->invdisptime = 26*2;
+
+            if( sb_snum&(1<<27) ) k = 1;
+            else k = 0;
+
+            if(p->refresh_inventory) p->refresh_inventory = 0;
+            dainv = p->inven_icon;
+
+            i = 0;
+            CHECKINV1:
+
+            if(i < 9)
+            {
+                i++;
+
+                switch(dainv)
+                {
+                    case 4:
+                        if(p->jetpack_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 5;
+                        else dainv = 3;
+                        goto CHECKINV1;
+                    case 6:
+                        if(p->scuba_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 7;
+                        else dainv = 5;
+                        goto CHECKINV1;
+                    case 2:
+                        if(p->steroids_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 3;
+                        else dainv = 1;
+                        goto CHECKINV1;
+                    case 3:
+                        if(p->holoduke_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 4;
+                        else dainv = 2;
+                        goto CHECKINV1;
+                    case 0:
+                    case 1:
+                        if(p->firstaid_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 2;
+                        else dainv = 7;
+                        goto CHECKINV1;
+                    case 5:
+                        if(p->heat_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 6;
+                        else dainv = 4;
+                        goto CHECKINV1;
+                    case 7:
+                        if(p->boot_amount > 0 && i > 1)
+                            break;
+                        if(k) dainv = 1;
+                        else dainv = 6;
+                        goto CHECKINV1;
+                }
+            }
+            else dainv = 0;
+            p->inven_icon = dainv;
+
+            switch(dainv)
+            {
+                case 1: FTA(3,p,0);break;
+                case 2: FTA(90,p,0);break;
+                case 3: FTA(91,p,0);break;
+                case 4: FTA(88,p,0);break;
+                case 5: FTA(101,p,0);break;
+                case 6: FTA(89,p,0);break;
+                case 7: FTA(6,p,0);break;
+            }
+        }
+
+        j = ( (sb_snum&(15<<8))>>8 ) - 1;
+
+        if( j > 0 && p->kickback_pic > 0)
+            p->wantweaponfire = j;
+
+        if(p->last_pissed_time <= (26*218) && p->show_empty_weapon == 0 && p->kickback_pic == 0 && p->quick_kick == 0 && sprite[p->i].xrepeat > 32 && p->access_incs == 0 && p->knee_incs == 0 )
+        {
+            if(  ( p->weapon_pos == 0 || ( p->holster_weapon && p->weapon_pos == -9 ) ) )
+            {
+                if(j == 10 || j == 11)
+                {
+                    k = p->curr_weapon;
+                    j = ( j == 10 ? -1 : 1 );
+                    i = 0;
+
+                    while( ( k >= 0 && k < 10 ) || ( k == GROW_WEAPON && (p->subweapon&(1<<GROW_WEAPON) ) ) )
+                    {
+                        if(k == GROW_WEAPON)
+                        {
+                            if(j == -1)
+                                k = 5;
+                            else k = 7;
+
+                        }
+                        else
+                        {
+                           k += j;
+                           if( k == 6 && p->subweapon&(1<<GROW_WEAPON) )
+                               k = GROW_WEAPON;
+                        }
+
+                        if(k == -1) k = 9;
+                        else if(k == 10) k = 0;
+
+                        if( p->gotweapon[k] && p->ammo_amount[k] > 0 )
+                        {
+                            if( k == SHRINKER_WEAPON && p->subweapon&(1<<GROW_WEAPON) )
+                                k = GROW_WEAPON;
+                            j = k;
+                            break;
+                        }
+                        else
+                            if(k == GROW_WEAPON && p->ammo_amount[GROW_WEAPON] == 0 && p->gotweapon[SHRINKER_WEAPON] && p->ammo_amount[SHRINKER_WEAPON] > 0)
+                        {
+                            j = SHRINKER_WEAPON;
+                            p->subweapon &= ~(1<<GROW_WEAPON);
+                            break;
+                        }
+                        else
+                            if(k == SHRINKER_WEAPON && p->ammo_amount[SHRINKER_WEAPON] == 0 && p->gotweapon[SHRINKER_WEAPON] && p->ammo_amount[GROW_WEAPON] > 0)
+                        {
+                            j = GROW_WEAPON;
+                            p->subweapon |= (1<<GROW_WEAPON);
+                            break;
+                        }
+
+                        i++;
+                        if(i == 10)
+                        {
+                            addweapon( p, KNEE_WEAPON );
+                            break;
+                        }
+                    }
+                }
+
+                k = -1;
+
+
+                if( j == HANDBOMB_WEAPON && p->ammo_amount[HANDBOMB_WEAPON] == 0 )
+                {
+                    k = headspritestat[1];
+                    while(k >= 0)
+                    {
+                        if( sprite[k].picnum == HEAVYHBOMB && sprite[k].owner == p->i )
+                        {
+                            p->gotweapon[HANDBOMB_WEAPON] = 1;
+                            j = HANDREMOTE_WEAPON;
+                            break;
+                        }
+                        k = nextspritestat[k];
+                    }
+                }
+
+                if(j == SHRINKER_WEAPON)
+                {
+                    if(screenpeek == snum) pus = NUMPAGES;
+
+                    if( p->curr_weapon != GROW_WEAPON && p->curr_weapon != SHRINKER_WEAPON )
+                    {
+                        if( p->ammo_amount[GROW_WEAPON] > 0 )
+                        {
+                            if( (p->subweapon&(1<<GROW_WEAPON)) == (1<<GROW_WEAPON) )
+                                j = GROW_WEAPON;
+                            else if(p->ammo_amount[SHRINKER_WEAPON] == 0)
+                            {
+                                j = GROW_WEAPON;
+                                p->subweapon |= (1<<GROW_WEAPON);
+                            }
+                        }
+                        else if( p->ammo_amount[SHRINKER_WEAPON] > 0 )
+                            p->subweapon &= ~(1<<GROW_WEAPON);
+                    }
+                    else if( p->curr_weapon == SHRINKER_WEAPON )
+                    {
+                        p->subweapon |= (1<<GROW_WEAPON);
+                        j = GROW_WEAPON;
+                    }
+                    else
+                        p->subweapon &= ~(1<<GROW_WEAPON);
+                }
+
+                if(p->holster_weapon)
+                {
+                    sb_snum |= 1<<19;
+                    p->weapon_pos = -9;
+                }
+                else if( p->gotweapon[j] && p->curr_weapon != j ) switch(j)
+                {
+                    case KNEE_WEAPON:
+                        addweapon( p, KNEE_WEAPON );
+                        break;
+                    case PISTOL_WEAPON:
+                        if ( p->ammo_amount[PISTOL_WEAPON] == 0 )
+                            if(p->show_empty_weapon == 0)
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, PISTOL_WEAPON );
+                        break;
+                    case SHOTGUN_WEAPON:
+                        if( p->ammo_amount[SHOTGUN_WEAPON] == 0 && p->show_empty_weapon == 0)
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, SHOTGUN_WEAPON);
+                        break;
+                    case CHAINGUN_WEAPON:
+                        if( p->ammo_amount[CHAINGUN_WEAPON] == 0 && p->show_empty_weapon == 0)
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, CHAINGUN_WEAPON);
+                        break;
+                    case RPG_WEAPON:
+                        if( p->ammo_amount[RPG_WEAPON] == 0 )
+                            if(p->show_empty_weapon == 0)
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, RPG_WEAPON );
+                        break;
+                    case DEVISTATOR_WEAPON:
+                        if( p->ammo_amount[DEVISTATOR_WEAPON] == 0 && p->show_empty_weapon == 0 )
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, DEVISTATOR_WEAPON );
+                        break;
+                    case FREEZE_WEAPON:
+                        if( p->ammo_amount[FREEZE_WEAPON] == 0 && p->show_empty_weapon == 0)
+                        {
+                            p->last_full_weapon = p->curr_weapon;
+                            p->show_empty_weapon = 32;
+                        }
+                        addweapon( p, FREEZE_WEAPON );
+                        break;
+                    case GROW_WEAPON:
+                    case SHRINKER_WEAPON:
+
+                        if( p->ammo_amount[j] == 0 && p->show_empty_weapon == 0)
+                        {
+                            p->show_empty_weapon = 32;
+                            p->last_full_weapon = p->curr_weapon;
+                        }
+
+                        addweapon(p, j);
+                        break;
+                    case HANDREMOTE_WEAPON:
+                        if(k >= 0) // Found in list of [1]'s
+                        {
+                            p->curr_weapon = HANDREMOTE_WEAPON;
+                            p->last_weapon = -1;
+                            p->weapon_pos = 10;
+                        }
+                        break;
+                    case HANDBOMB_WEAPON:
+                        if( p->ammo_amount[HANDBOMB_WEAPON] > 0 && p->gotweapon[HANDBOMB_WEAPON] )
+                            addweapon( p, HANDBOMB_WEAPON );
+                        break;
+                    case TRIPBOMB_WEAPON:
+                        if( p->ammo_amount[TRIPBOMB_WEAPON] > 0 && p->gotweapon[TRIPBOMB_WEAPON] )
+                            addweapon( p, TRIPBOMB_WEAPON );
+                        break;
+                }
+            }
+
+            if( sb_snum&(1<<19) )
+			{
+                if( p->curr_weapon > KNEE_WEAPON )
+                {
+                    if(p->holster_weapon == 0 && p->weapon_pos == 0)
+                    {
+                        p->holster_weapon = 1;
+                        p->weapon_pos = -1;
+                        FTA(73,p,1);
+                    }
+                    else if(p->holster_weapon == 1 && p->weapon_pos == -9)
+                    {
+                        p->holster_weapon = 0;
+                        p->weapon_pos = 10;
+                        FTA(74,p,1);
+                    }
+                }
+            }
+        }
+
+        if( sb_snum&(1<<24) && p->newowner == -1 )
+        {
+            if( p->holoduke_on == -1 )
+            {
+
+                if( p->holoduke_amount > 0 )
+                {
+                    p->inven_icon = 3;
+
+                    p->holoduke_on = i =
+                        EGS(p->cursectnum,
+                        p->posx,
+                        p->posy,
+                        p->posz+(30<<8),APLAYER,-64,0,0,p->ang,0,0,-1,10);
+                    T4 = T5 = 0;
+                    SP = snum;
+                    sprite[i].extra = 0;
+                    FTA(47,p,0);
+                }
+                else FTA(49,p,0);
+                spritesound(TELEPORTER,p->holoduke_on);
+
+            }
+            else
+            {
+                spritesound(TELEPORTER,p->holoduke_on);
+                p->holoduke_on = -1;
+                FTA(48,p,0);
+            }
+        }
+
+        if( sb_snum&(1<<16) )
+        {
+            if( p->firstaid_amount > 0 && sprite[p->i].extra < max_player_health )
+            {
+                j = max_player_health-sprite[p->i].extra;
+
+                if(p->firstaid_amount > j)
+                {
+                    p->firstaid_amount -= j;
+                    sprite[p->i].extra = max_player_health;
+                    p->inven_icon = 1;
+                }
+                else
+                {
+                    sprite[p->i].extra += p->firstaid_amount;
+                    p->firstaid_amount = 0;
+                    checkavailinven(p);
+                }
+                spritesound(DUKE_USEMEDKIT,p->i);
+            }
+        }
+
+        if( sb_snum&(1<<25) && p->newowner == -1)
+        {
+            if( p->jetpack_amount > 0 )
+            {
+                p->jetpack_on = !p->jetpack_on;
+                if(p->jetpack_on)
+                {
+                    p->inven_icon = 4;
+                    if(p->scream_voice > FX_Ok)
+                    {
+                        FX_StopSound(p->scream_voice);
+                        testcallback(DUKE_SCREAM);
+                        p->scream_voice = FX_Ok;
+                    }
+
+                    spritesound(DUKE_JETPACK_ON,p->i);
+
+                    FTA(52,p,0);
+                }
+                else
+                {
+                    p->hard_landing = 0;
+                    p->poszv = 0;
+                    spritesound(DUKE_JETPACK_OFF,p->i);
+                    stopsound(DUKE_JETPACK_IDLE);
+                    stopsound(DUKE_JETPACK_ON);
+                    FTA(53,p,0);
+                }
+            }
+            else FTA(50,p,0);
+        }
+
+        if(sb_snum&(1<<28) && p->one_eighty_count == 0)
+            p->one_eighty_count = -1024;
+    }
+}
+
+void checksectors(short snum)
+{
+    long i = -1,oldz;
+    struct player_struct *p;
+    short j,hitscanwall;
+
+    p = &ps[snum];
+
+    switch(sector[p->cursectnum].lotag)
+    {
+
+        case 32767:
+            sector[p->cursectnum].lotag = 0;
+            FTA(9,p,0); // secret place found
+            p->secret_rooms++;
+            return;
+        case -1:
+            for(i=connecthead;i>=0;i=connectpoint2[i])
+                ps[i].gm = MODE_EOL;
+            sector[p->cursectnum].lotag = 0;
+            if(ud.from_bonus)
+            {
+                ud.level_number = ud.from_bonus;
+                ud.m_level_number = ud.level_number;
+                ud.from_bonus = 0;
+            }
+            else
+            {
+                ud.level_number++;
+                if( (ud.volume_number && ud.level_number > 10 ) || ud.level_number > 5 )
+                    ud.level_number = 0;
+                ud.m_level_number = ud.level_number;
+            }
+            return;
+        case -2:
+            sector[p->cursectnum].lotag = 0;
+            p->timebeforeexit = 26*8;
+            p->customexitsound = sector[p->cursectnum].hitag;
+            return;
+        default:
+            if(sector[p->cursectnum].lotag >= 10000 && sector[p->cursectnum].lotag < 16383)
+            {
+                if(snum == screenpeek || ud.coop == 1 )
+                    spritesound(sector[p->cursectnum].lotag-10000,p->i);
+                sector[p->cursectnum].lotag = 0;
+            }
+            break;
+
+    }
+
+    //After this point the the player effects the map with space
+
+    if(p->gm&MODE_TYPE || sprite[p->i].extra <= 0) return;
+
+    if( ud.cashman && sync[snum].bits&(1<<29) )
+        lotsofmoney(&sprite[p->i],2);
+
+    if(p->newowner >= 0)
+    {
+        if( klabs(sync[snum].svel) > 768 || klabs(sync[snum].fvel) > 768 )
+        {
+            i = -1;
+            goto CLEARCAMERAS;
+        }
+    }
+
+    if( !(sync[snum].bits&(1<<29)) && !(sync[snum].bits&(1<<31)))
+        p->toggle_key_flag = 0;
+
+    else if(!p->toggle_key_flag)
+    {
+
+        if( (sync[snum].bits&(1<<31)) )
+        {
+            if( p->newowner >= 0 )
+            {
+                i = -1;
+                goto CLEARCAMERAS;
+            }
+            return;
+        }
+
+        neartagsprite = -1;
+        p->toggle_key_flag = 1;
+        hitscanwall = -1;
+
+        i = hitawall(p,&hitscanwall);
+
+        if(i < 1280 && hitscanwall >= 0 && wall[hitscanwall].overpicnum == MIRROR)
+            if( wall[hitscanwall].lotag > 0 && Sound[wall[hitscanwall].lotag].num == 0 && snum == screenpeek)
+        {
+            spritesound(wall[hitscanwall].lotag,p->i);
+            return;
+        }
+
+        if(hitscanwall >= 0 && (wall[hitscanwall].cstat&16) )
+            switch(wall[hitscanwall].overpicnum)
+        {
+            default:
+                if(wall[hitscanwall].lotag)
+                    return;
+        }
+
+        if(p->newowner >= 0)
+            neartag(p->oposx,p->oposy,p->oposz,sprite[p->i].sectnum,p->oang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,1280L,1);
+        else
+        {
+            neartag(p->posx,p->posy,p->posz,sprite[p->i].sectnum,p->oang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,1280L,1);
+            if(neartagsprite == -1 && neartagwall == -1 && neartagsector == -1)
+                neartag(p->posx,p->posy,p->posz+(8<<8),sprite[p->i].sectnum,p->oang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,1280L,1);
+            if(neartagsprite == -1 && neartagwall == -1 && neartagsector == -1)
+                neartag(p->posx,p->posy,p->posz+(16<<8),sprite[p->i].sectnum,p->oang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,1280L,1);
+            if(neartagsprite == -1 && neartagwall == -1 && neartagsector == -1)
+            {
+                neartag(p->posx,p->posy,p->posz+(16<<8),sprite[p->i].sectnum,p->oang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,1280L,3);
+                if(neartagsprite >= 0)
+                {
+                    switch(sprite[neartagsprite].picnum)
+                    {
+                        case FEM1:
+                        case FEM2:
+                        case FEM3:
+                        case FEM4:
+                        case FEM5:
+                        case FEM6:
+                        case FEM7:
+                        case FEM8:
+                        case FEM9:
+                        case FEM10:
+                        case PODFEM1:
+                        case NAKED1:
+                        case STATUE:
+                        case TOUGHGAL:
+                            return;
+                    }
+                }
+
+                neartagsprite = -1;
+                neartagwall = -1;
+                neartagsector = -1;
+            }
+        }
+
+        if(p->newowner == -1 && neartagsprite == -1 && neartagsector == -1 && neartagwall == -1 )
+            if( isanunderoperator(sector[sprite[p->i].sectnum].lotag) )
+                neartagsector = sprite[p->i].sectnum;
+
+        if( neartagsector >= 0 && (sector[neartagsector].lotag&16384) )
+            return;
+
+        if( neartagsprite == -1 && neartagwall == -1)
+            if(sector[p->cursectnum].lotag == 2 )
+            {
+                oldz = hitasprite(p->i,&neartagsprite);
+                if(oldz > 1280) neartagsprite = -1;
+            }
+
+        if(neartagsprite >= 0)
+        {
+            if( checkhitswitch(snum,neartagsprite,1) ) return;
+
+            switch(sprite[neartagsprite].picnum)
+            {
+                case TOILET:
+                case STALL:
+                    if(p->last_pissed_time == 0)
+                    {
+                        if(ud.lockout == 0) spritesound(DUKE_URINATE,p->i);
+
+                        p->last_pissed_time = 26*220;
+                        p->transporter_hold = 29*2;
+                        if(p->holster_weapon == 0)
+                        {
+                            p->holster_weapon = 1;
+                            p->weapon_pos = -1;
+                        }
+                        if(sprite[p->i].extra <= (max_player_health-(max_player_health/10) ) )
+                        {
+                            sprite[p->i].extra += max_player_health/10;
+                            p->last_extra = sprite[p->i].extra;
+                        }
+                        else if(sprite[p->i].extra < max_player_health )
+                             sprite[p->i].extra = max_player_health;
+                    }
+                    else if(Sound[FLUSH_TOILET].num == 0)
+                        spritesound(FLUSH_TOILET,p->i);
+                    return;
+
+                case NUKEBUTTON:
+
+                    hitawall(p,&j);
+                    if(j >= 0 && wall[j].overpicnum == 0)
+                        if(hittype[neartagsprite].temp_data[0] == 0)
+                    {
+                        hittype[neartagsprite].temp_data[0] = 1;
+                        sprite[neartagsprite].owner = p->i;
+                        p->buttonpalette = sprite[neartagsprite].pal;
+                        if(p->buttonpalette)
+                            ud.secretlevel = sprite[neartagsprite].lotag;
+                        else ud.secretlevel = 0;
+                    }
+                    return;
+                case WATERFOUNTAIN:
+                    if(hittype[neartagsprite].temp_data[0] != 1)
+                    {
+                        hittype[neartagsprite].temp_data[0] = 1;
+                        sprite[neartagsprite].owner = p->i;
+
+                        if(sprite[p->i].extra < max_player_health)
+                        {
+                            sprite[p->i].extra++;
+                            spritesound(DUKE_DRINKING,p->i);
+                        }
+                    }
+                    return;
+                case PLUG:
+                    spritesound(SHORT_CIRCUIT,p->i);
+                    sprite[p->i].extra -= 2+(TRAND&3);
+                    p->pals[0] = 48;
+                    p->pals[1] = 48;
+                    p->pals[2] = 64;
+                    p->pals_time = 32;
+                    break;
+                case VIEWSCREEN:
+                case VIEWSCREEN2:
+                    {
+                        i = headspritestat[1];
+
+                        while(i >= 0)
+                        {
+                            if( PN == CAMERA1 && SP == 0 && sprite[neartagsprite].hitag == SLT )
+                            {
+                                SP = 1; //Using this camera
+                                spritesound(MONITOR_ACTIVE,neartagsprite);
+
+                                sprite[neartagsprite].owner = i;
+                                sprite[neartagsprite].yvel = 1;
+
+
+                                j = p->cursectnum;
+                                p->cursectnum = SECT;
+                                setpal(p);
+                                p->cursectnum = j;
+
+                                // parallaxtype = 2;
+                                p->newowner = i;
+                                return;
+                            }
+                            i = nextspritestat[i];
+                       }
+                    }
+
+                    CLEARCAMERAS:
+
+                    if(i < 0)
+                    {
+                        p->posx = p->oposx;
+                        p->posy = p->oposy;
+                        p->posz = p->oposz;
+                        p->ang = p->oang;
+                        p->newowner = -1;
+
+                        updatesector(p->posx,p->posy,&p->cursectnum);
+                        setpal(p);
+
+
+                        i = headspritestat[1];
+                        while(i >= 0)
+                        {
+                            if(PN==CAMERA1) SP = 0;
+                            i = nextspritestat[i];
+                        }
+                    }
+                    else if(p->newowner >= 0)
+                        p->newowner = -1;
+
+                    if( KB_KeyPressed(sc_Escape) )
+                        KB_ClearKeyDown(sc_Escape);
+
+                    return;
+            }
+        }
+
+        if( (sync[snum].bits&(1<<29)) == 0 ) return;
+        else if(p->newowner >= 0) { i = -1; goto CLEARCAMERAS; }
+
+        if(neartagwall == -1 && neartagsector == -1 && neartagsprite == -1)
+            if( klabs(hits(p->i)) < 512 )
+        {
+            if( (TRAND&255) < 16 )
+                spritesound(DUKE_SEARCH2,p->i);
+            else spritesound(DUKE_SEARCH,p->i);
+            return;
+        }
+
+        if( neartagwall >= 0 )
+        {
+            if( wall[neartagwall].lotag > 0 && isadoorwall(wall[neartagwall].picnum) )
+            {
+                if(hitscanwall == neartagwall || hitscanwall == -1)
+                    checkhitswitch(snum,neartagwall,0);
+                return;
+            }
+            else if(p->newowner >= 0)
+            {
+                i = -1;
+                goto CLEARCAMERAS;
+            }
+        }
+
+        if( neartagsector >= 0 && (sector[neartagsector].lotag&16384) == 0 && isanearoperator(sector[neartagsector].lotag) )
+        {
+            i = headspritesect[neartagsector];
+            while(i >= 0)
+            {
+                if( PN == ACTIVATOR || PN == MASTERSWITCH )
+                    return;
+                i = nextspritesect[i];
+            }
+            operatesectors(neartagsector,p->i);
+        }
+        else if( (sector[sprite[p->i].sectnum].lotag&16384) == 0 )
+        {
+            if( isanunderoperator(sector[sprite[p->i].sectnum].lotag) )
+            {
+                i = headspritesect[sprite[p->i].sectnum];
+                while(i >= 0)
+                {
+                    if(PN == ACTIVATOR || PN == MASTERSWITCH) return;
+                    i = nextspritesect[i];
+                }
+                operatesectors(sprite[p->i].sectnum,p->i);
+            }
+            else checkhitswitch(snum,neartagwall,0);
+        }
+    }
+}
+
+
+
--- /dev/null
+++ b/Game/src/sounddebugdefs.h
@@ -1,0 +1,8 @@
+#ifndef _SOUND_DEBUG_DEFS_H_
+#define _SOUND_DEBUG_DEFS_H_
+
+extern unsigned long sounddebugActiveSounds;
+extern unsigned long sounddebugAllocateSoundCalls;
+extern unsigned long sounddebugDeallocateSoundCalls;
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/Game/src/soundefs.h
@@ -1,0 +1,1233 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#define SECTOREFFECTOR 1
+#define ACTIVATOR 2
+#define TOUCHPLATE 3
+#define ACTIVATORLOCKED 4
+#define MUSICANDSFX 5
+#define LOCATORS 6
+#define CYCLER 7
+#define MASTERSWITCH 8
+#define RESPAWN 9
+#define GPSPEED 10
+#define ARROW 20
+#define FIRSTGUNSPRITE 21
+#define CHAINGUNSPRITE 22
+#define RPGSPRITE 23
+#define FREEZESPRITE 24
+#define SHRINKERSPRITE 25
+#define HEAVYHBOMB 26
+#define TRIPBOMBSPRITE 27
+#define SHOTGUNSPRITE 28
+#define DEVISTATORSPRITE 29
+#define HEALTHBOX 30
+#define AMMOBOX 31
+#define INVENTORYBOX 33
+#define FREEZEAMMO 37
+#define AMMO 40
+#define BATTERYAMMO 41
+#define DEVISTATORAMMO 42
+#define RPGAMMO 44
+#define GROWAMMO 45
+#define CRYSTALAMMO 46
+#define HBOMBAMMO 47
+#define AMMOLOTS 48
+#define SHOTGUNAMMO 49
+#define COLA 51
+#define SIXPAK 52
+#define FIRSTAID 53
+#define SHIELD 54
+#define STEROIDS 55
+#define AIRTANK 56
+#define JETPACK 57
+#define HEATSENSOR 59
+#define ACCESSCARD 60
+#define BOOTS 61
+#define MIRRORBROKE 70
+#define CLOUDYOCEAN 78
+#define CLOUDYSKIES 79
+#define MOONSKY1 80
+#define MOONSKY2 81
+#define MOONSKY3 82
+#define MOONSKY4 83
+#define BIGORBIT1 84
+#define BIGORBIT2 85
+#define BIGORBIT3 86
+#define BIGORBIT4 87
+#define BIGORBIT5 88
+#define LA 89
+#define REDSKY1 98
+#define REDSKY2 99
+#define ATOMICHEALTH 100
+#define TECHLIGHT2 120
+#define TECHLIGHTBUST2 121
+#define TECHLIGHT4 122
+#define TECHLIGHTBUST4 123
+#define WALLLIGHT4 124
+#define WALLLIGHTBUST4 125
+#define ACCESSSWITCH 130
+#define SLOTDOOR 132
+#define LIGHTSWITCH 134
+#define SPACEDOORSWITCH 136
+#define SPACELIGHTSWITCH 138
+#define FRANKENSTINESWITCH 140
+#define NUKEBUTTON 142
+#define MULTISWITCH 146
+#define DOORTILE5 150
+#define DOORTILE6 151
+#define DOORTILE1 152
+#define DOORTILE2 153
+#define DOORTILE3 154
+#define DOORTILE4 155
+#define DOORTILE7 156
+#define DOORTILE8 157
+#define DOORTILE9 158
+#define DOORTILE10 159
+#define DOORSHOCK 160
+#define DIPSWITCH 162
+#define DIPSWITCH2 164
+#define TECHSWITCH 166
+#define DIPSWITCH3 168
+#define ACCESSSWITCH2 170
+#define REFLECTWATERTILE 180
+#define FLOORSLIME 200
+#define BIGFORCE 230
+#define EPISODE 247
+#define MASKWALL9 255
+#define W_LIGHT 260
+#define SCREENBREAK1 263
+#define SCREENBREAK2 264
+#define SCREENBREAK3 265
+#define SCREENBREAK4 266
+#define SCREENBREAK5 267
+#define SCREENBREAK6 268
+#define SCREENBREAK7 269
+#define SCREENBREAK8 270
+#define SCREENBREAK9 271
+#define SCREENBREAK10 272
+#define SCREENBREAK11 273
+#define SCREENBREAK12 274
+#define SCREENBREAK13 275
+#define MASKWALL1 285
+#define W_TECHWALL1 293
+#define W_TECHWALL2 297
+#define W_TECHWALL15 299
+#define W_TECHWALL3 301
+#define W_TECHWALL4 305
+#define W_TECHWALL10 306
+#define W_TECHWALL16 307
+#define WATERTILE2 336
+#define BPANNEL1 341
+#define PANNEL1 342
+#define PANNEL2 343
+#define WATERTILE 344
+#define STATIC 351
+#define W_SCREENBREAK 357
+#define W_HITTECHWALL3 360
+#define W_HITTECHWALL4 361
+#define W_HITTECHWALL2 362
+#define W_HITTECHWALL1 363
+#define MASKWALL10 387
+#define MASKWALL11 391
+#define DOORTILE22 395
+#define FANSPRITE 407
+#define FANSPRITEBROKE 411
+#define FANSHADOW 412
+#define FANSHADOWBROKE 416
+#define DOORTILE18 447
+#define DOORTILE19 448
+#define DOORTILE20 449
+// #define SPACESHUTTLE 487
+#define SATELLITE 489
+#define VIEWSCREEN2 499
+#define VIEWSCREENBROKE 501
+#define VIEWSCREEN 502
+#define GLASS 503
+#define GLASS2 504
+#define STAINGLASS1 510
+#define MASKWALL5 514
+#define SATELITE 516
+#define FUELPOD 517
+#define SLIMEPIPE 538
+#define CRACK1 546
+#define CRACK2 547
+#define CRACK3 548
+#define CRACK4 549
+#define FOOTPRINTS 550
+#define DOMELITE 551
+#define CAMERAPOLE 554
+#define CHAIR1 556
+#define CHAIR2 557
+#define BROKENCHAIR 559
+#define MIRROR 560
+#define WATERFOUNTAIN 563
+#define WATERFOUNTAINBROKE 567
+#define FEMMAG1 568
+#define TOILET 569
+#define STALL 571
+#define STALLBROKE 573
+#define FEMMAG2 577
+#define REACTOR2 578
+#define REACTOR2BURNT 579
+#define REACTOR2SPARK 580
+#define GRATE1 595
+#define BGRATE1 596
+#define SOLARPANNEL 602
+#define NAKED1 603
+#define ANTENNA 607
+#define MASKWALL12 609
+#define TOILETBROKE 615
+#define PIPE2 616
+#define PIPE1B 617
+#define PIPE3 618
+#define PIPE1 619
+#define CAMERA1 621
+#define BRICK 626
+#define SPLINTERWOOD 630
+#define PIPE2B 633
+#define BOLT1 634
+#define W_NUMBERS 640
+#define WATERDRIP 660
+#define WATERBUBBLE 661
+#define WATERBUBBLEMAKER 662
+#define W_FORCEFIELD 663
+#define VACUUM 669
+#define FOOTPRINTS2 672
+#define FOOTPRINTS3 673
+#define FOOTPRINTS4 674
+#define EGG 675
+#define SCALE 678
+#define CHAIR3 680
+#define CAMERALIGHT 685
+#define MOVIECAMERA 686
+#define IVUNIT 689
+#define POT1 694
+#define POT2 695
+#define POT3 697
+#define PIPE3B 700
+#define WALLLIGHT3 701
+#define WALLLIGHTBUST3 702
+#define WALLLIGHT1 703
+#define WALLLIGHTBUST1 704
+#define WALLLIGHT2 705
+#define WALLLIGHTBUST2 706
+#define LIGHTSWITCH2 712
+#define WAITTOBESEATED 716
+#define DOORTILE14 717
+#define STATUE 753
+#define MIKE 762
+#define VASE 765
+#define SUSHIPLATE1 768
+#define SUSHIPLATE2 769
+#define SUSHIPLATE3 774
+#define SUSHIPLATE4 779
+#define DOORTILE16 781
+#define SUSHIPLATE5 792
+#define OJ 806
+#define MASKWALL13 830
+#define HURTRAIL 859
+#define POWERSWITCH1 860
+#define LOCKSWITCH1 862
+#define POWERSWITCH2 864
+#define ATM 867
+#define STATUEFLASH 869
+#define ATMBROKE 888
+#define BIGHOLE2 893
+#define STRIPEBALL 901
+#define QUEBALL 902
+#define POCKET 903
+#define WOODENHORSE 904
+#define TREE1 908
+#define TREE2 910
+#define CACTUS 911
+#define MASKWALL2 913
+#define MASKWALL3 914
+#define MASKWALL4 915
+#define FIREEXT 916
+#define TOILETWATER 921
+#define NEON1 925
+#define NEON2 926
+#define CACTUSBROKE 939
+#define BOUNCEMINE 940
+#define BROKEFIREHYDRENT 950
+#define BOX 951
+#define BULLETHOLE 952
+#define BOTTLE1 954
+#define BOTTLE2 955
+#define BOTTLE3 956
+#define BOTTLE4 957
+#define FEMPIC5 963
+#define FEMPIC6 964
+#define FEMPIC7 965
+#define HYDROPLANT 969
+#define OCEANSPRITE1 971
+#define OCEANSPRITE2 972
+#define OCEANSPRITE3 973
+#define OCEANSPRITE4 974
+#define OCEANSPRITE5 975
+#define GENERICPOLE 977
+#define CONE 978
+#define HANGLIGHT 979
+#define HYDRENT 981
+#define MASKWALL14 988
+#define TIRE 990
+#define PIPE5 994
+#define PIPE6 995
+#define PIPE4 996
+#define PIPE4B 997
+#define BROKEHYDROPLANT 1003
+#define PIPE5B 1005
+#define NEON3 1007
+#define NEON4 1008
+#define NEON5 1009
+#define BOTTLE5 1012
+#define BOTTLE6 1013
+#define BOTTLE8 1014
+#define SPOTLITE 1020
+#define HANGOOZ 1022
+#define MASKWALL15 1024
+#define BOTTLE7 1025
+#define HORSEONSIDE 1026
+#define GLASSPIECES 1031
+#define HORSELITE 1034
+#define DONUTS 1045
+#define NEON6 1046
+#define MASKWALL6 1059
+#define CLOCK 1060
+#define RUBBERCAN 1062
+#define BROKENCLOCK 1067
+#define PLUG 1069
+#define OOZFILTER 1079
+#define FLOORPLASMA 1082
+#define REACTOR 1088
+#define REACTORSPARK 1092
+#define REACTORBURNT 1096
+#define DOORTILE15 1102
+#define HANDSWITCH 1111
+#define CIRCLEPANNEL 1113
+#define CIRCLEPANNELBROKE 1114
+#define PULLSWITCH 1122
+#define MASKWALL8 1124
+#define BIGHOLE 1141
+#define ALIENSWITCH 1142
+#define DOORTILE21 1144
+#define HANDPRINTSWITCH 1155
+#define BOTTLE10 1157
+#define BOTTLE11 1158
+#define BOTTLE12 1159
+#define BOTTLE13 1160
+#define BOTTLE14 1161
+#define BOTTLE15 1162
+#define BOTTLE16 1163
+#define BOTTLE17 1164
+#define BOTTLE18 1165
+#define BOTTLE19 1166
+#define DOORTILE17 1169
+#define MASKWALL7 1174
+#define JAILBARBREAK 1175
+#define DOORTILE11 1178
+#define DOORTILE12 1179
+#define VENDMACHINE 1212
+#define VENDMACHINEBROKE 1214
+#define COLAMACHINE 1215
+#define COLAMACHINEBROKE 1217
+#define CRANEPOLE 1221
+#define CRANE 1222
+#define BARBROKE 1225
+#define BLOODPOOL 1226
+#define NUKEBARREL 1227
+#define NUKEBARRELDENTED 1228
+#define NUKEBARRELLEAKED 1229
+#define CANWITHSOMETHING 1232
+#define MONEY 1233
+#define BANNER 1236
+#define EXPLODINGBARREL 1238
+#define EXPLODINGBARREL2 1239
+#define FIREBARREL 1240
+#define SEENINE 1247
+#define SEENINEDEAD 1248
+#define STEAM 1250
+#define CEILINGSTEAM 1255
+#define PIPE6B 1260
+#define TRANSPORTERBEAM 1261
+#define RAT 1267
+#define TRASH 1272
+#define FEMPIC1 1280
+#define FEMPIC2 1289
+#define BLANKSCREEN 1293
+#define PODFEM1 1294
+#define FEMPIC3 1298
+#define FEMPIC4 1306
+#define FEM1 1312
+#define FEM2 1317
+#define FEM3 1321
+#define FEM5 1323
+#define BLOODYPOLE 1324
+#define FEM4 1325
+#define FEM6 1334
+#define FEM6PAD 1335
+#define FEM8 1336
+#define HELECOPT 1346
+#define FETUSJIB 1347
+#define HOLODUKE 1348
+#define SPACEMARINE 1353
+#define INDY 1355
+#define FETUS 1358
+#define FETUSBROKE 1359
+#define MONK 1352
+#define LUKE 1354
+#define COOLEXPLOSION1 1360
+#define WATERSPLASH2 1380
+#define FIREVASE 1390
+#define SCRATCH 1393
+#define FEM7 1395
+#define APLAYERTOP 1400
+#define APLAYER 1405
+#define PLAYERONWATER 1420
+#define DUKELYINGDEAD 1518
+#define DUKETORSO 1520
+#define DUKEGUN 1528
+#define DUKELEG 1536
+#define SHARK 1550
+#define BLOOD 1620
+#define FIRELASER 1625
+#define TRANSPORTERSTAR 1630
+#define SPIT 1636
+#define LOOGIE 1637
+#define FIST 1640
+#define FREEZEBLAST 1641
+#define DEVISTATORBLAST 1642
+#define SHRINKSPARK 1646
+#define TONGUE 1647
+#define MORTER 1650
+#define SHRINKEREXPLOSION 1656
+#define RADIUSEXPLOSION 1670
+#define FORCERIPPLE 1671
+#define LIZTROOP 1680
+#define LIZTROOPRUNNING 1681
+#define LIZTROOPSTAYPUT 1682
+#define LIZTOP 1705
+#define LIZTROOPSHOOT 1715
+#define LIZTROOPJETPACK 1725
+#define LIZTROOPDSPRITE 1734
+#define LIZTROOPONTOILET 1741
+#define LIZTROOPJUSTSIT 1742
+#define LIZTROOPDUCKING 1744
+#define HEADJIB1 1768
+#define ARMJIB1 1772
+#define LEGJIB1 1776
+#define CANNONBALL 1817
+#define OCTABRAIN 1820
+#define OCTABRAINSTAYPUT 1821
+#define OCTATOP 1845
+#define OCTADEADSPRITE 1855
+#define INNERJAW 1860
+#define DRONE 1880
+#define EXPLOSION2 1890
+#define COMMANDER 1920
+#define COMMANDERSTAYPUT 1921
+#define RECON 1960
+#define TANK 1975
+#define PIGCOP 2000
+#define PIGCOPSTAYPUT 2001
+#define PIGCOPDIVE 2045
+#define PIGCOPDEADSPRITE 2060
+#define PIGTOP 2061
+#define LIZMAN 2120
+#define LIZMANSTAYPUT 2121
+#define LIZMANSPITTING 2150
+#define LIZMANFEEDING 2160
+#define LIZMANJUMP 2165
+#define LIZMANDEADSPRITE 2185
+#define FECES 2200
+#define LIZMANHEAD1 2201
+#define LIZMANARM1 2205
+#define LIZMANLEG1 2209
+#define EXPLOSION2BOT 2219
+#define USERWEAPON 2235
+#define HEADERBAR 2242
+#define JIBS1 2245
+#define JIBS2 2250
+#define JIBS3 2255
+#define JIBS4 2260
+#define JIBS5 2265
+#define BURNING 2270
+#define FIRE 2271
+#define JIBS6 2286
+#define BLOODSPLAT1 2296
+#define BLOODSPLAT3 2297
+#define BLOODSPLAT2 2298
+#define BLOODSPLAT4 2299
+#define OOZ 2300
+#define OOZ2 2309
+#define WALLBLOOD1 2301
+#define WALLBLOOD2 2302
+#define WALLBLOOD3 2303
+#define WALLBLOOD4 2304
+#define WALLBLOOD5 2305
+#define WALLBLOOD6 2306
+#define WALLBLOOD7 2307
+#define WALLBLOOD8 2308
+#define BURNING2 2310
+#define FIRE2 2311
+#define CRACKKNUCKLES 2324
+#define SMALLSMOKE 2329
+#define SMALLSMOKEMAKER 2330
+#define FLOORFLAME 2333
+#define ROTATEGUN 2360
+#define GREENSLIME 2370
+#define WATERDRIPSPLASH 2380
+#define SCRAP6 2390
+#define SCRAP1 2400
+#define SCRAP2 2404
+#define SCRAP3 2408
+#define SCRAP4 2412
+#define SCRAP5 2416
+#define ORGANTIC 2420
+#define BETAVERSION 2440
+#define PLAYERISHERE 2442
+#define PLAYERWASHERE 2443
+#define SELECTDIR 2444
+#define F1HELP 2445
+#define NOTCHON 2446
+#define NOTCHOFF 2447
+#define GROWSPARK 2448
+#define DUKEICON 2452
+#define BADGUYICON 2453
+#define FOODICON 2454
+#define GETICON 2455
+#define MENUSCREEN 2456
+#define MENUBAR 2457
+#define KILLSICON 2458
+#define FIRSTAID_ICON 2460
+#define HEAT_ICON 2461
+#define BOTTOMSTATUSBAR 2462
+#define BOOT_ICON 2463
+#define FRAGBAR 2465
+#define JETPACK_ICON 2467
+#define AIRTANK_ICON 2468
+#define STEROIDS_ICON 2469
+#define HOLODUKE_ICON 2470
+#define ACCESS_ICON 2471
+#define DIGITALNUM 2472
+#define DUKECAR 2491
+#define CAMCORNER 2482
+#define CAMLIGHT 2484
+#define LOGO 2485
+#define TITLE 2486
+#define NUKEWARNINGICON 2487
+#define MOUSECURSOR 2488
+#define SLIDEBAR 2489
+#define DREALMS 2492
+#define BETASCREEN 2493
+#define WINDOWBORDER1 2494
+#define TEXTBOX 2495
+#define WINDOWBORDER2 2496
+#define DUKENUKEM 2497
+#define THREEDEE 2498
+#define INGAMEDUKETHREEDEE 2499
+#define TENSCREEN 2500
+#define PLUTOPAKSPRITE 2501
+#define DEVISTATOR 2510
+#define KNEE 2521
+#define CROSSHAIR 2523
+#define FIRSTGUN 2524
+#define FIRSTGUNRELOAD 2528
+#define FALLINGCLIP 2530
+#define CLIPINHAND 2531
+#define HAND 2532
+#define SHELL 2533
+#define SHOTGUNSHELL 2535
+#define CHAINGUN 2536
+#define RPGGUN 2544
+#define RPGMUZZLEFLASH 2545
+#define FREEZE 2548
+#define CATLITE 2552
+#define SHRINKER 2556
+#define HANDHOLDINGLASER 2563
+#define TRIPBOMB 2566
+#define LASERLINE 2567
+#define HANDHOLDINGACCESS 2568
+#define HANDREMOTE 2570
+#define HANDTHROW 2573
+#define TIP 2576
+#define GLAIR 2578
+#define SCUBAMASK 2581
+#define SPACEMASK 2584
+#define FORCESPHERE 2590
+#define SHOTSPARK1 2595
+#define RPG 2605
+#define LASERSITE 2612
+#define SHOTGUN 2613
+#define BOSS1 2630
+#define BOSS1STAYPUT 2631
+#define BOSS1SHOOT 2660
+#define BOSS1LOB 2670
+#define BOSSTOP 2696
+#define BOSS2 2710
+#define BOSS3 2760
+#define SPINNINGNUKEICON 2813
+#define BIGFNTCURSOR 2820
+#define SMALLFNTCURSOR 2821
+#define STARTALPHANUM 2822
+#define ENDALPHANUM 2915
+#define BIGALPHANUM 2940
+#define BIGPERIOD 3002
+#define BIGCOMMA 3003
+#define BIGX 3004
+#define BIGQ 3005
+#define BIGSEMI 3006
+#define BIGCOLIN 3007
+#define THREEBYFIVE 3010
+#define BIGAPPOS 3022
+#define BLANK 3026
+#define MINIFONT 3072
+#define BUTTON1 3164
+#define GLASS3 3187
+#define RESPAWNMARKERRED 3190
+#define RESPAWNMARKERYELLOW 3200
+#define RESPAWNMARKERGREEN 3210
+#define BONUSSCREEN 3240
+#define VIEWBORDER 3250
+#define VICTORY1 3260
+#define ORDERING 3270
+#define TEXTSTORY 3280
+#define LOADSCREEN 3281
+#define BORNTOBEWILDSCREEN 3370
+#define BLIMP 3400
+#define FEM9 3450
+#define FOOTPRINT 3701
+#define POOP 4094
+#define FRAMEEFFECT1 4095
+// FIX_00037: steroids trigger a too many sprite respawn in 1.3 and 1.3d.
+#define FRAMEEFFECT1_13CON 3999 // Needed to ensure 1.3/1.3d compliance --mk
+#define PANNEL3 4099
+#define SCREENBREAK19 4125
+#define W_TECHWALL11 4130
+#define W_TECHWALL12 4131
+#define W_TECHWALL13 4132
+#define W_TECHWALL14 4133
+#define SCREENBREAK14 4120
+#define SCREENBREAK15 4123
+#define SCREENBREAK16 4127
+#define SCREENBREAK17 4128
+#define SCREENBREAK18 4129
+#define W_TECHWALL5 4134
+#define W_TECHWALL6 4136
+#define W_TECHWALL7 4138
+#define W_TECHWALL8 4140
+#define W_TECHWALL9 4142
+#define BPANNEL3 4100
+#define MIMON 4120
+#define W_HITTECHWALL16 4144
+#define W_HITTECHWALL10 4145
+#define W_HITTECHWALL15 4147
+#define W_MILKSHELF 4181
+#define W_MILKSHELFBROKE 4203
+#define PURPLELAVA 4240
+#define LAVABUBBLE 4340
+#define DUKECUTOUT 4352
+#define TARGET 4359
+#define GUNPOWDERBARREL 4360
+#define DUCK 4361
+#define HATRACK 4367
+#define DESKLAMP 4370
+#define COFFEEMACHINE 4372
+#define CUPS 4373
+#define GAVALS 4374
+#define GAVALS2 4375
+#define POLICELIGHTPOLE 4377
+#define FLOORBASKET 4388
+#define PUKE 4389
+#define DOORTILE23 4391
+#define TOPSECRET 4396
+#define SPEAKER 4397
+#define TEDDYBEAR 4400
+#define ROBOTDOG 4402
+#define ROBOTPIRATE 4404
+#define ROBOTMOUSE 4407
+#define MAIL 4410
+#define MAILBAG 4413
+#define HOTMEAT 4427
+#define COFFEEMUG 4438
+#define DONUTS2 4440
+#define TRIPODCAMERA 4444
+#define METER 4453
+#define DESKPHONE 4454
+#define GUMBALLMACHINE 4458
+#define GUMBALLMACHINEBROKE 4459
+#define PAPER 4460
+#define MACE 4464
+#define GENERICPOLE2 4465
+#define XXXSTACY 4470
+#define WETFLOOR 4495
+#define BROOM 4496
+#define MOP 4497
+#define PIRATE1A 4510
+#define PIRATE4A 4511
+#define PIRATE2A 4512
+#define PIRATE5A 4513
+#define PIRATE3A 4514
+#define PIRATE6A 4515
+#define PIRATEHALF 4516
+#define CHESTOFGOLD 4520
+#define SIDEBOLT1 4525
+#define FOODOBJECT1 4530
+#define FOODOBJECT2 4531
+#define FOODOBJECT3 4532
+#define FOODOBJECT4 4533
+#define FOODOBJECT5 4534
+#define FOODOBJECT6 4535
+#define FOODOBJECT7 4536
+#define FOODOBJECT8 4537
+#define FOODOBJECT9 4538
+#define FOODOBJECT10 4539
+#define FOODOBJECT11 4540
+#define FOODOBJECT12 4541
+#define FOODOBJECT13 4542
+#define FOODOBJECT14 4543
+#define FOODOBJECT15 4544
+#define FOODOBJECT16 4545
+#define FOODOBJECT17 4546
+#define FOODOBJECT18 4547
+#define FOODOBJECT19 4548
+#define FOODOBJECT20 4549
+#define HEADLAMP 4550
+#define TAMPON 4557
+#define SKINNEDCHICKEN 4554
+#define FEATHEREDCHICKEN 4555
+#define ROBOTDOG2 4560
+#define JOLLYMEAL 4569
+#define DUKEBURGER 4570
+#define SHOPPINGCART 4576
+#define CANWITHSOMETHING2 4580
+#define CANWITHSOMETHING3 4581
+#define CANWITHSOMETHING4 4582
+#define SNAKEP 4590
+#define DOLPHIN1 4591
+#define DOLPHIN2 4592
+#define NEWBEAST 4610
+#define NEWBEASTSTAYPUT 4611
+#define NEWBEASTJUMP 4690
+#define NEWBEASTHANG 4670
+#define NEWBEASTHANGDEAD 4671
+#define BOSS4 4740
+#define BOSS4STAYPUT 4741
+#define FEM10 4864
+#define TOUGHGAL 4866
+#define MAN 4871
+#define MAN2 4872
+#define WOMAN 4874
+#define PLEASEWAIT 4887
+#define NATURALLIGHTNING 4890
+#define WEATHERWARN 4893
+#define DUKETAG 4900
+#define SIGN1 4909
+#define SIGN2 4912
+#define JURYGUY 4943
+
+
+
+
+// These tile positions are reserved!
+#define RESERVEDSLOT1 6132
+#define RESERVEDSLOT2 6133
+#define RESERVEDSLOT3 6134
+#define RESERVEDSLOT4 6135
+#define RESERVEDSLOT5 6136
+#define RESERVEDSLOT6 6137
+#define RESERVEDSLOT7 6138
+#define RESERVEDSLOT8 6139
+#define RESERVEDSLOT9 6140
+#define RESERVEDSLOT10 6141
+#define RESERVEDSLOT11 6142
+#define RESERVEDSLOT12 6143
+
+// Defines weapon, not to be used with the 'shoot' keyword.
+
+#define KNEE_WEAPON         0
+#define PISTOL_WEAPON       1
+#define SHOTGUN_WEAPON      2
+#define CHAINGUN_WEAPON     3
+#define RPG_WEAPON          4
+#define HANDBOMB_WEAPON     5
+#define SHRINKER_WEAPON     6
+#define DEVISTATOR_WEAPON   7
+#define TRIPBOMB_WEAPON     8
+#define FREEZE_WEAPON       9
+#define HANDREMOTE_WEAPON   10
+#define GROW_WEAPON         11
+
+// Defines the motion characteristics of an actor
+#define faceplayer 1
+#define geth 2
+#define getv 4
+#define randomangle 8
+#define faceplayerslow 16
+#define spin 32
+#define faceplayersmart 64
+#define fleeenemy 128
+#define jumptoplayer 257
+#define seekplayer 512
+#define furthestdir 1024
+#define dodgebullet 4096
+
+// Some misc #defines
+#define NO       0
+#define YES      1
+
+// Defines for 'useractor' keyword
+#define notenemy       0
+#define enemy          1
+#define enemystayput   2
+
+// Player Actions.
+#define pstanding 1
+#define pwalking 2
+#define prunning 4
+#define pducking 8
+#define pfalling 16
+#define pjumping 32
+#define phigher 64
+#define pwalkingback 128
+#define prunningback 256
+#define pkicking 512
+#define pshrunk 1024
+#define pjetpack 2048
+#define ponsteroids 4096
+#define ponground 8192
+#define palive 16384
+#define pdead 32768
+#define pfacing 65536
+
+
+#define GET_STEROIDS     0
+#define GET_SHIELD       1
+#define GET_SCUBA        2
+#define GET_HOLODUKE     3
+#define GET_JETPACK      4
+#define GET_ACCESS       6
+#define GET_HEATS        7
+#define GET_FIRSTAID     9
+#define GET_BOOTS       10
+
+
+#define KICK_HIT                         0
+#define PISTOL_RICOCHET                  1
+#define PISTOL_BODYHIT                   2
+#define PISTOL_FIRE                      3
+#define EJECT_CLIP                       4
+#define INSERT_CLIP                      5
+#define CHAINGUN_FIRE                    6
+#define RPG_SHOOT                        7
+#define POOLBALLHIT                      8
+#define RPG_EXPLODE                      9
+#define CAT_FIRE                        10
+#define SHRINKER_FIRE                   11
+#define ACTOR_SHRINKING                 12
+#define PIPEBOMB_BOUNCE                 13
+#define PIPEBOMB_EXPLODE                14
+#define LASERTRIP_ONWALL                15
+#define LASERTRIP_ARMING                16
+#define LASERTRIP_EXPLODE               17
+#define VENT_BUST                       18
+#define GLASS_BREAKING                  19
+#define GLASS_HEAVYBREAK                20
+#define SHORT_CIRCUIT                   21
+#define ITEM_SPLASH                     22
+#define DUKE_BREATHING                  23
+#define DUKE_EXHALING                   24
+#define DUKE_GASP                       25
+#define SLIM_RECOG                      26
+// #define ENDSEQVOL3SND1                  27
+#define DUKE_URINATE                    28
+#define ENDSEQVOL3SND2                  29
+#define ENDSEQVOL3SND3                  30
+#define DUKE_PASSWIND                   32
+#define DUKE_CRACK                      33
+#define SLIM_ATTACK                     34
+#define SOMETHINGHITFORCE               35
+#define DUKE_DRINKING                   36
+#define DUKE_KILLED1                    37
+#define DUKE_GRUNT                      38
+#define DUKE_HARTBEAT                   39
+#define DUKE_ONWATER                    40
+#define DUKE_DEAD                       41
+#define DUKE_LAND                       42
+#define DUKE_WALKINDUCTS                43
+#define DUKE_GLAD                       44
+#define DUKE_YES                        45
+#define DUKE_HEHE                       46
+#define DUKE_SHUCKS                     47
+#define DUKE_UNDERWATER                 48
+#define DUKE_JETPACK_ON                 49
+#define DUKE_JETPACK_IDLE               50
+#define DUKE_JETPACK_OFF                51
+#define LIZTROOP_GROWL                  52
+#define LIZTROOP_TALK1                  53
+#define LIZTROOP_TALK2                  54
+#define LIZTROOP_TALK3                  55
+#define DUKETALKTOBOSS                  56
+#define LIZCAPT_GROWL                   57
+#define LIZCAPT_TALK1                   58
+#define LIZCAPT_TALK2                   59
+#define LIZCAPT_TALK3                   60
+#define LIZARD_BEG                      61
+#define LIZARD_PAIN                     62
+#define LIZARD_DEATH                    63
+#define LIZARD_SPIT                     64
+#define DRONE1_HISSRATTLE               65
+#define DRONE1_HISSSCREECH              66
+#define DUKE_TIP2                       67
+#define FLESH_BURNING                   68
+#define SQUISHED                        69
+#define TELEPORTER                      70
+#define ELEVATOR_ON                     71
+#define DUKE_KILLED3                    72
+#define ELEVATOR_OFF                    73
+#define DOOR_OPERATE1                   74
+#define SUBWAY                          75
+#define SWITCH_ON                       76
+#define FAN                             77
+#define DUKE_GETWEAPON3                 78
+#define FLUSH_TOILET                    79
+#define HOVER_CRAFT                     80
+#define EARTHQUAKE                      81
+#define INTRUDER_ALERT                  82
+#define END_OF_LEVEL_WARN               83
+#define ENGINE_OPERATING                84
+#define REACTOR_ON                      85
+#define COMPUTER_AMBIENCE               86
+#define GEARS_GRINDING                  87
+#define BUBBLE_AMBIENCE                 88
+#define MACHINE_AMBIENCE                89
+#define SEWER_AMBIENCE                  90
+#define WIND_AMBIENCE                   91
+#define SOMETHING_DRIPPING              92
+#define STEAM_HISSING                   93
+#define THEATER_BREATH                  94
+#define BAR_MUSIC                       95
+#define BOS1_ROAM                       96
+#define BOS1_RECOG                      97
+#define BOS1_ATTACK1                    98
+#define BOS1_PAIN                       99
+#define BOS1_DYING                     100
+#define BOS2_ROAM                      101
+#define BOS2_RECOG                     102
+#define BOS2_ATTACK                    103
+#define BOS2_PAIN                      104
+#define BOS2_DYING                     105
+#define GETATOMICHEALTH                106
+#define DUKE_GETWEAPON2                107
+#define BOS3_DYING                     108
+#define SHOTGUN_FIRE                   109
+#define PRED_ROAM                      110
+#define PRED_RECOG                     111
+#define PRED_ATTACK                    112
+#define PRED_PAIN                      113
+#define PRED_DYING                     114
+#define CAPT_ROAM                      115
+#define CAPT_ATTACK                    116
+#define CAPT_RECOG                     117
+#define CAPT_PAIN                      118
+#define CAPT_DYING                     119
+#define PIG_ROAM                       120
+#define PIG_RECOG                      121
+#define PIG_ATTACK                     122
+#define PIG_PAIN                       123
+#define PIG_DYING                      124
+#define RECO_ROAM                      125
+#define RECO_RECOG                     126
+#define RECO_ATTACK                    127
+#define RECO_PAIN                      128
+#define RECO_DYING                     129
+#define DRON_ROAM                      130
+#define DRON_RECOG                     131
+#define DRON_ATTACK1                   132
+#define DRON_PAIN                      133
+#define DRON_DYING                     134
+#define COMM_ROAM                      135
+#define COMM_RECOG                     136
+#define COMM_ATTACK                    137
+#define COMM_PAIN                      138
+#define COMM_DYING                     139
+#define OCTA_ROAM                      140
+#define OCTA_RECOG                     141
+#define OCTA_ATTACK1                   142
+#define OCTA_PAIN                      143
+#define OCTA_DYING                     144
+#define TURR_ROAM                      145
+#define TURR_RECOG                     146
+#define TURR_ATTACK                    147
+#define DUMPSTER_MOVE                  148
+#define SLIM_DYING                     149
+#define BOS3_ROAM                      150
+#define BOS3_RECOG                     151
+#define BOS3_ATTACK1                   152
+#define BOS3_PAIN                      153
+#define BOS1_ATTACK2                   154
+#define COMM_SPIN                      155
+#define BOS1_WALK                      156
+#define DRON_ATTACK2                   157
+#define THUD                           158
+#define OCTA_ATTACK2                   159
+#define WIERDSHOT_FLY                  160
+#define TURR_PAIN                      161
+#define TURR_DYING                     162
+#define SLIM_ROAM                      163
+#define LADY_SCREAM                    164
+#define DOOR_OPERATE2                  165
+#define DOOR_OPERATE3                  166
+#define DOOR_OPERATE4                  167
+#define BORNTOBEWILDSND                168
+#define SHOTGUN_COCK                   169
+#define GENERIC_AMBIENCE1              170
+#define GENERIC_AMBIENCE2              171
+#define GENERIC_AMBIENCE3              172
+#define GENERIC_AMBIENCE4              173
+#define GENERIC_AMBIENCE5              174
+#define GENERIC_AMBIENCE6              175
+#define BOS3_ATTACK2                   176
+#define GENERIC_AMBIENCE17             177
+#define GENERIC_AMBIENCE18             178
+#define GENERIC_AMBIENCE19             179
+#define GENERIC_AMBIENCE20             180
+#define GENERIC_AMBIENCE21             181
+#define GENERIC_AMBIENCE22             182
+#define SECRETLEVELSND                 183
+#define GENERIC_AMBIENCE8              184
+#define GENERIC_AMBIENCE9              185
+#define GENERIC_AMBIENCE10             186
+#define GENERIC_AMBIENCE11             187
+#define GENERIC_AMBIENCE12             188
+#define GENERIC_AMBIENCE13             189
+#define GENERIC_AMBIENCE14             190
+#define GENERIC_AMBIENCE15             192
+#define GENERIC_AMBIENCE16             193
+#define FIRE_CRACKLE                   194
+#define BONUS_SPEECH1                  195
+#define BONUS_SPEECH2                  196
+#define BONUS_SPEECH3                  197
+#define PIG_CAPTURE_DUKE               198
+#define BONUS_SPEECH4                  199
+#define DUKE_LAND_HURT                 200
+#define DUKE_HIT_STRIPPER1             201
+#define DUKE_TIP1                      202
+#define DUKE_KILLED2                   203
+#define PRED_ROAM2                     204
+#define PIG_ROAM2                      205
+#define DUKE_GETWEAPON1                206
+#define DUKE_SEARCH2                   207
+#define DUKE_CRACK2                    208
+#define DUKE_SEARCH                    209
+#define DUKE_GET                       210
+#define DUKE_LONGTERM_PAIN             211
+#define MONITOR_ACTIVE                 212
+#define NITEVISION_ONOFF               213
+#define DUKE_HIT_STRIPPER2             214
+#define DUKE_CRACK_FIRST               215
+#define DUKE_USEMEDKIT                 216
+#define DUKE_TAKEPILLS                 217
+#define DUKE_PISSRELIEF                218
+#define SELECT_WEAPON                  219
+#define WATER_GURGLE                   220
+#define DUKE_GETWEAPON4                221
+#define JIBBED_ACTOR1                  222
+#define JIBBED_ACTOR2                  223
+#define JIBBED_ACTOR3                  224
+#define JIBBED_ACTOR4                  225
+#define JIBBED_ACTOR5                  226
+#define JIBBED_ACTOR6                  227
+#define JIBBED_ACTOR7                  228
+#define DUKE_GOTHEALTHATLOW            229
+#define BOSSTALKTODUKE                 230
+#define WAR_AMBIENCE1                  231
+#define WAR_AMBIENCE2                  232
+#define WAR_AMBIENCE3                  233
+#define WAR_AMBIENCE4                  234
+#define WAR_AMBIENCE5                  235
+#define WAR_AMBIENCE6                  236
+#define WAR_AMBIENCE7                  237
+#define WAR_AMBIENCE8                  238
+#define WAR_AMBIENCE9                  239
+#define WAR_AMBIENCE10                 240
+#define ALIEN_TALK1                    241
+#define ALIEN_TALK2                    242
+#define EXITMENUSOUND                  243
+#define FLY_BY                         244
+#define DUKE_SCREAM                    245
+#define SHRINKER_HIT                   246
+#define RATTY                          247
+#define INTO_MENU                      248
+#define BONUSMUSIC                     249
+#define DUKE_BOOBY                     250
+#define DUKE_TALKTOBOSSFALL            251
+#define DUKE_LOOKINTOMIRROR            252
+#define PIG_ROAM3                      253
+#define KILLME                         254
+#define DRON_JETSND                    255
+#define SPACE_DOOR1                    256
+#define SPACE_DOOR2                    257
+#define SPACE_DOOR3                    258
+#define SPACE_DOOR4                    259
+#define SPACE_DOOR5                    260
+#define ALIEN_ELEVATOR1                261
+#define VAULT_DOOR                     262
+#define JIBBED_ACTOR13                 263
+#define DUKE_GETWEAPON6                264
+#define JIBBED_ACTOR8                  265
+#define JIBBED_ACTOR9                  266
+#define JIBBED_ACTOR10                 267
+#define JIBBED_ACTOR11                 268
+#define JIBBED_ACTOR12                 269
+#define DUKE_KILLED4                   270
+#define DUKE_KILLED5                   271
+#define ALIEN_SWITCH1                  272
+#define DUKE_STEPONFECES               273
+#define DUKE_LONGTERM_PAIN2            274
+#define DUKE_LONGTERM_PAIN3            275
+#define DUKE_LONGTERM_PAIN4            276
+#define COMPANB2                       277
+#define KTIT                           278
+#define HELICOP_IDLE                   279
+#define STEPNIT                        280
+#define SPACE_AMBIENCE1                281
+#define SPACE_AMBIENCE2                282
+#define SLIM_HATCH                     283
+#define RIPHEADNECK                    284
+#define FOUNDJONES                     285
+#define ALIEN_DOOR1                    286
+#define ALIEN_DOOR2                    287
+#define ENDSEQVOL3SND4                 288
+#define ENDSEQVOL3SND5                 289
+#define ENDSEQVOL3SND6                 290
+#define ENDSEQVOL3SND7                 291
+#define ENDSEQVOL3SND8                 292
+#define ENDSEQVOL3SND9                 293
+#define WHIPYOURASS                    294
+#define ENDSEQVOL2SND1                 295
+#define ENDSEQVOL2SND2                 296
+#define ENDSEQVOL2SND3                 297
+#define ENDSEQVOL2SND4                 298
+#define ENDSEQVOL2SND5                 299
+#define ENDSEQVOL2SND6                 300
+#define ENDSEQVOL2SND7                 301
+#define GENERIC_AMBIENCE23             302
+#define SOMETHINGFROZE                 303
+#define DUKE_LONGTERM_PAIN5            304
+#define DUKE_LONGTERM_PAIN6            305
+#define DUKE_LONGTERM_PAIN7            306
+#define DUKE_LONGTERM_PAIN8            307
+#define WIND_REPEAT                    308
+#define MYENEMY_ROAM                   309
+#define MYENEMY_HURT                   310
+#define MYENEMY_DEAD                   311
+#define MYENEMY_SHOOT                  312
+#define STORE_MUSIC                    313
+#define STORE_MUSIC_BROKE              314
+#define ACTOR_GROWING                  315
+#define NEWBEAST_ROAM                  316
+#define NEWBEAST_RECOG                 317
+#define NEWBEAST_ATTACK                318
+#define NEWBEAST_PAIN                  319
+#define NEWBEAST_DYING                 320
+#define NEWBEAST_SPIT                  321
+#define VOL4_1                         322
+#define SUPERMARKET                    323
+#define MOUSEANNOY                     324
+#define BOOKEM                         325
+#define SUPERMARKETCRY                 326
+#define DESTRUCT                       327
+#define EATFOOD                        328
+#define MAKEMYDAY                      329
+#define WITNESSSTAND                   330
+#define VACATIONSPEECH                 331
+#define YIPPEE1                        332
+#define YOHOO1                         333
+#define YOHOO2                         334
+#define DOLPHINSND                     335
+#define TOUGHGALSND1                   336
+#define TOUGHGALSND2                   337
+#define TOUGHGALSND3                   338
+#define TOUGHGALSND4                   339
+#define TANK_ROAM                      340
+#define BOS4_ROAM                      341
+#define BOS4_RECOG                     342
+#define BOS4_ATTACK                    343
+#define BOS4_PAIN                      344
+#define BOS4_DYING                     345
+#define NEWBEAST_ATTACKMISS            346
+#define VOL4_2                         347
+#define COOKINGDEEPFRIER               348
+#define WHINING_DOG                    349
+#define DEAD_DOG                       350
+#define LIGHTNING_SLAP                 351
+#define THUNDER                        352
+#define HAPPYMOUSESND1                 353
+#define HAPPYMOUSESND2                 354
+#define HAPPYMOUSESND3                 355
+#define HAPPYMOUSESND4                 356
+#define ALARM                          357
+#define RAIN                           358
+#define DTAG_GREENRUN                  359
+#define DTAG_BROWNRUN                  360
+#define DTAG_GREENSCORE                361
+#define DTAG_BROWNSCORE                362
+#define INTRO4_1                       363
+#define INTRO4_2                       364
+#define INTRO4_3                       365
+#define INTRO4_4                       366
+#define INTRO4_5                       367
+#define INTRO4_6                       368
+#define SCREECH                        369
+#define BOSS4_DEADSPEECH               370
+#define BOSS4_FIRSTSEE                 371
+#define PARTY_SPEECH                   372
+#define POSTAL_SPEECH                  373
+#define TGSPEECH                       374
+#define DOGROOMSPEECH                  375
+#define SMACKED                        376
+#define MDEVSPEECH                     377
+#define AREA51SPEECH                   378
+#define JEEPSOUND                      379
+#define BIGDOORSLAM                    380
+#define BOS4_LAY                       381
+#define WAVESOUND                      382
+#define ILLBEBACK                      383
+#define VOL4ENDSND1                    384
+#define VOL4ENDSND2                    385
+#define EXPANDERHIT                    386
+#define SNAKESPEECH                    387
+#define EXPANDERSHOOT                  388
+#define GETBACKTOWORK                  389
+#define JIBBED_ACTOR14                 390
+#define JIBBED_ACTOR15                 391
+#define INTRO4_B                       392
+#define BIGBANG                        393
+#define SMACKIT                        394
+#define BELLSND                        395
+// MAXIMUM NUMBER OF SOUNDS: 450 ( 0-449 )
--- /dev/null
+++ b/Game/src/sounds.c
@@ -1,0 +1,688 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#if PLATFORM_DOS
+#include <conio.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "types.h"
+#include "util_lib.h"
+#include "duke3d.h"
+
+
+#define LOUDESTVOLUME 150
+
+long backflag,numenvsnds;
+
+/*
+===================
+=
+= SoundStartup
+=
+===================
+*/
+
+void SoundStartup( void )
+   {
+   int32 status;
+
+   // if they chose None lets return
+   if (FXDevice == NumSoundCards) return;
+
+   // Do special Sound Blaster, AWE32 stuff
+   if (
+         ( FXDevice == SoundBlaster ) ||
+         ( FXDevice == Awe32 )
+      )
+      {
+      int MaxVoices;
+      int MaxBits;
+      int MaxChannels;
+
+      status = FX_SetupSoundBlaster
+                  (
+                  BlasterConfig, (int *)&MaxVoices, (int *)&MaxBits, (int *)&MaxChannels
+                  );
+      }
+   else
+      {
+      status = FX_Ok;
+      }
+
+   if ( status == FX_Ok )
+      {
+      if ( eightytwofifty && numplayers > 1)
+         {
+         status = FX_Init( FXDevice, min( NumVoices,4 ), 1, 8, 8000 );
+         }
+      else
+         {
+         status = FX_Init( FXDevice, NumVoices, NumChannels, NumBits, MixRate );
+         }
+      if ( status == FX_Ok )
+         {
+
+         FX_SetVolume( FXVolume );
+         if (ReverseStereo == 1)
+            {
+            FX_SetReverseStereo(!FX_GetReverseStereo());
+            }
+         }
+      }
+   if ( status != FX_Ok )
+      {
+      Error(EXIT_FAILURE, FX_ErrorString( FX_Error ));
+      }
+
+   status = FX_SetCallBack( TestCallBack );
+
+   if ( status != FX_Ok )
+      {
+      Error(EXIT_FAILURE, FX_ErrorString( FX_Error ));
+      }
+   }
+
+/*
+===================
+=
+= SoundShutdown
+=
+===================
+*/
+
+void SoundShutdown( void )
+   {
+   int32 status;
+
+   // if they chose None lets return
+   if (FXDevice == NumSoundCards)
+      return;
+
+   status = FX_Shutdown();
+   if ( status != FX_Ok )
+      {
+      Error(EXIT_FAILURE, FX_ErrorString( FX_Error ));
+      }
+   }
+
+/*
+===================
+=
+= MusicStartup
+=
+===================
+*/
+
+void MusicStartup( void )
+   {
+   int32 status;
+
+   // if they chose None lets return
+   if ((MusicDevice == NumSoundCards) || (eightytwofifty && numplayers > 1) )
+      return;
+
+   // satisfy AWE32 and WAVEBLASTER stuff
+   BlasterConfig.Midi = MidiPort;
+
+   // Do special Sound Blaster, AWE32 stuff
+   if (
+         ( FXDevice == SoundBlaster ) ||
+         ( FXDevice == Awe32 )
+      )
+      {
+      int MaxVoices;
+      int MaxBits;
+      int MaxChannels;
+
+      FX_SetupSoundBlaster
+                  (
+                  BlasterConfig, (int *)&MaxVoices, (int *)&MaxBits, (int *)&MaxChannels
+                  );
+      }
+   status = MUSIC_Init( MusicDevice, MidiPort );
+
+   if ( status == MUSIC_Ok )
+      {
+      MUSIC_SetVolume( MusicVolume );
+      }
+   else
+   {
+      SoundShutdown();
+      uninittimer();
+      uninitengine();
+      CONTROL_Shutdown();
+      CONFIG_WriteSetup();
+      KB_Shutdown();
+      uninitgroupfile();
+      unlink("duke3d.tmp");
+      Error(EXIT_FAILURE, "Couldn't find selected sound card, or, error w/ sound card itself\n");
+   }
+}
+
+/*
+===================
+=
+= MusicShutdown
+=
+===================
+*/
+
+void MusicShutdown( void )
+   {
+   int32 status;
+
+   // if they chose None lets return
+   if ((MusicDevice == NumSoundCards) || (eightytwofifty && numplayers > 1) )
+      return;
+
+   status = MUSIC_Shutdown();
+   if ( status != MUSIC_Ok )
+      {
+      //Error( MUSIC_ErrorString( MUSIC_ErrorCode ));
+      }
+   }
+
+int USRHOOKS_GetMem(char **ptr, unsigned long size )
+{
+   *ptr = malloc(size);
+
+   if (*ptr == NULL)
+      return(USRHOOKS_Error);
+
+   return( USRHOOKS_Ok);
+
+}
+
+int USRHOOKS_FreeMem(char *ptr)
+{
+   free(ptr);
+   return( USRHOOKS_Ok);
+}
+
+char menunum=0;
+
+void intomenusounds(void)
+{
+    short i;
+    static const short menusnds[] =
+    {
+        LASERTRIP_EXPLODE,
+        DUKE_GRUNT,
+        DUKE_LAND_HURT,
+        CHAINGUN_FIRE,
+        SQUISHED,
+        KICK_HIT,
+        PISTOL_RICOCHET,
+        PISTOL_BODYHIT,
+        PISTOL_FIRE,
+        SHOTGUN_FIRE,
+        BOS1_WALK,
+        RPG_EXPLODE,
+        PIPEBOMB_BOUNCE,
+        PIPEBOMB_EXPLODE,
+        NITEVISION_ONOFF,
+        RPG_SHOOT,
+        SELECT_WEAPON
+    };
+    sound(menusnds[(int)menunum++]);
+    menunum %= 17;
+}
+
+void playmusic(char *fn)
+{
+#if PLATFORM_DOS
+    short      fp;
+    long        l;
+
+    if(MusicToggle == 0) return;
+    if(MusicDevice == NumSoundCards) return;
+    if(eightytwofifty && numplayers > 1) return;
+
+    fp = kopen4load(fn,0);
+
+    if(fp == -1) return;
+
+    l = kfilelength( fp );
+    if(l >= 72000)
+    {
+        kclose(fp);
+        return;
+    }
+
+    kread( fp, MusicPtr, l);
+    kclose( fp );
+    MUSIC_PlaySong( MusicPtr, MUSIC_LoopSong );
+#else
+    if(MusicToggle == 0) return;
+    if(MusicDevice == NumSoundCards) return;
+
+    // the SDL_mixer version does more or less this same thing.  --ryan.
+    PlayMusic(fn);
+#endif
+}
+
+char loadsound(unsigned short num)
+{
+    long   fp, l;
+
+    if(num >= NUM_SOUNDS || SoundToggle == 0) return 0;
+    if (FXDevice == NumSoundCards) return 0;
+
+    fp = TCkopen4load(sounds[num],0);
+    if(fp == -1)
+    {
+        sprintf(&fta_quotes[113][0],"Sound %s(#%d) not found.",sounds[num],num);
+        FTA(113,&ps[myconnectindex],1);
+        return 0;
+    }
+
+    l = kfilelength( fp );
+    soundsiz[num] = l;
+
+    Sound[num].lock = 200;
+
+    allocache((long *)&Sound[num].ptr,l,(unsigned char *)&Sound[num].lock);
+    kread( fp, Sound[num].ptr , l);
+    kclose( fp );
+    return 1;
+}
+
+int xyzsound(short num,short i,long x,long y,long z)
+{
+    long sndist, cx, cy, cz, j,k;
+    short pitche,pitchs,cs;
+    int voice, sndang, ca, pitch;
+
+//    if(num != 358) return 0;
+
+    if( num >= NUM_SOUNDS ||
+        FXDevice == NumSoundCards ||
+        ( (soundm[num]&8) && ud.lockout ) ||
+        SoundToggle == 0 ||
+        Sound[num].num > 3 ||
+        FX_VoiceAvailable(soundpr[num]) == 0 ||
+        (ps[myconnectindex].timebeforeexit > 0 && ps[myconnectindex].timebeforeexit <= 26*3) ||
+        ps[myconnectindex].gm&MODE_MENU) return -1;
+
+    if( soundm[num]&128 )
+    {
+        sound(num);
+        return 0;
+    }
+
+    if( soundm[num]&4 )
+    {
+		// FIX_00041: Toggle to hear the opponent sound in DM (like it used to be in v1.3d)
+		if(VoiceToggle==0 || (ud.multimode > 1 && PN == APLAYER && sprite[i].yvel != screenpeek && /*ud.coop!=1 &&*/ !OpponentSoundToggle) ) return -1; //xduke : 1.3d Style: makes opponent sound in DM as in COOP
+
+        for(j=0;j<NUM_SOUNDS;j++)
+          for(k=0;k<Sound[j].num;k++)
+            if( (Sound[j].num > 0) && (soundm[j]&4) )
+              return -1;
+    }
+
+    cx = ps[screenpeek].oposx;
+    cy = ps[screenpeek].oposy;
+    cz = ps[screenpeek].oposz;
+    cs = ps[screenpeek].cursectnum;
+    ca = ps[screenpeek].ang+ps[screenpeek].look_ang;
+
+    sndist = FindDistance3D((cx-x),(cy-y),(cz-z)>>4);
+
+    if( i >= 0 && (soundm[num]&16) == 0 && PN == MUSICANDSFX && SLT < 999 && (sector[SECT].lotag&0xff) < 9 )
+        sndist = divscale14(sndist,(SHT+1));
+
+    pitchs = soundps[num];
+    pitche = soundpe[num];
+    cx = klabs(pitche-pitchs);
+
+    if(cx)
+    {
+        if( pitchs < pitche )
+             pitch = pitchs + ( rand()%cx );
+        else pitch = pitche + ( rand()%cx );
+    }
+    else pitch = pitchs;
+
+    sndist += soundvo[num];
+    if(sndist < 0) sndist = 0;
+    if( sndist && PN != MUSICANDSFX && !cansee(cx,cy,cz-(24<<8),cs,SX,SY,SZ-(24<<8),SECT) )
+        sndist += sndist>>5;
+
+    switch(num)
+    {
+        case PIPEBOMB_EXPLODE:
+        case LASERTRIP_EXPLODE:
+        case RPG_EXPLODE:
+            if(sndist > (6144) )
+                sndist = 6144;
+            if(sector[ps[screenpeek].cursectnum].lotag == 2)
+                pitch -= 1024;
+            break;
+        default:
+            if(sector[ps[screenpeek].cursectnum].lotag == 2 && (soundm[num]&4) == 0)
+                pitch = -768;
+            if( sndist > 31444 && PN != MUSICANDSFX)
+                return -1;
+            break;
+    }
+
+
+    if( Sound[num].num > 0 && PN != MUSICANDSFX )
+    {
+        if( SoundOwner[num][0].i == i ) stopsound(num);
+        else if( Sound[num].num > 1 ) stopsound(num);
+        else if( badguy(&sprite[i]) && sprite[i].extra <= 0 ) stopsound(num);
+    }
+
+    if( PN == APLAYER && sprite[i].yvel == screenpeek )
+    {
+        sndang = 0;
+        sndist = 0;
+    }
+    else
+    {
+        sndang = 2048 + ca - getangle(cx-x,cy-y);
+        sndang &= 2047;
+    }
+
+    if(Sound[num].ptr == 0) { if( loadsound(num) == 0 ) return 0; }
+    else
+    {
+       if (Sound[num].lock < 200)
+          Sound[num].lock = 200;
+       else Sound[num].lock++;
+    }
+
+    if( soundm[num]&16 ) sndist = 0;
+
+    if(sndist < ((255-LOUDESTVOLUME)<<6) )
+        sndist = ((255-LOUDESTVOLUME)<<6);
+
+    if( soundm[num]&1 )
+    {
+        unsigned short start;
+
+        if(Sound[num].num > 0) return -1;
+
+        start = *(unsigned short *)(Sound[num].ptr + 0x14);
+
+        if(*Sound[num].ptr == 'C')
+            voice = FX_PlayLoopedVOC( Sound[num].ptr, start, start + soundsiz[num],
+                    pitch,sndist>>6,sndist>>6,0,soundpr[num],num);
+        else
+            voice = FX_PlayLoopedWAV( Sound[num].ptr, start, start + soundsiz[num],
+                    pitch,sndist>>6,sndist>>6,0,soundpr[num],num);
+    }
+    else
+    {
+        if( *Sound[num].ptr == 'C')
+            voice = FX_PlayVOC3D( Sound[ num ].ptr,pitch,sndang>>6,sndist>>6, soundpr[num], num );
+        else voice = FX_PlayWAV3D( Sound[ num ].ptr,pitch,sndang>>6,sndist>>6, soundpr[num], num );
+    }
+
+    if ( voice > FX_Ok )
+    {
+        SoundOwner[num][Sound[num].num].i = i;
+        SoundOwner[num][Sound[num].num].voice = voice;
+        Sound[num].num++;
+    }
+    else Sound[num].lock--;
+    return (voice);
+}
+
+void sound(short num)
+{
+    short pitch,pitche,pitchs,cx;
+    int voice;
+    long start;
+
+    if (FXDevice == NumSoundCards) return;
+    if(SoundToggle==0) return;
+    if(VoiceToggle==0 && (soundm[num]&4) ) return;
+    if( (soundm[num]&8) && ud.lockout ) return;
+    if(FX_VoiceAvailable(soundpr[num]) == 0) return;
+
+    pitchs = soundps[num];
+    pitche = soundpe[num];
+    cx = klabs(pitche-pitchs);
+
+    if(cx)
+    {
+        if( pitchs < pitche )
+             pitch = pitchs + ( rand()%cx );
+        else pitch = pitche + ( rand()%cx );
+    }
+    else pitch = pitchs;
+
+    if(Sound[num].ptr == 0) { if( loadsound(num) == 0 ) return; }
+    else
+    {
+       if (Sound[num].lock < 200)
+          Sound[num].lock = 200;
+       else Sound[num].lock++;
+    }
+
+    if( soundm[num]&1 )
+    {
+        if(*Sound[num].ptr == 'C')
+        {
+            start = (long)*(unsigned short *)(Sound[num].ptr + 0x14);
+            voice = FX_PlayLoopedVOC( Sound[num].ptr, start, start + soundsiz[num],
+                    pitch,LOUDESTVOLUME,LOUDESTVOLUME,LOUDESTVOLUME,soundpr[num],num);
+        }
+        else
+        {
+            start = (long)*(unsigned short *)(Sound[num].ptr + 0x14);
+            voice = FX_PlayLoopedWAV( Sound[num].ptr, start, start + soundsiz[num],
+                    pitch,LOUDESTVOLUME,LOUDESTVOLUME,LOUDESTVOLUME,soundpr[num],num);
+        }
+    }
+    else
+    {
+        if(*Sound[num].ptr == 'C')
+            voice = FX_PlayVOC3D( Sound[ num ].ptr, pitch,0,255-LOUDESTVOLUME,soundpr[num], num );
+        else
+            voice = FX_PlayWAV3D( Sound[ num ].ptr, pitch,0,255-LOUDESTVOLUME,soundpr[num], num );
+    }
+
+    if(voice > FX_Ok) return;
+    Sound[num].lock--;
+}
+
+int spritesound(unsigned short num, short i)
+{
+    if(num >= NUM_SOUNDS) return -1;
+    return xyzsound(num,i,SX,SY,SZ);
+}
+
+void stopsound(short num)
+{
+    if(Sound[num].num > 0)
+    {
+        FX_StopSound(SoundOwner[num][Sound[num].num-1].voice);
+        testcallback(num);
+    }
+}
+
+void stopenvsound(short num,short i)
+{
+    short j, k;
+
+    if(Sound[num].num > 0)
+    {
+        k = Sound[num].num;
+        for(j=0;j<k;j++)
+           if(SoundOwner[num][j].i == i)
+        {
+            FX_StopSound(SoundOwner[num][j].voice);
+            break;
+        }
+    }
+}
+
+void pan3dsound(void)
+{
+    long sndist, sx, sy, sz, cx, cy, cz;
+    short sndang,ca,j,k,i,cs;
+
+    numenvsnds = 0;
+
+    if(ud.camerasprite == -1)
+    {
+        cx = ps[screenpeek].oposx;
+        cy = ps[screenpeek].oposy;
+        cz = ps[screenpeek].oposz;
+        cs = ps[screenpeek].cursectnum;
+        ca = ps[screenpeek].ang+ps[screenpeek].look_ang;
+    }
+    else
+    {
+        cx = sprite[ud.camerasprite].x;
+        cy = sprite[ud.camerasprite].y;
+        cz = sprite[ud.camerasprite].z;
+        cs = sprite[ud.camerasprite].sectnum;
+        ca = sprite[ud.camerasprite].ang;
+    }
+
+    for(j=0;j<NUM_SOUNDS;j++) for(k=0;k<Sound[j].num;k++)
+    {
+        i = SoundOwner[j][k].i;
+
+        sx = sprite[i].x;
+        sy = sprite[i].y;
+        sz = sprite[i].z;
+
+        if( PN == APLAYER && sprite[i].yvel == screenpeek)
+        {
+            sndang = 0;
+            sndist = 0;
+        }
+        else
+        {
+            sndang = 2048 + ca - getangle(cx-sx,cy-sy);
+            sndang &= 2047;
+            sndist = FindDistance3D((cx-sx),(cy-sy),(cz-sz)>>4);
+            if( i >= 0 && (soundm[j]&16) == 0 && PN == MUSICANDSFX && SLT < 999 && (sector[SECT].lotag&0xff) < 9 )
+                sndist = divscale14(sndist,(SHT+1));
+        }
+
+        sndist += soundvo[j];
+        if(sndist < 0) sndist = 0;
+
+        if( sndist && PN != MUSICANDSFX && !cansee(cx,cy,cz-(24<<8),cs,sx,sy,sz-(24<<8),SECT) )
+            sndist += sndist>>5;
+
+        if(PN == MUSICANDSFX && SLT < 999)
+            numenvsnds++;
+
+        switch(j)
+        {
+            case PIPEBOMB_EXPLODE:
+            case LASERTRIP_EXPLODE:
+            case RPG_EXPLODE:
+                if(sndist > (6144)) sndist = (6144);
+                break;
+            default:
+                if( sndist > 31444 && PN != MUSICANDSFX)
+                {
+                    stopsound(j);
+                    continue;
+                }
+        }
+
+        if(Sound[j].ptr == 0 && loadsound(j) == 0 ) continue;
+        if( soundm[j]&16 ) sndist = 0;
+
+        if(sndist < ((255-LOUDESTVOLUME)<<6) )
+            sndist = ((255-LOUDESTVOLUME)<<6);
+
+        FX_Pan3D(SoundOwner[j][k].voice,sndang>>6,sndist>>6);
+    }
+}
+
+void TestCallBack(unsigned long num)
+{
+    short tempi,tempj,tempk;
+
+        if(num < 0)
+        {
+            if(lumplockbyte[-num] >= 200)
+                lumplockbyte[-num]--;
+            return;
+        }
+
+        tempk = Sound[num].num;
+
+        if(tempk > 0)
+        {
+            if( (soundm[num]&16) == 0)
+                for(tempj=0;tempj<tempk;tempj++)
+            {
+                tempi = SoundOwner[num][tempj].i;
+                if(sprite[tempi].picnum == MUSICANDSFX && sector[sprite[tempi].sectnum].lotag < 3 && sprite[tempi].lotag < 999)
+                {
+                    hittype[tempi].temp_data[0] = 0;
+                    if( (tempj + 1) < tempk )
+                    {
+                        SoundOwner[num][tempj].voice = SoundOwner[num][tempk-1].voice;
+                        SoundOwner[num][tempj].i     = SoundOwner[num][tempk-1].i;
+                    }
+                    break;
+                }
+            }
+
+            Sound[num].num--;
+            SoundOwner[num][tempk-1].i = -1;
+        }
+
+        Sound[num].lock--;
+}
+
+
+// no idea if this is right. I added this function.  --ryan.
+void testcallback(unsigned long num)
+{
+//    STUBBED("wtf?");
+    TestCallBack(num);
+}
+
+
+void clearsoundlocks(void)
+{
+    long i;
+
+    for(i=0;i<NUM_SOUNDS;i++)
+        if(Sound[i].lock >= 200)
+            Sound[i].lock = 199;
+
+    for(i=0;i<11;i++)
+        if(lumplockbyte[i] >= 200)
+            lumplockbyte[i] = 199;
+}
+
--- /dev/null
+++ b/Game/src/sounds.h
@@ -1,0 +1,62 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+//****************************************************************************
+//
+// sounds.h
+//
+//****************************************************************************
+#include "audiolib\fx_man.h"
+
+#ifndef _sounds_public_
+#define _sounds_public_
+
+#define HIRESMUSICPATH "tunes"
+
+extern int32 FXDevice;
+extern int32 MusicDevice;
+extern int32 FXVolume;
+extern int32 MusicVolume;
+extern fx_blaster_config BlasterConfig;
+extern int32 NumVoices;
+extern int32 NumChannels;
+extern int32 NumBits;
+extern int32 MixRate;
+extern int32 MidiPort;
+extern int32 ReverseStereo;
+
+void SoundStartup( void );
+void SoundShutdown( void );
+void MusicStartup( void );
+void MusicShutdown( void );
+
+/* sounds.c */
+void clearsoundlocks(void);
+
+/* dunno where this came from; I added it. --ryan. */
+void testcallback(unsigned long num);
+
+#endif
--- /dev/null
+++ b/Game/src/types.h
@@ -1,0 +1,102 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+#ifndef _types_public
+#define _types_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+//***************************************************************************
+//
+//  Global Data Types (For portability)
+//
+//***************************************************************************
+
+typedef unsigned char           uint8;
+typedef uint8                   byte;
+typedef signed char             int8;
+
+typedef unsigned short int      uint16;
+typedef uint16                  word;
+typedef short int               int16;
+
+typedef unsigned long           uint32;
+typedef long                    int32;
+typedef uint32                  dword;
+
+typedef int32                   fixed;
+typedef char                    boolean;
+typedef float                   float32;
+typedef double                  float64;
+typedef long double             float128;
+typedef float64                 appfloat;
+
+#define MAXINT32                0x7fffffff
+#define MININT32                -0x80000000
+#define MAXUINT32               0xffffffff
+#define MINUINT32               0
+
+#define MAXINT16                0x7fff
+#define MININT16                -0x8000
+#define MAXUINT16               0xffff
+#define MINUINT16               0
+
+//***************************************************************************
+//
+//  boolean values
+//
+//***************************************************************************
+
+#define true ( 1 == 1 )
+#define false ( ! true )
+
+//***************************************************************************
+//
+//  BYTE ACCESS MACROS
+//
+//***************************************************************************
+
+// WORD macros
+#define Int16_HighByte( x ) ( (uint8) ((x)>>8) )
+#define Int16_LowByte( x )  ( (uint8) ((x)&0xff) )
+
+// DWORD macros
+#define Int32_4Byte( x )   ( (uint8) ((x)>>24)&0xff )
+#define Int32_3Byte( x )   ( (uint8) (((x)>>16)&0xff) )
+#define Int32_2Byte( x )   ( (uint8) (((x)>>8)&0xff) )
+#define Int32_1Byte( x )   ( (uint8) ((x)&0xff) )
+
+#ifdef __NeXT__
+#define stricmp strcasecmp
+#define strcmpi strcasecmp
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/Game/src/util_lib.h
@@ -1,0 +1,67 @@
+//-------------------------------------------------------------------------
+/*
+Copyright (C) 1996, 2003 - 3D Realms Entertainment
+
+This file is part of Duke Nukem 3D version 1.5 - Atomic Edition
+
+Duke Nukem 3D is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Original Source: 1996 - Todd Replogle
+Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
+*/
+//-------------------------------------------------------------------------
+
+//***************************************************************************
+//
+//    UTIL_LIB.C - various utils
+//
+//***************************************************************************
+
+#ifndef _util_lib_public
+#define _util_lib_public
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if (defined(__MSDOS__) && !defined(__FLAT__))
+extern  int16    _argc;
+#else
+extern  int32    _argc;
+#endif
+extern  char **  _argv;
+
+void RegisterShutdownFunction( void (* shutdown) (void) );
+void   Error (int errorType, char *error, ...);
+
+char   CheckParm (char *check);
+
+void   *SafeMalloc (int32 size);
+int32  SafeMallocSize (void * ptr);
+void   SafeFree (void * ptr);
+void   SafeRealloc (void ** ptr, int32 newsize);
+int32  ParseHex (char *hex);
+int32  ParseNum (char *str);
+int16  MotoShort (int16 l);
+int16  IntelShort (int16 l);
+int32  MotoLong (int32 l);
+int32  IntelLong (int32 l);
+
+void HeapSort(char * base, int32 nel, int32 width, int32 (*compare)(), void (*switcher)());
+
+#ifdef __cplusplus
+};
+#endif
+#endif
--- /dev/null
+++ b/V19.7.1_doc.txt
@@ -1,0 +1,728 @@
+================
+Table of content 
+================
+
+I.   Change log
+
+ I.1 Change log xDuke v19.7.1 vs xDuke 19.7, April 2006
+ I.2 Change log xDuke v19.7   vs xDuke 19.6, March 2006
+ I.3 Change log xDuke v19.6   vs xDuke 19.5
+ I.4 Change log xDUke v19.5   vs xDuke 19.3
+ I.5 Change log xDuke v19.3   vs Rancid 19.2
+
+II.  Frequently Asked Questions
+III. Compiling xDuke
+IV.  Previous DOC for rancid 19.1 and less
+V.   GNU GENERAL PUBLIC LICENSE Version 2, June 1991 
+
+
+xd#m-klein.com  replace # by @.
+
+
+
+=============
+I. Change log
+=============
+
+ 
+ I.1 Change log xDuke v19.7.1 vs xDuke 19.7, April 2006
+ ======================================================
+
+Extra fixes. V19.7.1 allows netgames with 19.7 users.
+
+
+Bugs fixed 
+----------
+
+- FIX_00086: grp loaded by smaller sucessive chunks to avoid overloading low ram computers (Spanator)
+- FIX_00087: intro in 1024x768 mode being slow. Undone FIX_00070 and fixed font issue again (Turrican/Bryzian)
+- FIX_00088: crash on maps using a bad palette index like the end of roch3.map (NY00123)
+- FIX_00089: scoreboard not shown for last player who quits a DM. Only 19.7 affected. (Sarah)
+- FIX_00090: Removed showinfo key. FPS were shown after CRC msg. CRC not always removed. (Turrican)
+- FIX_00091: Main theme starting too early (Bryzian/Turrican)
+- FIX_00092: corrupted saved files making all the next saved files invisible (Bryzian)
+- FIX_00093: fixed crashbugs in multiplayer (mine and blimp)
+
+
+
+ 
+ I.2 Change log xDuke v19.7 vs xDuke 19.6, Marsh 2006
+ ====================================================
+
+
+Bugs fixed 
+----------
+
+- FIX_00056: Refresh issue w/FPS, small Weapon and custom FTA, when screen resized down
+- FIX_00057: Using ESC to get out of the console mode wouldn't take pause mode off
+- FIX_00058: Save/load game crash in both single and multiplayer
+- FIX_00059: levels going too far in menu in 1.3d and 1.5 in multiplayer (Turrican)
+- FIX_00060: Repeat key function was not working in the menu
+- FIX_00061: "ERROR: Two players have the same random ID" too frequent cuz of internet windows times
+- FIX_00062: Better support and identification for GRP and CON files for 1.3/1.3d/1.4/1.5 (Turrican)
+- FIX_00063: Duke's angle changing or incorrect when using toggle fullscreen/window mode
+- FIX_00064: Cinematics explosions were not right for 1.3/1.3d grp (Turrican)
+- FIX_00065: Music cycling with F5 and SHIFT-F5 messed up (Turrican)
+- FIX_00066: Removed the QuickKick restrictions for 1.3/1.3d (like 1.3d dos version behavior)
+- FIX_00067: *.cfg parser fails to read negative values as it could be for the Midi selection (Turrican)
+- FIX_00068: menu "New Game" in multiplayer mode now allowing left/right arrow for selection (Turrican)
+- FIX_00069: Hitting Esc at the menu screen shows an empty green screen
+- FIX_00070: Small Font have a missing line (fixed by David Koenig)
+- FIX_00071: do not ask for internal default con if external con or internal con is buggy
+- FIX_00072: all files are now 1st searched in Duke's root folder and then in the GRP.
+- FIX_00073: menu off messed up. While in game hit Esc -> select quit -> press esc => stuck in menu
+- FIX_00074: Shift f5 doesn't change hi-res tunes, but only midi tunes
+- FIX_00075: Bad Sensitivity along Y axis when using mouse in menu (Turrican)
+- FIX_00076: Added default names for bots + fixed a "killed <name>" bug in Fakeplayers with AI
+- FIX_00077: Menu goes directly to the "NEW GAME" sub-menu when starting new game (Turrican)
+- FIX_00078: Out Of Synch error (multiplayer) when using console in pause mode
+- FIX_00079: "waiting player" screen showing a black screen
+- FIX_00080: Out Of Synch in demos. Tries recovering OOS in old demos v28/29/117/118. New: v30/v119. 
+- FIX_00081: Screen border in menu (Turrican)
+- FIX_00082: /q option taken off when playing a demo (Turrican)
+- FIX_00083: Sporadic crash on fullscreen/window mode toggle
+- FIX_00084: Various bugs in the load game (single player) option if ESC is hit or if wrong version 
+
+
+Enhancements
+------------
+- FIX_00085: Optimized Video driver. FPS increases by 0-20%.
+
+
+ 
+ I.3 Change log xDuke v19.6 vs xDuke 19.5
+ ========================================
+
+
+Bugs fixed 
+----------
+
+- FIX_00051: Medkit was broken
+- FIX_00055: ReverseStereo was broken
+
+
+Enhancements
+------------
+
+
+- FIX_00052: Y axis for the mouse is now twice as sensitive as before
+- FIX_00015: (bis) changed NumVoices=8 to NumVoices=32
+- FIX_00053: Added more defaults when creating a new CFG: ScreenWidth = 800, ScreenHeight = 600, 
+             ScreenGamma = 16, CommbatMacro#x to default quotes, RunMode = 1, Hide_Weapon = "S" "", 
+             Auto_Aim = "V" "",Show_Info = "B" "", Console = "C" "", OpponentSoundToggle = 1,
+             PlayerName = "XDUKE", RTSName = "DUKE.RTS", FXDevice = 8 (use 13 to disable),
+             MusicDevice = 0 (use 13 to disable), ControllerType = 1 (mouse+kbd) 
+- FIX_00054: Changed MouseSensitivity MouseSensitivity_Y to MouseSensitivity_X_Rancid and
+             MouseSensitivity_Y_Rancid respectively, so you can use the same CFG for both
+             JonoF and Rancid without changing the mouse sensitivity all the time
+
+
+
+
+ 
+ I.4 Change log xDUke v19.5 vs xDuke 19.3
+ ========================================
+
+
+Bugs fixed 
+----------
+
+- FIX_00001: Mouse speed was uneven and slower in windowed mode vs fullscreen mode.
+- FIX_00023: Moved Addfaz's autoaim handler to synch variables (to avoid out of synch)
+- FIX_00030: Brightness step was not the same from the keys vs menu 
+
+
+
+Enhancements
+------------
+
+- FIX_00011: duke3d.cfg not needed anymore to start the game. Will create a default one
+             if not found and use default keys.
+- FIX_00012: added "weapon autoswitch" toggle allowing to turn the autoswitch off
+             when picking up new weapons. The weapon sound on pickup will remain on, to not 
+             affect the opponent's gameplay (so he can still hear you picking up new weapons)
+- FIX_00015: Backward compliance with older demos (down to demos v27, 28, 116 and 117 only)
+- FIX_00027: Added an extra small statusbar (HUD)
+- FIX_00039: Toggle autoaim between Normal (full) and partial (on bullet weapons only)
+- FIX_00040: Preventing multi keypress locks (designed with JonoF)
+- FIX_00046: Visual C++ Runtimes (dll) now integrated in exe
+
+
+
+
+
+ 
+ I.5 Change log xDuke  v19.3 vs Rancid 19.2
+ ==========================================
+
+
+Bugs fixed 
+----------
+
+- FIX_00003: Pause mode is now fully responsive - (Thx to Jonathon Fowler tips)
+- FIX_00007: game speed corrected. The game speed is now as the real 
+             DOS duke3d. Unloading a full 200 bullet pistol must take 45.1 sec.
+- FIX_00008: minor protocol error after frags (NET TRANSPORT ERROR: Unknown command 5)
+- FIX_00019: DigitalAxis Handling now supported. (cool for medkit use)
+- FIX_00020: Protect you from assigning a function to the ESC key through duke3d.cfg
+- FIX_00021: Duke was moving when moving the mouse up/down. Y axis move is disabled.
+- FIX_00022b: Sound effects are now sharper and they sound as in the real DOS duke3d.
+- FIX_00028: No need to call the videodriver on gameexit()
+- FIX_00031: Names now limited to 10 chars max that is the fragbar field limit.
+- FIX_00033: Fake multi and AI are now fully working
+- FIX_00034: Demos do not turn your run mode off anymore
+- FIX_00035: allow loading internal con file in the GRP if external con files not found.
+- FIX_00037: steroids trigger a too many sprite respawn in 1.3 and 1.3d.
+- FIX_00041: Toggle to hear the opponent sound in DM (like it used to be in v1.3d)
+- FIX_00044: Markers are now on by default in netgames (as real DOS duke3d)
+- FIX_00048: entering a level, turning music off in ogg format, entering 
+             a new level and turning the music on was crashing.
+- FIX_00049: /disableautoaim was broken. Now fully working.
+
+
+
+
+Enhancements
+------------
+                                
+
+- FIX_00002: New Toggle Windowed/FullScreen system now simpler and will 
+             dynamically change for Windib or Directx driver. Windowed/Fullscreen 
+             toggle also made available from menu.
+- FIX_00004: SDL.dll and SDL_Mixer.dll are now integrated within the exe
+             (this also makes the Windib/Directx driver switching easier with SDL)
+- FIX_00005: Mouse pointer can be toggled on/off (see mouse menu or use CTRL-M)
+             This is usefull to move the duke window when playing in window mode.
+- FIX_00006: better naming system for screenshots + message when pic is taken. 
+             Use ./screenshots folder. Screenshot code rerwritten. Faster and
+             makes smaller files. Doesn't freeze or lag the game anymore.
+- FIX_00009: Show map CRC and GRP file version of each player in case of Out Of Synch
+- FIX_00010: Hi resolution tunes (*.ogg files) are now first searched in .\tunes\ 
+             and then searched in the main folder. Allows a better separation of files
+             OGG tunes are NOT required. They are only used if found else normal
+             MIDI files are used by default for music
+- FIX_00013: 3rd person camera view during demo playback can now be 
+             turned off (no need to use hacked no-camera maps anymore)
+- FIX_00014: Added Y cursor setup for mouse sensitivity in the menus 
+- FIX_00015: Forced NumVoices=8, NumChannels=2, NumBits=16, MixRate=44100, ScreenMode = x
+- FIX_00016: Build in Keyboard/mouse setup.
+- FIX_00017: heavy autoexec.cfg not needed anymore.
+- FIX_00018: Removed the "smoothmouse" option. This was just a bad fix. Mouse is now faster,
+             smoother.
+- FIX_00022: Automatically recognize the shareware grp (v1.3) + full version (1.3d) +
+             atomic (1.4/1.5 grp) and the con files version (either 1.3 or 1.4) (JonoF's idea)
+- FIX_00024: A key can be assigned to the new SHOW_INFO function. Display map CRC when
+             in deathmatch. Usefull to identify who loaded a wrong map in multiplayer.
+- FIX_00025: Can toggle FPS and map name during a game (use dnrate OR toggle
+             from menu when in deathmatch). 
+- FIX_00026: Weapon can now be hidden (on your screen only).
+- FIX_00029: toggle cinematics on / off for very fast boot up and exit
+- FIX_00032: Added multi base GRP manager. Use duke3d*.grp to handle multiple grp.
+- FIX_00036: Mouse wheel can now be used in menu
+- FIX_00038: Improved Mouse accuracy (losses of integer computation)
+- FIX_00042: Build in Video setup.
+- FIX_00043: Nicer exit on error. Ask the user to hit a key on exits and error exits.
+- FIX_00045: Autoaim mode can now be toggled on/off from menu
+- FIX_00050: send game's rev to opponents
+
+
+
+==============================
+II. Frequently Asked Questions
+==============================
+
+
+- Duke slows down and he's almost stuck when I 
+  pick up a new weapon or get hit. It gets normal
+  again after 1 or 2 seconds: This bug happens
+  when xDuke is processing some fadding effects and
+  when some application running in the background are
+  using or locking some video ressources, like winamp, 
+  bittorrent, Longhorn inspirat theme, and some 
+  applications using special video effect. Try shutting 
+  them down 1 by 1 and see if it helps. Use Ctrl-Alt-Del
+  under the the process tab to kill invisible applications
+
+- My color are all messed up sometims: simple hit alt-enter
+  to toggle fullscreen\windowed mode. Do it again to get back
+  to your initial mode. It will restore the colors.
+
+- The mouse doesn't work and I see a mouse pointer: hit
+  alt-m, This grabs or frees up the mouse. It's usefull to
+  free the mouse when you are in windwed mode.
+
+- duke is globally slow: you are using a too hi resolution.
+  Lower the resolution in the video options till it's fine. 
+  It's useless to run xDuke in your maximum resolution, like 
+  1600x1200. Using 800x600 is way enough. Go in option and 
+  select the SHOW FPS option. This will show you the Frames Per
+  Second of the game. 
+
+- Keyboard setup menu is slow as hell in 1024x768 resolution 
+  mode _only_ : Change that resolution to a lower one, or try 
+  using a higher resolution if you've got enough cpu power : 
+  if u've got a fast enough CPU, even in higher resolutions 
+  than 1024x768, this _huge_ slowdown won't happen. This is a
+  known slow down bug in the 1024x768 screen resolution, and 
+  it will be fixed in the future : simply avoid it, for now
+ 
+- Duke is slow when (looking down) on slopes, when getting 
+  closer or against to any sprite (even the sprites sticked 
+  on walls). Same crap as the previous question. That's a 
+  known 102x768 slow down. Change away from the 1024x768 resolution 
+  and get a lower one or rise it above 1024x768 if you have enough 
+  cpu power.
+
+- I don't have a cfg file: You need only 2 files, the exe
+  and at least 1 grp file. Nothing else is needed. The cfg will
+  be recreated by xDuke if it's missing.
+
+- Where are the con files? xDuke uses the con files it finds
+  in the grp files, as they are always included in the grp file.
+  External con files are thus not required. You can still use 
+  external con files if you like.
+
+- Duke Nukem 3D is now free under the 
+  GNU licence. See Licence below.
+
+- To update to a full version, you only need to provide a 
+  better duke3d.*.grp as the one provided with the atomic 
+  pack. (3Drealms Licence doesn't allow me to distribute
+  the full version of the GRP, this is why this port 
+  has a shareware GRP only)
+
+- To play online, report bugs and questions, 
+  visit: http://forums.dukesterx.net
+
+- for general support, missing files etc ..
+  see: http://www.vachu.com/duke3d
+  
+- To use multiple GRP file, rename them as
+  duke3d*.grp in your duke's folder.
+
+  EG: duke3d.13.share.grp   for the shareware v1.3
+      duke3d.13d.full.grp   for the full version of 1.3 (called v1.3d)
+      duke3d.15.atomic.grp  for Atomic etc ...
+
+  xDuke will ask you what grp to use if it finds more than
+  one base GRP file.
+
+- Shareware GRP doesn't allow to play user's maps. You can 
+  still play netgames.
+
+- This port was originally started by Icculus
+  (www.icculus.org) then by Dave (www.rancidmeat.com)
+  who gave the name of "rancidmeat" duke3d port. I have
+  included all of his documentation below.
+  Dave's lastest rev was rancidmeat v19.2.
+  I decided to fix bugs and glitches to keep the 
+  port alive and renamed the port xDuke as Dave asked it.
+  xDuke port is available at http://duke3d.m-klein.com 
+
+- I found a bug. What can I do?
+  Report it in the forums http://forums.dukesterx.net
+
+- What's the "tunes\" folder?
+  This folder contains the new hi-quality arrangements 
+  of the Duke3d songs by Mark McWane. If this folder is 
+  not found, you will only hear the midi formatted songs.
+  See http://www.markmcwane.com/ for more cool songs!
+
+
+====================
+III. Compiling xDuke
+====================
+
+Source code is provided in a different package at duke3d.m-klein.com
+since most of people are interested by the compiled Binaries only.
+
+It was compiled successfully or Windows. Compilation for other 
+plateforms should be quite easy: I keep the code as portable as 
+possible. It also uses the SDL lib that is fully ported to Linux.
+Furthermore xDuke is based on Rancidmeat port that was based on 
+the icculus port. Icculus is a very portable port that is compiled 
+successfully on at least 5 different plateforms. Pl. contact me
+if you have trouble at compiling for other plateforms as Windows.
+
+For windows:
+
+1. Install the lastest Directx SDK at Microsoft Web Site. This is a 
+   350 Mb download. This SDK won't work if you don't have a legal 
+   Windows Installed since it is checked by the "Genine Advantage"
+   This SDk is only needed if you don't have it already. EG Visual C
+   2003 and before have the package. (VC 2005 doesn't have it though).
+
+2. You will need the source code (not the dll/runtime) of both SDL and 
+   SDL Mixer at http://www.libsdl.org/ and  http://www.libsdl.org/projects/SDL_mixer/
+   Compile them as a LIB and not as a DLL.
+
+3. Compile the Engine first as a LIB. You can try various flags, but I'll suggest
+   using nDEBUG; nUSE_I386_ASM; PLATFORM_WIN32; UDP_NETWORKING; WIN32; _LIB; 
+   _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE
+
+4. Compile the Game as a Win32 EXE. Flags should be something like 
+   nDBGRECORD; nDEBUG; nUSE_I386_ASM; WIN32; _CONSOLE; PLATFORM_WIN32; 
+   _CRT_SECURE_NO_DEPRECATE; _CRT_NONSTDC_NO_DEPRECATE
+   With the following additional Lib dependencies:
+   dxguid.lib sdl.lib sdl_mixer.lib winmm.lib WS2_32.lib will do.
+
+
+=========================================
+IV. Previous DOC for rancid 19.1 and less
+=========================================
+
+Release: Build 19.1
+Date: 4/24/2004 2:50pm PST
+
+The homepage for this port is:
+http://www.rancidmeat.com/project.php3?id=1
+
+The official irc channel for this port is:
+irc.homelan.com #DukeNukem
+
+-dave
+
+Special Thanks to Adrian "JimCamel" Clark, and Ahmed Rasool for contributing
+new console variables and commands. And Thanks to "Worsel"for helping debug
+the Joystick issues. Thanks to AddFaz for networking changes, and auto-aim toggle.
+Thanks the Spem for the joystick testing.
+
+
+Build notes:
+---------------------
+Build 19.1
+* Modified optimization compiler flags in VC6 projects.
+* Fixed game_dir option
+* Fixed issues with flying pig cops not moving.
+
+Build 19
+* Added .ogg/.s3m/.mod/.it/.xm/.wav/.mp3 Music playback support
+  (mp3 playback is pretty crappy. I blame the smpeg lib)
+* Improved performance. (breaks old .dmo files.. enjoy the new ones) 
+* Added new 2 player stable networking. (-netmode_stable) 
+* Included initial version of Setup_w32. (does very little currently)
+* "Fixed" blimp crash in multiplayer.
+* Fixed a console crash when pressing the down arrow.
+* Made a few changes to SDL lib so that the renderer does not need 
+  to be set via environment variables.
+
+
+Build 18.5(alpha development build)
+* This build incorporates new experimental networking.
+
+Build 18.4(development build)
+* Added support for DukesterX's stun server feature. This should help those behind
+  firewalls. (thx AddFaz)
+
+Build 18.3(development build)
+* Added screenshot functionality back in. This had been on the TODO list for a while.
+  It now saves out a 24-bit BMP in the game directory. (F12 to take a screenshot)
+
+Build 18.2(development build)
+* Opening the console now pauses the game for singleplayer games.
+* Added -game_dir commandline param for mods/total conversions. 
+  This attempts to load all resources from the specified game directory.
+  It will fallback to the main root dir if missing resources.
+  It has been tested with the "Alienz TC" and "Duke LA" Total conversions.
+  Saved games are saved to the game_dir.
+
+Example use of each: 
+  Alienz TC
+  duke3d_w32.exe -game_dir mods\alienztc
+
+  Duke L.A.
+  duke3d_w32.exe -game_dir mods\dukela /xLAGame.DM /gdla.prg
+
+  To download these or other TCs, check out: 
+  http://www.planetduke.com/duke3/files/tc_1.shtml
+
+  You simply need to make a directory to stick the TCs files in. I recommend \duke3d_w32\mods\<Your TC>
+
+*NOTE: this release is not intended as an official release. It's simply for the 
+       amusment of those who wish to be amused. It has not undergone the standard
+       brute force testing. So, feel free to check it out, but it may not act as
+       expected. If you notice any odd behavior, let me know. If you run into problems 
+       with this version, you can simply revert to Build 18.1. 
+
+
+Build 18.1
+* Fixed an issue with joystick support. (ie. the buttons and hats were unresponsive)
+* Fixed an issue with ControllerType 7 breaking smooth mouse.
+
+Build 18
+* Changed input to allow the original functionality. (ie. two keys per command)
+* Fixed the input bug in which the keyboard mouse and joystick would fight causing
+  users to not have the ability to bind one command to the both the keyboard AND mouse.
+  This was most apparent on the mouse wheel when trying to bind it to next/prev weapon.
+* Changed Console to allow for user to bind it to any key, rather than forcing the tilde.
+* Mouse now works in the menu.
+* Autoaim can now be toggled via the console.
+* New router fixes to the net code.
+* Integrated automatic optimal renderer selection based on windowed versus fullscreen.
+  (windib vs. directx)
+* When connecting to a NET game, custom maps are now started immediately, instead of dropping 
+  into a setup screen. This fixes the issue where players would accidentally hit "start" 
+  instead of "custom map" thus launching episode 1 instead of the custom map. 
+* Markers are also disabled automatically for network games. This is done for network
+  integrity/stability/bandwidth consumption. This may change when network code stability is
+  more mature.
+
+Build 17.9.3(patch)
+* Fixed annoying reverb bug. (This would cause an infinite sound hiccup)
+* Added "DebugSound" console variable
+
+Build 17.9.2(patch)
+* Fixed crash with sloping walls.
+* Fixed crash with sound system. (feel free to use EnableCubic once again)
+* Changed console Up/Down arrow to loop through the commands you entered.
+* Added "Sensitivity" console variable for non-smooth mouse sensitivity. (thx Chris Marsh)
+* Added "TickRate" console variable. (default = 120)
+* Added "TicksPerFrame" console variable. (default = 26)
+!! These last two console variables control the speed of the game.
+   The formula is this: TickRate/TicksPerFrame. You should keep a 4:1 ratio
+   to ensure that the demo playback is not broken. If you toy with these
+   during a network game, you'll most likely go out of sync really quick.
+!! Also, make sure that TicksPerFrame is never larger than TickRate. 
+
+Build 17.9.1(patch)
+* Added "top hat" support for Joysticks
+* Fixed a crash with swinging doors where the associated sprite index was looking for -1.
+* <alt-enter> crash should be fixed.
+
+Build 17.9
+* Fixed ControllerType=1(keyboard + mouse) problem. Mouse would move player forward when in mouse aim mode.
+* Added a smooth mouse mode.(Experimental)
+* Added cvars to support the new mouse mode.
+  ->EnableSmoothMouse 1 = enables smooth mouse. This is the default.
+  ->SmoothMouseSensX 15000 = Sets the sensitivity of the X axis while in smooth mouse mode. 
+  ->SmoothMouseSensY 15000 = Sets the sensitivity of the Y axis while in smooth mouse mode. 
+
+Build 17.8-5
+* Fixed issue with fullscreen mode.
+
+Build 17.8
+* Fixed Mouse-flip for ControlType 2 and 7.
+* Fixed Analog issue for joystick axis 0 and 1.
+* Fixed issue where analog mouse settings were over writing the joystick
+  settings.
+
+Build 17.7
+* Fixed "ControlType 7(joystick + mouse)" problems. Keyboard input should work in 
+  mode 7 as well.
+! Currently you can not bind the same function (ie. shoot, jump) to a 
+  mouse/joystick/keyboard button. I will be fixing that. I don't anticipate that being
+  a huge problem for people. It's more of an annoyance. It shouldn't affect too many 
+  folks.
+
+Build 17.6
+* Fixed(hack-fix) Joystick problem where one could not properly move with joystick when
+  in mouse-look mode. (I will probably make a nicer fix for this later)
+* Re-implemented "analog_lookingupanddown" axis type for joysticks to use for looking
+  up and down.
+
+Build 17.5 Bug fixes
+* Fixed the ugly bug where "JoystickAnalogScale" was not being save in the config.
+* Added a joystick value debug cvar. "DebugJoystick 1"
+
+Build 17
+* New Windows help file version of the docs
+* Added "TransConsole" CVAR
+* Added validation to "Level" command for the 1.5 data
+* Fixed WinMidi to allow users to change which midi device is used via duke3d.cfg. 
+  ->Change the "MusicDevice" variable to match your device. Most users will want this setting:
+    "MusicDevice = 0"
+* Added command line option for fullscreen. "-fullscreen"
+* Joystick support! And the addition of a Joystick+Mouse configuration. 
+  ->Please read the duke3d_w32.chm for more information. (the input section)
+
+
+Build 16
+* Integrated fixed sound code from kode54. (yay 48000hz!)
+* Fixed crash on Area51 demo.
+* Added many console variables
+* Fixed duke3d.cfg handling of negative numbers. 
+  ->(bug would cause duke not to run if there negative numbers in the duke3d.cfg)
+* Added new autoexec.cfg system. This will allow you to auto-run commands in the 
+  console at startup. One main reason for this is to allow folks to run "classic"
+  mode without needing to type that into the console everytime they run the game.
+* Added transparency to the console. (Thanks for the tip Cyborg)
+
+
+Build 15
+* Fixed a small console rendering bug. (caused by different resolutions)
+
+Build 14
+* Added brand new drop down console.
+  ->Read console.txt for information.
+
+Build 13
+* Fixed demo playback for 1.4 demos (I hope)
+* Integrated new Icculus networking changes. 
+  ->Updated networking.txt 
+* Integrated several other Icculus fixes. (for case-insensitivity etc.)
+
+Build 12
+* Fixed the demo playback for the original demo (Atomic Edition)
+  ->(Thanks to Andy Hill)
+* Updated the duke3d.cfg default keyboard config to be a little less goofy.
+* Integrated a few small Icculus port updates.
+
+Build 11
+* Integrates the lastest Icculus changes
+* Currently not using the latest networking code off the Icculus CVS.
+  -> It's broken on win32, so I didn't integrate it.
+  -> I need to look into that. For now net play between
+  -> two players works in this build.
+* Added some player maps tht I and a friend worked on back in 
+  -> the day. Also included some maps we used to play frequently.
+
+Build 10
+* Integrated new Audiolib-based sound system. 
+  Fixes the sound lag issues
+  Fixes the sound pitch issue
+  Fixes the sound cutting out issue.
+  Kudos to Icculus.org
+
+Build 9
+* Improved VOC sampling.
+* TCP/IP networked games 
+  -Not optimized for internet play.
+  -Lan games work pretty well though.
+
+Build 8
+* Can load .GRP files for Shareware, 1.3, and Atomic Edition
+  So no more need for extracting the data files first. The .GRP is all you need.
+
+Build 7
+* Odd movement of some platforms in the game(ie. secret rocket launcher E1L1) is fixed.
+* New Icculus code merged.
+
+Build 6
+* Sounds now cache after being played once. This fixes the sound lag bug.
+  I'm going to look into having all sounds simply pre-cache upon level load. (maybe)
+* Integrated the latest Icculus cvs code.
+* I left the win_midi code in since there are some bugs with the midi playpack
+  in the SDL port.
+* Fixed the crash on exit bug in scriplib(Thanks to Icculus)
+
+
+HOWTO get it running:
+---------------------
+
+1. copy your duke3d.grp into the "bin" directory.
+2. run duke3d_w32.exe
+
+Hint: you can run custom maps like so:
+duke3d_w32.exe -map spaceprt.map
+
+Networked play using TCP/IP:
+read the duke3d_w32.chm doc.
+
+
+Links:
+------
+
+libSDL                    
+  - http://www.libsdl.org
+SDL_mixer                 
+  - http://www.libsdl.org/projects/SDL_mixer/
+Icculus Duke3d Linux port 
+  - http://icculus.org/duke3d/
+Another win32 port:
+  - http://www.shacknews.com/ja.zz?id=7144651
+
+
+
+
+
+==================================================
+V. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 
+==================================================
+
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+Preamble
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. 
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. 
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. 
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 
+
+The precise terms and conditions for copying, distribution and modification follow. 
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 
+
+
+a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 
+
+b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 
+
+c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 
+
+a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
+
+b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
+
+c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
+
+
+END OF TERMS AND CONDITIONS
+
+
--- /dev/null
+++ b/duke3d_w32.sln
@@ -1,0 +1,28 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game\Game.vcproj", "{6D80B050-1DC6-406A-98E3-60C65C3304F3}"
+	ProjectSection(ProjectDependencies) = postProject
+		{05511B4A-FB13-49E5-AE53-EE1F063C055D} = {05511B4A-FB13-49E5-AE53-EE1F063C055D}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Engine", "Engine\Engine.vcproj", "{05511B4A-FB13-49E5-AE53-EE1F063C055D}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6D80B050-1DC6-406A-98E3-60C65C3304F3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{6D80B050-1DC6-406A-98E3-60C65C3304F3}.Debug|Win32.Build.0 = Debug|Win32
+		{6D80B050-1DC6-406A-98E3-60C65C3304F3}.Release|Win32.ActiveCfg = Release|Win32
+		{6D80B050-1DC6-406A-98E3-60C65C3304F3}.Release|Win32.Build.0 = Release|Win32
+		{05511B4A-FB13-49E5-AE53-EE1F063C055D}.Debug|Win32.ActiveCfg = Debug|Win32
+		{05511B4A-FB13-49E5-AE53-EE1F063C055D}.Debug|Win32.Build.0 = Debug|Win32
+		{05511B4A-FB13-49E5-AE53-EE1F063C055D}.Release|Win32.ActiveCfg = Release|Win32
+		{05511B4A-FB13-49E5-AE53-EE1F063C055D}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
--