Quake!

This ports id Software's Quake to run on the SDL plugin runtime. The
source code originated from id under the GPLv2 license. I used
https://github.com/ahefner/sdlquake as the base of my port.

Performance is, unsurprisingly, not on par with what you're probably
used to on PC. I average about 10FPS on ipod6g, but it's still
playable.

Sound works well enough, but in-game music is not supported. I've
written ARM assembly routines for the inner sound loop. Make sure you
turn the "brightness" all the way down, or colors will look funky.

To run, extract Quake's data files to /.rockbox/quake. Have fun!

Change-Id: I4285036e967d7f0722802d43cf2096c808ca5799
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index a0527c8..ad99f39 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -80,6 +80,7 @@
 pong,games
 ppm,viewers
 properties,viewers
+quake,games
 random_folder_advance_config,apps
 remote_control,apps
 resistor,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 0ff6eb9..c3e56af 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -78,6 +78,7 @@
     (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
 #if (PLUGIN_BUFFER_SIZE > 0x14000) && defined(CPU_ARM)
 duke3d.c
+quake.c
 wolf3d.c
 #endif
 #endif
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 04c3d76..93cf829 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -16,11 +16,11 @@
     (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
 xworld
 
-/* for duke, etc. */
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && (PLUGIN_BUFFER_SIZE > 0x14000) && \
-    defined(CPU_ARM)
+/* for duke3d, wolf3d and quake */
+#if (PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM)
 sdl
 #endif
+
 puzzles
 #endif
 
diff --git a/apps/plugins/lib/stdio_compat.h b/apps/plugins/lib/stdio_compat.h
index b9056fa..bce21ea 100644
--- a/apps/plugins/lib/stdio_compat.h
+++ b/apps/plugins/lib/stdio_compat.h
@@ -50,6 +50,8 @@
 #undef stderr
 #define stderr _stderr_
 
+#define getc fgetc
+
 typedef struct {
     int fd;
     int unget_char;
diff --git a/apps/plugins/quake.c b/apps/plugins/quake.c
new file mode 100644
index 0000000..d3abd43
--- /dev/null
+++ b/apps/plugins/quake.c
@@ -0,0 +1,31 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2017 Franklin Wei
+ *
+ * Duke3D stub loader.
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+
+#include "lib/overlay.h"
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(const void* parameter)
+{
+    return run_overlay(parameter, PLUGIN_GAMES_DIR "/quake.ovl", "Quake!");
+}
diff --git a/apps/plugins/sdl/SOURCES.quake b/apps/plugins/sdl/SOURCES.quake
new file mode 100644
index 0000000..10a53a3
--- /dev/null
+++ b/apps/plugins/sdl/SOURCES.quake
@@ -0,0 +1,77 @@
+progs/quake/cd_null.c
+progs/quake/chase.c
+progs/quake/cl_demo.c
+progs/quake/cl_input.c
+progs/quake/cl_main.c
+progs/quake/cl_parse.c
+progs/quake/cl_tent.c
+progs/quake/cmd.c
+progs/quake/common.c
+progs/quake/console.c
+progs/quake/crc.c
+progs/quake/cvar.c
+progs/quake/d_edge.c
+progs/quake/d_fill.c
+progs/quake/d_init.c
+progs/quake/d_modech.c
+progs/quake/d_part.c
+progs/quake/d_polyse.c
+progs/quake/draw.c
+progs/quake/d_scan.c
+progs/quake/d_sky.c
+progs/quake/d_sprite.c
+progs/quake/d_surf.c
+progs/quake/d_vars.c
+progs/quake/d_zpoint.c
+progs/quake/host.c
+progs/quake/host_cmd.c
+progs/quake/keys.c
+progs/quake/mathlib.c
+progs/quake/menu.c
+progs/quake/model.c
+progs/quake/net_loop.c
+progs/quake/net_main.c
+progs/quake/net_none.c
+progs/quake/net_vcr.c
+progs/quake/nonintel.c
+progs/quake/pr_cmds.c
+progs/quake/pr_edict.c
+progs/quake/pr_exec.c
+progs/quake/r_aclip.c
+progs/quake/r_alias.c
+progs/quake/r_bsp.c
+progs/quake/r_draw.c
+progs/quake/r_edge.c
+progs/quake/r_efrag.c
+progs/quake/r_light.c
+progs/quake/r_main.c
+progs/quake/r_misc.c
+progs/quake/r_part.c
+progs/quake/r_sky.c
+progs/quake/r_sprite.c
+progs/quake/r_surf.c
+progs/quake/r_vars.c
+progs/quake/sbar.c
+progs/quake/screen.c
+progs/quake/snd_dma.c
+progs/quake/snd_mem.c
+progs/quake/snd_mix.c
+
+#ifdef __ARM_ARCH__5TEJ__
+progs/quake/snd_mix_arm.S
+#endif
+
+/* we still need generic no matter what */
+progs/quake/snd_mix_generic.c
+
+progs/quake/snd_sdl.c
+progs/quake/sv_main.c
+progs/quake/sv_move.c
+progs/quake/sv_phys.c
+progs/quake/sv_user.c
+progs/quake/sys_sdl.c
+progs/quake/vid_sdl.c
+progs/quake/view.c
+progs/quake/wad.c
+progs/quake/world.c
+progs/quake/zone.c
diff --git a/apps/plugins/sdl/include/SDL_config_rockbox.h b/apps/plugins/sdl/include/SDL_config_rockbox.h
index e268a73..2b7f9a6 100644
--- a/apps/plugins/sdl/include/SDL_config_rockbox.h
+++ b/apps/plugins/sdl/include/SDL_config_rockbox.h
@@ -38,7 +38,8 @@
 
 //#define COMBINED_SDL
 
-/* games will use this sample rate */
+/* "recommended" sample rate for Rockbox. Games should use this by
+ * default unless necessary to do otherwise. */
 #ifdef SIMULATOR
 #define RB_SAMPR SAMPR_44
 #else
@@ -57,6 +58,7 @@
 /* woot */
 #define SDL_AUDIO_DRIVER_ROCKBOX 1
 #define SDL_THREAD_ROCKBOX       1
+#undef  SDL_THREAD_PTHREAD
 #define SDL_TIMER_ROCKBOX        1
 #define SDL_VIDEO_DRIVER_ROCKBOX 1
 
@@ -78,6 +80,10 @@
 #define HAVE_FREE         1
 #define HAVE_REALLOC      1
 #define HAVE_QSORT        1
+#define HAVE_STRLEN       1
+#define HAVE_STRLCPY      1
+#define HAVE_STRCMP       1
+#define HAVE_STRNCMP      1
 
 #undef strdup
 
@@ -116,6 +122,7 @@
 #define floor floor_wrapper
 #define fmod fmod_wrapper
 #define free tlsf_free
+#define fscanf fscanf_wrapper
 #define getchar() rb->sleep(2*HZ)
 #define getenv SDL_getenv
 #define log rb_log
@@ -146,6 +153,7 @@
 #define strcpy strcpy_wrapper
 #define strdup strdup_wrapper
 #define strerror(x) "error"
+#define strlcpy rb->strlcpy
 #define strlen rb->strlen
 #define strncasecmp rb->strncasecmp
 #define strncat rb->strlcat /* hack */
@@ -163,8 +171,12 @@
 #define vsnprintf rb->vsnprintf
 #define vsprintf vsprintf_wrapper
 
+// use Rockbox's string routines
+#define SDL_memcpy memcpy
+#define SDL_memcmp memcmp
+
 #define M_PI 3.14159265358979323846
-#define EOF 0xff
+#define EOF (-1)
 
 #define LOAD_XPM
 #define MID_MUSIC
@@ -194,6 +206,7 @@
 void fatal(char *fmt, ...);
 void rb_exit(int rc);
 void rbsdl_atexit(void (*)(void));
+float atof_wrapper (char *str);
 
 /* speed */
 static inline uint16_t readLE16(void *addr)
diff --git a/apps/plugins/sdl/include/SDL_platform.h b/apps/plugins/sdl/include/SDL_platform.h
index 22c3201..40df082 100644
--- a/apps/plugins/sdl/include/SDL_platform.h
+++ b/apps/plugins/sdl/include/SDL_platform.h
@@ -108,6 +108,7 @@
 #endif
 #if defined(ROCKBOX)
 #undef __ROCKBOX__
+#undef __LINUX__ /* maybe sim */
 #define __ROCKBOX__ 1
 #endif
 
diff --git a/apps/plugins/sdl/main.c b/apps/plugins/sdl/main.c
index 478debc..cefc156 100644
--- a/apps/plugins/sdl/main.c
+++ b/apps/plugins/sdl/main.c
@@ -72,7 +72,7 @@
 }
 
 /* 256KB */
-static long main_stack[1024 * 1024 / 4];
+static long main_stack[1024 * 1024 / 2];
 int (*main_fn)(int argc, char *argv[]);
 int prog_idx;
 static void main_thread(void)
@@ -176,7 +176,7 @@
 #if defined(CPU_ARM) && !defined(SIMULATOR)
     /* (don't) set alignment trap. Will generate a data abort
      * exception on ARM. */
-    //set_cr(get_cr() | CR_A);
+    set_cr(get_cr() | CR_A);
 #endif
 
 #if 0
@@ -222,8 +222,11 @@
 #undef rb_atexit
     rb_atexit(cleanup);
 
-    /* make a new thread to use a bigger stack than otherwise accessible */
-    sdl_thread_id = rb->create_thread(main_thread, main_stack, sizeof(main_stack), 0, "sdl_main" IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
+    /* make a new thread to use a bigger stack and higher priority than otherwise accessible */
+    sdl_thread_id = rb->create_thread(main_thread, main_stack,
+                                      sizeof(main_stack), 0, "sdl_main"
+                                      IF_PRIO(, PRIORITY_USER_INTERFACE) // we need other threads still
+                                      IF_COP(, CPU));
     rb->thread_wait(sdl_thread_id);
 
     rb->sleep(HZ); /* wait a second in case there's an error message on screen */
diff --git a/apps/plugins/sdl/progs/quake/Makefile.linuxi386 b/apps/plugins/sdl/progs/quake/Makefile.linuxi386
new file mode 100644
index 0000000..e0750d1
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/Makefile.linuxi386
@@ -0,0 +1,1242 @@
+#
+# Quake Makefile for Linux 2.0
+#
+# Aug '98 by Zoid <zoid@idsoftware.com>
+#
+# ELF only
+#
+
+BASEVERSION=1.09
+VERSION=$(BASEVERSION)$(GLIBC)
+
+# RPM release number
+RPM_RELEASE=5
+
+ifneq (,$(findstring libc6,$(shell if [ -e /lib/libc.so.6 ];then echo libc6;fi)))
+GLIBC=-glibc
+else
+GLIBC=
+endif
+
+ifneq (,$(findstring alpha,$(shell uname -m)))
+ARCH=axp
+else
+ARCH=i386
+endif
+NOARCH=noarch
+
+#MOUNT_DIR=/grog/Projects/WinQuake
+MOUNT_DIR := $(shell pwd)
+MASTER_DIR=/grog/Projects/QuakeMaster
+MESA_DIR=/usr/local/src/Mesa-2.6
+TDFXGL_DIR = /home/zoid/3dfxgl
+
+BUILD_DEBUG_DIR=debug$(ARCH)$(GLIBC)
+BUILD_RELEASE_DIR=release$(ARCH)$(GLIBC)
+
+#EGCS=/usr/local/egcs-1.1.2/bin/gcc
+#CC=$(EGCS)
+
+BASE_CFLAGS=-Dstricmp=strcasecmp
+RELEASE_CFLAGS=$(BASE_CFLAGS) -g -mpentiumpro -O6 -ffast-math -funroll-loops \
+	-fomit-frame-pointer -fexpensive-optimizations
+DEBUG_CFLAGS=$(BASE_CFLAGS) -g
+LDFLAGS=-lm
+SVGALDFLAGS=-lvga
+XLDFLAGS=-L/usr/X11R6/lib -lX11 -lXext -lXxf86dga
+XCFLAGS=-DX11
+
+MESAGLLDFLAGS=-L/usr/X11/lib -L/usr/local/lib -L$(MESA_DIR)/lib -lMesaGL -lglide2x -lX11 -lXext -ldl
+TDFXGLLDFLAGS=-L$(TDFXGL_DIR)/release$(ARCH)$(GLIBC) -l3dfxgl -lglide2x -ldl
+GLLDFLAGS=-L/usr/X11/lib -L/usr/local/lib -lGL -lX11 -lXext -ldl -lXxf86dga -lXxf86vm -lm
+GLCFLAGS=-DGLQUAKE -I$(MESA_DIR)/include -I/usr/include/glide
+
+DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
+DO_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) -o $@ -c $<
+DO_GL_CC=$(CC) $(CFLAGS) $(GLCFLAGS) -o $@ -c $<
+DO_GL_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) $(GLCFLAGS) -o $@ -c $<
+DO_X11_CC=$(CC) $(CFLAGS) $(XCFLAGS) -o $@ -c $<
+DO_X11_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) $(XCFLAGS) -o $@ -c $<
+DO_O_CC=$(CC) -O $(CFLAGS) -o $@ -c $<
+#DO_AS=$(CC) $(CFLAGS) -DELF -x assembler-with-cpp -o $@ -c $<
+DO_AS=$(CC) $(CFLAGS) -DELF -o $@ -c $<
+DO_GL_AS=$(CC) $(CFLAGS) $(GLCFLAGS) -DELF -x assembler-with-cpp -o $@ -c $<
+
+#############################################################################
+# SETUP AND BUILD
+#############################################################################
+
+TARGETS=$(BUILDDIR)/bin/quake.x11\
+	$(BUILDDIR)/bin/squake \
+	$(BUILDDIR)/bin/glquake \
+	$(BUILDDIR)/bin/glquake.glx \
+	$(BUILDDIR)/bin/glquake.3dfxgl \
+	# $(BUILDDIR)/bin/unixded
+
+build_debug:
+	@-mkdir $(BUILD_DEBUG_DIR) \
+		$(BUILD_DEBUG_DIR)/bin \
+		$(BUILD_DEBUG_DIR)/glquake \
+		$(BUILD_DEBUG_DIR)/squake \
+		$(BUILD_DEBUG_DIR)/unixded \
+		$(BUILD_DEBUG_DIR)/x11
+	$(MAKE) targets BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)"
+
+build_release:
+	@-mkdir $(BUILD_RELEASE_DIR) \
+		$(BUILD_RELEASE_DIR)/bin \
+		$(BUILD_RELEASE_DIR)/glquake \
+		$(BUILD_RELEASE_DIR)/squake \
+		$(BUILD_RELEASE_DIR)/unixded \
+		$(BUILD_RELEASE_DIR)/x11
+	$(MAKE) targets BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)"
+
+all: build_debug build_release
+
+targets: $(TARGETS)
+
+#############################################################################
+# SVGALIB Quake
+#############################################################################
+
+SQUAKE_OBJS = \
+	$(BUILDDIR)/squake/cl_demo.o \
+	$(BUILDDIR)/squake/cl_input.o \
+	$(BUILDDIR)/squake/cl_main.o \
+	$(BUILDDIR)/squake/cl_parse.o \
+	$(BUILDDIR)/squake/cl_tent.o \
+	$(BUILDDIR)/squake/chase.o \
+	$(BUILDDIR)/squake/cmd.o \
+	$(BUILDDIR)/squake/common.o \
+	$(BUILDDIR)/squake/console.o \
+	$(BUILDDIR)/squake/crc.o \
+	$(BUILDDIR)/squake/cvar.o \
+	$(BUILDDIR)/squake/draw.o \
+	$(BUILDDIR)/squake/d_edge.o \
+	$(BUILDDIR)/squake/d_fill.o \
+	$(BUILDDIR)/squake/d_init.o \
+	$(BUILDDIR)/squake/d_modech.o \
+	$(BUILDDIR)/squake/d_part.o \
+	$(BUILDDIR)/squake/d_polyse.o \
+	$(BUILDDIR)/squake/d_scan.o \
+	$(BUILDDIR)/squake/d_sky.o \
+	$(BUILDDIR)/squake/d_sprite.o \
+	$(BUILDDIR)/squake/d_surf.o \
+	$(BUILDDIR)/squake/d_vars.o \
+	$(BUILDDIR)/squake/d_zpoint.o \
+	$(BUILDDIR)/squake/host.o \
+	$(BUILDDIR)/squake/host_cmd.o \
+	$(BUILDDIR)/squake/keys.o \
+	$(BUILDDIR)/squake/menu.o \
+	$(BUILDDIR)/squake/mathlib.o \
+	$(BUILDDIR)/squake/model.o \
+	$(BUILDDIR)/squake/net_dgrm.o \
+	$(BUILDDIR)/squake/net_loop.o \
+	$(BUILDDIR)/squake/net_main.o \
+	$(BUILDDIR)/squake/net_vcr.o \
+	$(BUILDDIR)/squake/net_udp.o \
+	$(BUILDDIR)/squake/net_bsd.o \
+	$(BUILDDIR)/squake/nonintel.o \
+	$(BUILDDIR)/squake/pr_cmds.o \
+	$(BUILDDIR)/squake/pr_edict.o \
+	$(BUILDDIR)/squake/pr_exec.o \
+	$(BUILDDIR)/squake/r_aclip.o \
+	$(BUILDDIR)/squake/r_alias.o \
+	$(BUILDDIR)/squake/r_bsp.o \
+	$(BUILDDIR)/squake/r_light.o \
+	$(BUILDDIR)/squake/r_draw.o \
+	$(BUILDDIR)/squake/r_efrag.o \
+	$(BUILDDIR)/squake/r_edge.o \
+	$(BUILDDIR)/squake/r_misc.o \
+	$(BUILDDIR)/squake/r_main.o \
+	$(BUILDDIR)/squake/r_sky.o \
+	$(BUILDDIR)/squake/r_sprite.o \
+	$(BUILDDIR)/squake/r_surf.o \
+	$(BUILDDIR)/squake/r_part.o \
+	$(BUILDDIR)/squake/r_vars.o \
+	$(BUILDDIR)/squake/screen.o \
+	$(BUILDDIR)/squake/sbar.o \
+	$(BUILDDIR)/squake/sv_main.o \
+	$(BUILDDIR)/squake/sv_phys.o \
+	$(BUILDDIR)/squake/sv_move.o \
+	$(BUILDDIR)/squake/sv_user.o \
+	$(BUILDDIR)/squake/zone.o	\
+	$(BUILDDIR)/squake/view.o	\
+	$(BUILDDIR)/squake/wad.o \
+	$(BUILDDIR)/squake/world.o \
+	$(BUILDDIR)/squake/cd_linux.o \
+	$(BUILDDIR)/squake/sys_linux.o \
+	$(BUILDDIR)/squake/vid_svgalib.o \
+	$(BUILDDIR)/squake/snd_dma.o \
+	$(BUILDDIR)/squake/snd_mem.o \
+	$(BUILDDIR)/squake/snd_mix.o \
+	$(BUILDDIR)/squake/snd_linux.o \
+	\
+	$(BUILDDIR)/squake/d_copy.o \
+	$(BUILDDIR)/squake/d_draw.o \
+	$(BUILDDIR)/squake/d_draw16.o \
+	$(BUILDDIR)/squake/d_parta.o \
+	$(BUILDDIR)/squake/d_polysa.o \
+	$(BUILDDIR)/squake/d_scana.o \
+	$(BUILDDIR)/squake/d_spr8.o \
+	$(BUILDDIR)/squake/d_varsa.o \
+	$(BUILDDIR)/squake/math.o \
+	$(BUILDDIR)/squake/r_aliasa.o \
+	$(BUILDDIR)/squake/r_drawa.o \
+	$(BUILDDIR)/squake/r_edgea.o \
+	$(BUILDDIR)/squake/r_varsa.o \
+	$(BUILDDIR)/squake/surf16.o \
+	$(BUILDDIR)/squake/surf8.o \
+	$(BUILDDIR)/squake/worlda.o \
+	$(BUILDDIR)/squake/r_aclipa.o \
+	$(BUILDDIR)/squake/snd_mixa.o \
+	$(BUILDDIR)/squake/sys_dosa.o
+
+$(BUILDDIR)/bin/squake : $(SQUAKE_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(SQUAKE_OBJS) $(SVGALDFLAGS) $(LDFLAGS)
+
+####
+
+$(BUILDDIR)/squake/cl_demo.o :  $(MOUNT_DIR)/cl_demo.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cl_input.o : $(MOUNT_DIR)/cl_input.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cl_main.o :  $(MOUNT_DIR)/cl_main.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cl_parse.o : $(MOUNT_DIR)/cl_parse.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cl_tent.o :  $(MOUNT_DIR)/cl_tent.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/chase.o :    $(MOUNT_DIR)/chase.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cmd.o :      $(MOUNT_DIR)/cmd.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/common.o :   $(MOUNT_DIR)/common.c
+	$(DO_DEBUG_CC)
+
+$(BUILDDIR)/squake/console.o :  $(MOUNT_DIR)/console.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/crc.o :      $(MOUNT_DIR)/crc.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cvar.o :     $(MOUNT_DIR)/cvar.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/draw.o :     $(MOUNT_DIR)/draw.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_edge.o :   $(MOUNT_DIR)/d_edge.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_fill.o :   $(MOUNT_DIR)/d_fill.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_init.o :   $(MOUNT_DIR)/d_init.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_modech.o : $(MOUNT_DIR)/d_modech.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_part.o :   $(MOUNT_DIR)/d_part.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_polyse.o : $(MOUNT_DIR)/d_polyse.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_scan.o :   $(MOUNT_DIR)/d_scan.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_sky.o :    $(MOUNT_DIR)/d_sky.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_sprite.o : $(MOUNT_DIR)/d_sprite.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_surf.o :   $(MOUNT_DIR)/d_surf.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_vars.o :   $(MOUNT_DIR)/d_vars.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/d_zpoint.o : $(MOUNT_DIR)/d_zpoint.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/host.o :     $(MOUNT_DIR)/host.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/host_cmd.o : $(MOUNT_DIR)/host_cmd.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/keys.o :     $(MOUNT_DIR)/keys.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/menu.o :     $(MOUNT_DIR)/menu.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/mathlib.o :  $(MOUNT_DIR)/mathlib.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/model.o :    $(MOUNT_DIR)/model.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_dgrm.o : $(MOUNT_DIR)/net_dgrm.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_loop.o : $(MOUNT_DIR)/net_loop.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_main.o : $(MOUNT_DIR)/net_main.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_vcr.o :  $(MOUNT_DIR)/net_vcr.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_udp.o :  $(MOUNT_DIR)/net_udp.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/net_bsd.o :  $(MOUNT_DIR)/net_bsd.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/nonintel.o : $(MOUNT_DIR)/nonintel.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/pr_cmds.o :  $(MOUNT_DIR)/pr_cmds.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/pr_edict.o : $(MOUNT_DIR)/pr_edict.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/pr_exec.o :  $(MOUNT_DIR)/pr_exec.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_aclip.o :  $(MOUNT_DIR)/r_aclip.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_alias.o :  $(MOUNT_DIR)/r_alias.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_bsp.o :    $(MOUNT_DIR)/r_bsp.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_light.o :  $(MOUNT_DIR)/r_light.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_draw.o :   $(MOUNT_DIR)/r_draw.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_efrag.o :  $(MOUNT_DIR)/r_efrag.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_edge.o :   $(MOUNT_DIR)/r_edge.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_misc.o :   $(MOUNT_DIR)/r_misc.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_main.o :   $(MOUNT_DIR)/r_main.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_sky.o :    $(MOUNT_DIR)/r_sky.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_sprite.o : $(MOUNT_DIR)/r_sprite.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_surf.o :   $(MOUNT_DIR)/r_surf.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_part.o :   $(MOUNT_DIR)/r_part.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/r_vars.o :   $(MOUNT_DIR)/r_vars.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/screen.o :   $(MOUNT_DIR)/screen.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sbar.o :     $(MOUNT_DIR)/sbar.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sv_main.o :  $(MOUNT_DIR)/sv_main.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sv_phys.o :  $(MOUNT_DIR)/sv_phys.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sv_move.o :  $(MOUNT_DIR)/sv_move.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sv_user.o :  $(MOUNT_DIR)/sv_user.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/zone.o	:   $(MOUNT_DIR)/zone.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/view.o	:   $(MOUNT_DIR)/view.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/wad.o :      $(MOUNT_DIR)/wad.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/world.o :    $(MOUNT_DIR)/world.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/cd_linux.o : $(MOUNT_DIR)/cd_linux.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/sys_linux.o :$(MOUNT_DIR)/sys_linux.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/vid_svgalib.o:$(MOUNT_DIR)/vid_svgalib.c
+	$(DO_O_CC)
+
+$(BUILDDIR)/squake/snd_dma.o :  $(MOUNT_DIR)/snd_dma.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/snd_mem.o :  $(MOUNT_DIR)/snd_mem.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/snd_mix.o :  $(MOUNT_DIR)/snd_mix.c
+	$(DO_CC)
+
+$(BUILDDIR)/squake/snd_linux.o :$(MOUNT_DIR)/snd_linux.c
+	$(DO_CC)
+
+#####
+
+$(BUILDDIR)/squake/d_copy.o :   $(MOUNT_DIR)/d_copy.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_draw.o :   $(MOUNT_DIR)/d_draw.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_draw16.o : $(MOUNT_DIR)/d_draw16.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_parta.o :  $(MOUNT_DIR)/d_parta.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_polysa.o : $(MOUNT_DIR)/d_polysa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_scana.o :  $(MOUNT_DIR)/d_scana.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_spr8.o :   $(MOUNT_DIR)/d_spr8.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/d_varsa.o :  $(MOUNT_DIR)/d_varsa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/math.o :     $(MOUNT_DIR)/math.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/r_aliasa.o : $(MOUNT_DIR)/r_aliasa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/r_drawa.o :  $(MOUNT_DIR)/r_drawa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/r_edgea.o :  $(MOUNT_DIR)/r_edgea.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/r_varsa.o :  $(MOUNT_DIR)/r_varsa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/surf16.o :   $(MOUNT_DIR)/surf16.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/surf8.o :    $(MOUNT_DIR)/surf8.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/worlda.o :   $(MOUNT_DIR)/worlda.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/r_aclipa.o : $(MOUNT_DIR)/r_aclipa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/snd_mixa.o : $(MOUNT_DIR)/snd_mixa.S
+	$(DO_AS)
+
+$(BUILDDIR)/squake/sys_dosa.o : $(MOUNT_DIR)/sys_dosa.S
+	$(DO_AS)
+
+#############################################################################
+# X11 Quake
+#############################################################################
+
+X11_OBJS = \
+	$(BUILDDIR)/x11/cl_demo.o \
+	$(BUILDDIR)/x11/cl_input.o \
+	$(BUILDDIR)/x11/cl_main.o \
+	$(BUILDDIR)/x11/cl_parse.o \
+	$(BUILDDIR)/x11/cl_tent.o \
+	$(BUILDDIR)/x11/chase.o \
+	$(BUILDDIR)/x11/cmd.o \
+	$(BUILDDIR)/x11/common.o \
+	$(BUILDDIR)/x11/console.o \
+	$(BUILDDIR)/x11/crc.o \
+	$(BUILDDIR)/x11/cvar.o \
+	$(BUILDDIR)/x11/draw.o \
+	$(BUILDDIR)/x11/d_edge.o \
+	$(BUILDDIR)/x11/d_fill.o \
+	$(BUILDDIR)/x11/d_init.o \
+	$(BUILDDIR)/x11/d_modech.o \
+	$(BUILDDIR)/x11/d_part.o \
+	$(BUILDDIR)/x11/d_polyse.o \
+	$(BUILDDIR)/x11/d_scan.o \
+	$(BUILDDIR)/x11/d_sky.o \
+	$(BUILDDIR)/x11/d_sprite.o \
+	$(BUILDDIR)/x11/d_surf.o \
+	$(BUILDDIR)/x11/d_vars.o \
+	$(BUILDDIR)/x11/d_zpoint.o \
+	$(BUILDDIR)/x11/host.o \
+	$(BUILDDIR)/x11/host_cmd.o \
+	$(BUILDDIR)/x11/keys.o \
+	$(BUILDDIR)/x11/menu.o \
+	$(BUILDDIR)/x11/mathlib.o \
+	$(BUILDDIR)/x11/model.o \
+	$(BUILDDIR)/x11/net_dgrm.o \
+	$(BUILDDIR)/x11/net_loop.o \
+	$(BUILDDIR)/x11/net_main.o \
+	$(BUILDDIR)/x11/net_vcr.o \
+	$(BUILDDIR)/x11/net_udp.o \
+	$(BUILDDIR)/x11/net_bsd.o \
+	$(BUILDDIR)/x11/nonintel.o \
+	$(BUILDDIR)/x11/pr_cmds.o \
+	$(BUILDDIR)/x11/pr_edict.o \
+	$(BUILDDIR)/x11/pr_exec.o \
+	$(BUILDDIR)/x11/r_aclip.o \
+	$(BUILDDIR)/x11/r_alias.o \
+	$(BUILDDIR)/x11/r_bsp.o \
+	$(BUILDDIR)/x11/r_light.o \
+	$(BUILDDIR)/x11/r_draw.o \
+	$(BUILDDIR)/x11/r_efrag.o \
+	$(BUILDDIR)/x11/r_edge.o \
+	$(BUILDDIR)/x11/r_misc.o \
+	$(BUILDDIR)/x11/r_main.o \
+	$(BUILDDIR)/x11/r_sky.o \
+	$(BUILDDIR)/x11/r_sprite.o \
+	$(BUILDDIR)/x11/r_surf.o \
+	$(BUILDDIR)/x11/r_part.o \
+	$(BUILDDIR)/x11/r_vars.o \
+	$(BUILDDIR)/x11/screen.o \
+	$(BUILDDIR)/x11/sbar.o \
+	$(BUILDDIR)/x11/sv_main.o \
+	$(BUILDDIR)/x11/sv_phys.o \
+	$(BUILDDIR)/x11/sv_move.o \
+	$(BUILDDIR)/x11/sv_user.o \
+	$(BUILDDIR)/x11/zone.o	\
+	$(BUILDDIR)/x11/view.o	\
+	$(BUILDDIR)/x11/wad.o \
+	$(BUILDDIR)/x11/world.o \
+	$(BUILDDIR)/x11/cd_linux.o \
+	$(BUILDDIR)/x11/sys_linux.o \
+	$(BUILDDIR)/x11/vid_x.o \
+	$(BUILDDIR)/x11/snd_dma.o \
+	$(BUILDDIR)/x11/snd_mem.o \
+	$(BUILDDIR)/x11/snd_mix.o \
+	$(BUILDDIR)/x11/snd_linux.o \
+	\
+	$(BUILDDIR)/x11/d_draw.o \
+	$(BUILDDIR)/x11/d_draw16.o \
+	$(BUILDDIR)/x11/d_parta.o \
+	$(BUILDDIR)/x11/d_polysa.o \
+	$(BUILDDIR)/x11/d_scana.o \
+	$(BUILDDIR)/x11/d_spr8.o \
+	$(BUILDDIR)/x11/d_varsa.o \
+	$(BUILDDIR)/x11/math.o \
+	$(BUILDDIR)/x11/r_aliasa.o \
+	$(BUILDDIR)/x11/r_drawa.o \
+	$(BUILDDIR)/x11/r_edgea.o \
+	$(BUILDDIR)/x11/r_varsa.o \
+	$(BUILDDIR)/x11/surf16.o \
+	$(BUILDDIR)/x11/surf8.o \
+	$(BUILDDIR)/x11/worlda.o \
+	$(BUILDDIR)/x11/r_aclipa.o \
+	$(BUILDDIR)/x11/snd_mixa.o \
+	$(BUILDDIR)/x11/sys_dosa.o
+
+$(BUILDDIR)/bin/quake.x11 : $(X11_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(X11_OBJS) $(XLDFLAGS) $(LDFLAGS)
+
+####
+
+$(BUILDDIR)/x11/cl_demo.o :  $(MOUNT_DIR)/cl_demo.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cl_input.o : $(MOUNT_DIR)/cl_input.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cl_main.o :  $(MOUNT_DIR)/cl_main.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cl_parse.o : $(MOUNT_DIR)/cl_parse.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cl_tent.o :  $(MOUNT_DIR)/cl_tent.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/chase.o :    $(MOUNT_DIR)/chase.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cmd.o :      $(MOUNT_DIR)/cmd.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/common.o :   $(MOUNT_DIR)/common.c
+	$(DO_X11_DEBUG_CC)
+
+$(BUILDDIR)/x11/console.o :  $(MOUNT_DIR)/console.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/crc.o :      $(MOUNT_DIR)/crc.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cvar.o :     $(MOUNT_DIR)/cvar.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/draw.o :     $(MOUNT_DIR)/draw.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_edge.o :   $(MOUNT_DIR)/d_edge.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_fill.o :   $(MOUNT_DIR)/d_fill.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_init.o :   $(MOUNT_DIR)/d_init.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_modech.o : $(MOUNT_DIR)/d_modech.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_part.o :   $(MOUNT_DIR)/d_part.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_polyse.o : $(MOUNT_DIR)/d_polyse.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_scan.o :   $(MOUNT_DIR)/d_scan.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_sky.o :    $(MOUNT_DIR)/d_sky.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_sprite.o : $(MOUNT_DIR)/d_sprite.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_surf.o :   $(MOUNT_DIR)/d_surf.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_vars.o :   $(MOUNT_DIR)/d_vars.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/d_zpoint.o : $(MOUNT_DIR)/d_zpoint.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/host.o :     $(MOUNT_DIR)/host.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/host_cmd.o : $(MOUNT_DIR)/host_cmd.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/keys.o :     $(MOUNT_DIR)/keys.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/menu.o :     $(MOUNT_DIR)/menu.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/mathlib.o :  $(MOUNT_DIR)/mathlib.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/model.o :    $(MOUNT_DIR)/model.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_dgrm.o : $(MOUNT_DIR)/net_dgrm.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_loop.o : $(MOUNT_DIR)/net_loop.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_main.o : $(MOUNT_DIR)/net_main.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_vcr.o :  $(MOUNT_DIR)/net_vcr.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_udp.o :  $(MOUNT_DIR)/net_udp.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/net_bsd.o :  $(MOUNT_DIR)/net_bsd.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/nonintel.o : $(MOUNT_DIR)/nonintel.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/pr_cmds.o :  $(MOUNT_DIR)/pr_cmds.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/pr_edict.o : $(MOUNT_DIR)/pr_edict.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/pr_exec.o :  $(MOUNT_DIR)/pr_exec.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_aclip.o :  $(MOUNT_DIR)/r_aclip.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_alias.o :  $(MOUNT_DIR)/r_alias.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_bsp.o :    $(MOUNT_DIR)/r_bsp.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_light.o :  $(MOUNT_DIR)/r_light.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_draw.o :   $(MOUNT_DIR)/r_draw.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_efrag.o :  $(MOUNT_DIR)/r_efrag.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_edge.o :   $(MOUNT_DIR)/r_edge.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_misc.o :   $(MOUNT_DIR)/r_misc.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_main.o :   $(MOUNT_DIR)/r_main.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_sky.o :    $(MOUNT_DIR)/r_sky.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_sprite.o : $(MOUNT_DIR)/r_sprite.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_surf.o :   $(MOUNT_DIR)/r_surf.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_part.o :   $(MOUNT_DIR)/r_part.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/r_vars.o :   $(MOUNT_DIR)/r_vars.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/screen.o :   $(MOUNT_DIR)/screen.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sbar.o :     $(MOUNT_DIR)/sbar.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sv_main.o :  $(MOUNT_DIR)/sv_main.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sv_phys.o :  $(MOUNT_DIR)/sv_phys.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sv_move.o :  $(MOUNT_DIR)/sv_move.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sv_user.o :  $(MOUNT_DIR)/sv_user.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/zone.o	:   $(MOUNT_DIR)/zone.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/view.o	:   $(MOUNT_DIR)/view.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/wad.o :      $(MOUNT_DIR)/wad.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/world.o :    $(MOUNT_DIR)/world.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/cd_linux.o : $(MOUNT_DIR)/cd_linux.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/sys_linux.o :$(MOUNT_DIR)/sys_linux.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/vid_x.o: $(MOUNT_DIR)/vid_x.c
+	$(DO_O_CC)
+
+$(BUILDDIR)/x11/snd_dma.o :  $(MOUNT_DIR)/snd_dma.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/snd_mem.o :  $(MOUNT_DIR)/snd_mem.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/snd_mix.o :  $(MOUNT_DIR)/snd_mix.c
+	$(DO_X11_CC)
+
+$(BUILDDIR)/x11/snd_linux.o :$(MOUNT_DIR)/snd_linux.c
+	$(DO_X11_CC)
+
+#####
+
+$(BUILDDIR)/x11/d_copy.o :   $(MOUNT_DIR)/d_copy.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_draw.o :   $(MOUNT_DIR)/d_draw.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_draw16.o : $(MOUNT_DIR)/d_draw16.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_parta.o :  $(MOUNT_DIR)/d_parta.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_polysa.o : $(MOUNT_DIR)/d_polysa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_scana.o :  $(MOUNT_DIR)/d_scana.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_spr8.o :   $(MOUNT_DIR)/d_spr8.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/d_varsa.o :  $(MOUNT_DIR)/d_varsa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/math.o :     $(MOUNT_DIR)/math.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/r_aliasa.o : $(MOUNT_DIR)/r_aliasa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/r_drawa.o :  $(MOUNT_DIR)/r_drawa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/r_edgea.o :  $(MOUNT_DIR)/r_edgea.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/r_varsa.o :  $(MOUNT_DIR)/r_varsa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/surf16.o :   $(MOUNT_DIR)/surf16.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/surf8.o :    $(MOUNT_DIR)/surf8.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/worlda.o :   $(MOUNT_DIR)/worlda.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/r_aclipa.o : $(MOUNT_DIR)/r_aclipa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/snd_mixa.o : $(MOUNT_DIR)/snd_mixa.S
+	$(DO_AS)
+
+$(BUILDDIR)/x11/sys_dosa.o : $(MOUNT_DIR)/sys_dosa.S
+	$(DO_AS)
+
+#############################################################################
+# GLQuake
+#############################################################################
+
+GLQUAKE_OBJS= \
+	$(BUILDDIR)/glquake/cl_demo.o \
+	$(BUILDDIR)/glquake/cl_input.o \
+	$(BUILDDIR)/glquake/cl_main.o \
+	$(BUILDDIR)/glquake/cl_parse.o \
+	$(BUILDDIR)/glquake/cl_tent.o \
+	$(BUILDDIR)/glquake/chase.o \
+	$(BUILDDIR)/glquake/cmd.o \
+	$(BUILDDIR)/glquake/common.o \
+	$(BUILDDIR)/glquake/console.o \
+	$(BUILDDIR)/glquake/crc.o \
+	$(BUILDDIR)/glquake/cvar.o \
+	\
+	$(BUILDDIR)/glquake/gl_draw.o \
+	$(BUILDDIR)/glquake/gl_mesh.o \
+	$(BUILDDIR)/glquake/gl_model.o \
+	$(BUILDDIR)/glquake/gl_refrag.o \
+	$(BUILDDIR)/glquake/gl_rlight.o \
+	$(BUILDDIR)/glquake/gl_rmain.o \
+	$(BUILDDIR)/glquake/gl_rmisc.o \
+	$(BUILDDIR)/glquake/gl_rsurf.o \
+	$(BUILDDIR)/glquake/gl_screen.o \
+	$(BUILDDIR)/glquake/gl_test.o \
+	$(BUILDDIR)/glquake/gl_warp.o \
+	\
+	$(BUILDDIR)/glquake/host.o \
+	$(BUILDDIR)/glquake/host_cmd.o \
+	$(BUILDDIR)/glquake/keys.o \
+	$(BUILDDIR)/glquake/menu.o \
+	$(BUILDDIR)/glquake/mathlib.o \
+	$(BUILDDIR)/glquake/net_dgrm.o \
+	$(BUILDDIR)/glquake/net_loop.o \
+	$(BUILDDIR)/glquake/net_main.o \
+	$(BUILDDIR)/glquake/net_vcr.o \
+	$(BUILDDIR)/glquake/net_udp.o \
+	$(BUILDDIR)/glquake/net_bsd.o \
+	$(BUILDDIR)/glquake/pr_cmds.o \
+	$(BUILDDIR)/glquake/pr_edict.o \
+	$(BUILDDIR)/glquake/pr_exec.o \
+	$(BUILDDIR)/glquake/r_part.o \
+	$(BUILDDIR)/glquake/sbar.o \
+	$(BUILDDIR)/glquake/sv_main.o \
+	$(BUILDDIR)/glquake/sv_phys.o \
+	$(BUILDDIR)/glquake/sv_move.o \
+	$(BUILDDIR)/glquake/sv_user.o \
+	$(BUILDDIR)/glquake/zone.o	\
+	$(BUILDDIR)/glquake/view.o	\
+	$(BUILDDIR)/glquake/wad.o \
+	$(BUILDDIR)/glquake/world.o \
+	$(BUILDDIR)/glquake/cd_linux.o \
+	$(BUILDDIR)/glquake/sys_linux.o \
+	$(BUILDDIR)/glquake/snd_dma.o \
+	$(BUILDDIR)/glquake/snd_mem.o \
+	$(BUILDDIR)/glquake/snd_mix.o \
+	$(BUILDDIR)/glquake/snd_linux.o \
+	\
+	$(BUILDDIR)/glquake/math.o \
+	$(BUILDDIR)/glquake/worlda.o \
+	$(BUILDDIR)/glquake/snd_mixa.o \
+	$(BUILDDIR)/glquake/sys_dosa.o
+
+GLSVGA_OBJS=$(BUILDDIR)/glquake/gl_vidlinux.o
+
+GLX_OBJS=$(BUILDDIR)/glquake/gl_vidlinuxglx.o
+
+$(BUILDDIR)/bin/glquake : $(GLQUAKE_OBJS) $(GLSVGA_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(GLQUAKE_OBJS) $(GLSVGA_OBJS) $(MESAGLLDFLAGS) $(SVGALDFLAGS) $(LDFLAGS)
+
+$(BUILDDIR)/bin/glquake.glx : $(GLQUAKE_OBJS) $(GLX_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(GLQUAKE_OBJS) $(GLX_OBJS) $(GLLDFLAGS) $(LDFLAGS)
+
+$(BUILDDIR)/bin/glquake.3dfxgl : $(GLQUAKE_OBJS) $(GLSVGA_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(GLQUAKE_OBJS) $(GLSVGA_OBJS) $(TDFXGLLDFLAGS) $(SVGALDFLAGS) $(LDFLAGS)
+
+$(BUILDDIR)/glquake/cl_demo.o :      $(MOUNT_DIR)/cl_demo.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cl_input.o :     $(MOUNT_DIR)/cl_input.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cl_main.o :      $(MOUNT_DIR)/cl_main.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cl_parse.o :     $(MOUNT_DIR)/cl_parse.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cl_tent.o :      $(MOUNT_DIR)/cl_tent.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/chase.o :        $(MOUNT_DIR)/chase.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cmd.o :          $(MOUNT_DIR)/cmd.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/common.o :       $(MOUNT_DIR)/common.c
+	$(DO_GL_DEBUG_CC)
+
+$(BUILDDIR)/glquake/console.o :      $(MOUNT_DIR)/console.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/crc.o :          $(MOUNT_DIR)/crc.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cvar.o :         $(MOUNT_DIR)/cvar.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_draw.o :      $(MOUNT_DIR)/gl_draw.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_mesh.o :      $(MOUNT_DIR)/gl_mesh.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_model.o :     $(MOUNT_DIR)/gl_model.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_refrag.o :    $(MOUNT_DIR)/gl_refrag.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_rlight.o :    $(MOUNT_DIR)/gl_rlight.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_rmain.o :     $(MOUNT_DIR)/gl_rmain.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_rmisc.o :     $(MOUNT_DIR)/gl_rmisc.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_rsurf.o :     $(MOUNT_DIR)/gl_rsurf.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_screen.o :    $(MOUNT_DIR)/gl_screen.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_test.o :      $(MOUNT_DIR)/gl_test.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_vidlinux.o :  $(MOUNT_DIR)/gl_vidlinux.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_vidlinuxglx.o :  $(MOUNT_DIR)/gl_vidlinuxglx.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/gl_warp.o :      $(MOUNT_DIR)/gl_warp.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/host.o :         $(MOUNT_DIR)/host.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/host_cmd.o :     $(MOUNT_DIR)/host_cmd.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/keys.o :         $(MOUNT_DIR)/keys.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/menu.o :         $(MOUNT_DIR)/menu.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/mathlib.o :      $(MOUNT_DIR)/mathlib.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_dgrm.o :     $(MOUNT_DIR)/net_dgrm.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_loop.o :     $(MOUNT_DIR)/net_loop.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_main.o :     $(MOUNT_DIR)/net_main.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_vcr.o :      $(MOUNT_DIR)/net_vcr.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_udp.o :      $(MOUNT_DIR)/net_udp.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/net_bsd.o :      $(MOUNT_DIR)/net_bsd.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/pr_cmds.o :      $(MOUNT_DIR)/pr_cmds.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/pr_edict.o :     $(MOUNT_DIR)/pr_edict.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/pr_exec.o :      $(MOUNT_DIR)/pr_exec.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/r_part.o :       $(MOUNT_DIR)/r_part.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sbar.o :         $(MOUNT_DIR)/sbar.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sv_main.o :      $(MOUNT_DIR)/sv_main.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sv_phys.o :      $(MOUNT_DIR)/sv_phys.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sv_move.o :      $(MOUNT_DIR)/sv_move.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sv_user.o :      $(MOUNT_DIR)/sv_user.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/zone.o	:        $(MOUNT_DIR)/zone.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/view.o	:        $(MOUNT_DIR)/view.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/wad.o :          $(MOUNT_DIR)/wad.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/world.o :        $(MOUNT_DIR)/world.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/cd_linux.o :     $(MOUNT_DIR)/cd_linux.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/sys_linux.o :    $(MOUNT_DIR)/sys_linux.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/snd_dma.o :      $(MOUNT_DIR)/snd_dma.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/snd_mem.o :      $(MOUNT_DIR)/snd_mem.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/snd_mix.o :      $(MOUNT_DIR)/snd_mix.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/snd_linux.o :    $(MOUNT_DIR)/snd_linux.c
+	$(DO_GL_CC)
+
+$(BUILDDIR)/glquake/math.o :         $(MOUNT_DIR)/math.S
+	$(DO_GL_AS)
+
+$(BUILDDIR)/glquake/worlda.o :       $(MOUNT_DIR)/worlda.S
+	$(DO_GL_AS)
+
+$(BUILDDIR)/glquake/snd_mixa.o :     $(MOUNT_DIR)/snd_mixa.S
+	$(DO_GL_AS)
+
+$(BUILDDIR)/glquake/sys_dosa.o :     $(MOUNT_DIR)/sys_dosa.S
+	$(DO_GL_AS)
+
+#############################################################################
+# RPM
+#############################################################################
+
+# Make RPMs.  You need to be root to make this work
+RPMROOT=/usr/src/redhat
+RPM = rpm
+RPMFLAGS = -bb
+INSTALLDIR = /usr/local/games/quake
+TMPDIR = /var/tmp
+RPMDIR = $(TMPDIR)/quake-$(VERSION)
+BASERPMDIR = $(TMPDIR)/quake-$(BASEVERSION)
+
+rpm: rpm-quake rpm-quake-data rpm-hipnotic rpm-rogue
+
+rpm-quake: quake.spec \
+		$(BUILD_RELEASE_DIR)/bin/squake \
+		$(BUILD_RELEASE_DIR)/bin/quake.x11 \
+		$(BUILD_RELEASE_DIR)/bin/glquake \
+		$(BUILD_RELEASE_DIR)/bin/glquake.glx \
+		$(BUILD_RELEASE_DIR)/bin/glquake.3dfxgl
+	touch $(RPMROOT)/SOURCES/quake-$(VERSION).tar.gz
+	if [ ! -d RPMS ];then mkdir RPMS;fi
+	cp $(MOUNT_DIR)/quake.gif $(RPMROOT)/SOURCES/quake.gif
+
+	# basic binaries rpm
+	-mkdirhier $(RPMDIR)/$(INSTALLDIR)
+	cp $(MOUNT_DIR)/docs/README $(RPMDIR)/$(INSTALLDIR)/.
+	cp $(BUILD_RELEASE_DIR)/bin/squake $(RPMDIR)/$(INSTALLDIR)/squake
+	strip $(RPMDIR)/$(INSTALLDIR)/squake
+	cp $(BUILD_RELEASE_DIR)/bin/quake.x11 $(RPMDIR)/$(INSTALLDIR)/quake.x11
+	strip $(RPMDIR)/$(INSTALLDIR)/quake.x11
+	cp $(BUILD_RELEASE_DIR)/bin/glquake $(RPMDIR)/$(INSTALLDIR)/glquake
+	strip $(RPMDIR)/$(INSTALLDIR)/glquake
+	cp $(BUILD_RELEASE_DIR)/bin/glquake.glx $(RPMDIR)/$(INSTALLDIR)/glquake.glx
+	strip $(RPMDIR)/$(INSTALLDIR)/glquake.glx
+	cp $(BUILD_RELEASE_DIR)/bin/glquake.3dfxgl $(RPMDIR)/$(INSTALLDIR)/glquake.3dfxgl
+	strip $(RPMDIR)/$(INSTALLDIR)/glquake.3dfxgl
+	-mkdirhier $(RPMDIR)/usr/lib
+	cp $(TDFXGL_DIR)/release$(ARCH)$(GLIBC)/lib3dfxgl.so $(RPMDIR)/usr/lib/lib3dfxgl.so
+	cp $(MESA_DIR)/lib/libMesaGL.so.2.6 $(RPMDIR)/usr/lib/libMesaGL.so.2.6
+
+	cp quake.spec $(RPMROOT)/SPECS/.
+	cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) quake.spec
+	rm -rf $(RPMDIR)
+	rm -f $(RPMROOT)/SOURCES/quake-$(VERSION).tar.gz
+
+	mv $(RPMROOT)/RPMS/$(ARCH)/quake-$(VERSION)-$(RPM_RELEASE).$(ARCH).rpm RPMS/.
+
+QUAKEDATADIR=$(TMPDIR)/quake-data-$(BASEVERSION)
+rpm-quake-data: quake-data.spec
+	# data rpm
+	touch $(RPMROOT)/SOURCES/quake-$(BASEVERSION)-data.tar.gz
+
+	-mkdirhier $(QUAKEDATADIR)/$(INSTALLDIR)/id1
+	cp $(MASTER_DIR)/id1/pak0.pak $(QUAKEDATADIR)/$(INSTALLDIR)/id1/.
+	cp $(MASTER_DIR)/id1/pak1.pak $(QUAKEDATADIR)/$(INSTALLDIR)/id1/.
+	cp $(MOUNT_DIR)/docs/README $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/comexp.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/help.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/licinfo.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/manual.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/readme.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/rlicnse.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/slicnse.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp $(MOUNT_DIR)/data/techinfo.txt $(QUAKEDATADIR)/$(INSTALLDIR)/.
+	cp quake-data.spec $(RPMROOT)/SPECS/.
+	cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) quake-data.spec
+	rm -rf $(QUAKEDATADIR)
+	rm -f $(RPMROOT)/SOURCES/quake-$(BASEVERSION)-data.tar.gz
+
+	mv $(RPMROOT)/RPMS/$(NOARCH)/quake-data-$(BASEVERSION)-$(RPM_RELEASE).$(NOARCH).rpm RPMS/.
+
+RPMHIPNOTICDIR=$(TMPDIR)/quake-hipnotic-$(BASEVERSION)
+rpm-hipnotic: quake-hipnotic.spec
+	touch $(RPMROOT)/SOURCES/quake-hipnotic-$(BASEVERSION).tar.gz
+	if [ ! -d RPMS ];then mkdir RPMS;fi
+	cp $(MOUNT_DIR)/quake.gif $(RPMROOT)/SOURCES/quake.gif
+	-mkdirhier $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs
+	cp $(MASTER_DIR)/hipnotic/pak0.pak $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/.
+	cp $(MASTER_DIR)/hipnotic/config.cfg $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/.
+	cp $(MASTER_DIR)/hipnotic/docs/manual.doc $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp $(MASTER_DIR)/hipnotic/docs/manual.htm $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp $(MASTER_DIR)/hipnotic/docs/manual.txt $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp $(MASTER_DIR)/hipnotic/docs/readme.doc $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp $(MASTER_DIR)/hipnotic/docs/readme.htm $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp $(MASTER_DIR)/hipnotic/docs/readme.txt $(RPMHIPNOTICDIR)/$(INSTALLDIR)/hipnotic/docs/.
+	cp quake-hipnotic.spec $(RPMROOT)/SPECS/.
+	cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) quake-hipnotic.spec
+	rm -rf $(RPMHIPNOTICDIR)
+	rm -f $(RPMROOT)/SOURCES/quake-hipnotic-$(BASEVERSION).tar.gz
+
+	mv $(RPMROOT)/RPMS/$(NOARCH)/quake-hipnotic-$(BASEVERSION)-$(RPM_RELEASE).$(NOARCH).rpm RPMS/.
+
+RPMROGUEDIR=$(TMPDIR)/quake-rogue-$(BASEVERSION)
+rpm-rogue: quake-rogue.spec
+	touch $(RPMROOT)/SOURCES/quake-rogue-$(BASEVERSION).tar.gz
+	if [ ! -d RPMS ];then mkdir RPMS;fi
+	cp $(MOUNT_DIR)/quake.gif $(RPMROOT)/SOURCES/quake.gif
+	-mkdirhier $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs
+	cp $(MASTER_DIR)/rogue/pak0.pak $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/.
+	cp $(MASTER_DIR)/rogue/docs/manual.doc $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/manual.htm $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/manual.txt $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/readme.doc $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/readme.htm $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/readme.txt $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/ctf.doc $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/ctf.htm $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp $(MASTER_DIR)/rogue/docs/ctf.txt $(RPMROGUEDIR)/$(INSTALLDIR)/rogue/docs/.
+	cp quake-rogue.spec $(RPMROOT)/SPECS/.
+	cd $(RPMROOT)/SPECS; $(RPM) $(RPMFLAGS) quake-rogue.spec
+	rm -rf $(RPMROGUEDIR)
+	rm -f $(RPMROOT)/SOURCES/quake-rogue-$(BASEVERSION).tar.gz
+
+	mv $(RPMROOT)/RPMS/$(NOARCH)/quake-rogue-$(BASEVERSION)-$(RPM_RELEASE).$(NOARCH).rpm RPMS/.
+
+quake.spec : $(MOUNT_DIR)/quake.spec.sh
+	sh $< $(VERSION) $(RPM_RELEASE) $(INSTALLDIR) > $@
+
+quake-data.spec : $(MOUNT_DIR)/quake-data.spec.sh
+	sh $< $(BASEVERSION) $(RPM_RELEASE) $(INSTALLDIR) > $@
+
+quake-hipnotic.spec : $(MOUNT_DIR)/quake-hipnotic.spec.sh
+	sh $< $(BASEVERSION) $(RPM_RELEASE) $(INSTALLDIR) > $@
+
+quake-rogue.spec : $(MOUNT_DIR)/quake-rogue.spec.sh
+	sh $< $(BASEVERSION) $(RPM_RELEASE) $(INSTALLDIR) > $@
+
+#############################################################################
+# MISC
+#############################################################################
+
+clean: clean-debug clean-release
+	rm -f squake.spec glquake.spec quake.x11.spec
+
+clean-debug:
+	$(MAKE) clean2 BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)"
+
+clean-release:
+	$(MAKE) clean2 BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(DEBUG_CFLAGS)"
+
+clean2:
+	-rm -f $(SQUAKE_OBJS) $(X11_OBJS) $(GLQUAKE_OBJS) $(GLSVGA_OBJS) \
+		$(GLX_OBJS)
+
diff --git a/apps/plugins/sdl/progs/quake/adivtab.h b/apps/plugins/sdl/progs/quake/adivtab.h
new file mode 100644
index 0000000..238df5c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/adivtab.h
@@ -0,0 +1,1077 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// table of quotients and remainders for [-15...16] / [-15...16]
+
+// numerator = -15
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{1, -7},
+{2, -1},
+{2, -3},
+{3, 0},
+{3, -3},
+{5, 0},
+{7, -1},
+{15, 0},
+{0, 0},
+{-15, 0},
+{-8, 1},
+{-5, 0},
+{-4, 1},
+{-3, 0},
+{-3, 3},
+{-3, 6},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-2, 11},
+{-2, 13},
+{-1, 0},
+{-1, 1},
+// numerator = -14
+{0, -14},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{2, 0},
+{2, -2},
+{2, -4},
+{3, -2},
+{4, -2},
+{7, 0},
+{14, 0},
+{0, 0},
+{-14, 0},
+{-7, 0},
+{-5, 1},
+{-4, 2},
+{-3, 1},
+{-3, 4},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-2, 10},
+{-2, 12},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+// numerator = -13
+{0, -13},
+{0, -13},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{2, -1},
+{2, -3},
+{3, -1},
+{4, -1},
+{6, -1},
+{13, 0},
+{0, 0},
+{-13, 0},
+{-7, 1},
+{-5, 2},
+{-4, 3},
+{-3, 2},
+{-3, 5},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-2, 11},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+// numerator = -12
+{0, -12},
+{0, -12},
+{0, -12},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{2, 0},
+{2, -2},
+{3, 0},
+{4, 0},
+{6, 0},
+{12, 0},
+{0, 0},
+{-12, 0},
+{-6, 0},
+{-4, 0},
+{-3, 0},
+{-3, 3},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-2, 10},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+// numerator = -11
+{0, -11},
+{0, -11},
+{0, -11},
+{0, -11},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{2, -1},
+{2, -3},
+{3, -2},
+{5, -1},
+{11, 0},
+{0, 0},
+{-11, 0},
+{-6, 1},
+{-4, 1},
+{-3, 1},
+{-3, 4},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+// numerator = -10
+{0, -10},
+{0, -10},
+{0, -10},
+{0, -10},
+{0, -10},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{2, 0},
+{2, -2},
+{3, -1},
+{5, 0},
+{10, 0},
+{0, 0},
+{-10, 0},
+{-5, 0},
+{-4, 2},
+{-3, 2},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+// numerator = -9
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{2, -1},
+{3, 0},
+{4, -1},
+{9, 0},
+{0, 0},
+{-9, 0},
+{-5, 1},
+{-3, 0},
+{-3, 3},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+// numerator = -8
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{2, 0},
+{2, -2},
+{4, 0},
+{8, 0},
+{0, 0},
+{-8, 0},
+{-4, 0},
+{-3, 1},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+// numerator = -7
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{2, -1},
+{3, -1},
+{7, 0},
+{0, 0},
+{-7, 0},
+{-4, 1},
+{-3, 2},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+// numerator = -6
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{1, 0},
+{1, -1},
+{1, -2},
+{2, 0},
+{3, 0},
+{6, 0},
+{0, 0},
+{-6, 0},
+{-3, 0},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+// numerator = -5
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{1, 0},
+{1, -1},
+{1, -2},
+{2, -1},
+{5, 0},
+{0, 0},
+{-5, 0},
+{-3, 1},
+{-2, 1},
+{-2, 3},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+// numerator = -4
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{1, 0},
+{1, -1},
+{2, 0},
+{4, 0},
+{0, 0},
+{-4, 0},
+{-2, 0},
+{-2, 2},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+// numerator = -3
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{1, 0},
+{1, -1},
+{3, 0},
+{0, 0},
+{-3, 0},
+{-2, 1},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+// numerator = -2
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{1, 0},
+{2, 0},
+{0, 0},
+{-2, 0},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+{-1, 14},
+// numerator = -1
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{1, 0},
+{0, 0},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+{-1, 14},
+{-1, 15},
+// numerator = 0
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+// numerator = 1
+{-1, -14},
+{-1, -13},
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{0, 0},
+{1, 0},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+// numerator = 2
+{-1, -13},
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, 0},
+{0, 0},
+{2, 0},
+{1, 0},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+// numerator = 3
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -1},
+{-3, 0},
+{0, 0},
+{3, 0},
+{1, 1},
+{1, 0},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+// numerator = 4
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -2},
+{-2, 0},
+{-4, 0},
+{0, 0},
+{4, 0},
+{2, 0},
+{1, 1},
+{1, 0},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+// numerator = 5
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -3},
+{-2, -1},
+{-3, -1},
+{-5, 0},
+{0, 0},
+{5, 0},
+{2, 1},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+// numerator = 6
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, 0},
+{-6, 0},
+{0, 0},
+{6, 0},
+{3, 0},
+{2, 0},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+// numerator = 7
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -2},
+{-4, -1},
+{-7, 0},
+{0, 0},
+{7, 0},
+{3, 1},
+{2, 1},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+// numerator = 8
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -1},
+{-4, 0},
+{-8, 0},
+{0, 0},
+{8, 0},
+{4, 0},
+{2, 2},
+{2, 0},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+// numerator = 9
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -3},
+{-3, 0},
+{-5, -1},
+{-9, 0},
+{0, 0},
+{9, 0},
+{4, 1},
+{3, 0},
+{2, 1},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+// numerator = 10
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -2},
+{-4, -2},
+{-5, 0},
+{-10, 0},
+{0, 0},
+{10, 0},
+{5, 0},
+{3, 1},
+{2, 2},
+{2, 0},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+// numerator = 11
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -4},
+{-3, -1},
+{-4, -1},
+{-6, -1},
+{-11, 0},
+{0, 0},
+{11, 0},
+{5, 1},
+{3, 2},
+{2, 3},
+{2, 1},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 11},
+{0, 11},
+{0, 11},
+{0, 11},
+{0, 11},
+// numerator = 12
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -3},
+{-3, 0},
+{-4, 0},
+{-6, 0},
+{-12, 0},
+{0, 0},
+{12, 0},
+{6, 0},
+{4, 0},
+{3, 0},
+{2, 2},
+{2, 0},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 12},
+{0, 12},
+{0, 12},
+{0, 12},
+// numerator = 13
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -11},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -5},
+{-3, -2},
+{-4, -3},
+{-5, -2},
+{-7, -1},
+{-13, 0},
+{0, 0},
+{13, 0},
+{6, 1},
+{4, 1},
+{3, 1},
+{2, 3},
+{2, 1},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 13},
+{0, 13},
+{0, 13},
+// numerator = 14
+{-1, -1},
+{-1, 0},
+{-2, -12},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -4},
+{-3, -1},
+{-4, -2},
+{-5, -1},
+{-7, 0},
+{-14, 0},
+{0, 0},
+{14, 0},
+{7, 0},
+{4, 2},
+{3, 2},
+{2, 4},
+{2, 2},
+{2, 0},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 14},
+{0, 14},
+// numerator = 15
+{-1, 0},
+{-2, -13},
+{-2, -11},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -6},
+{-3, -3},
+{-3, 0},
+{-4, -1},
+{-5, 0},
+{-8, -1},
+{-15, 0},
+{0, 0},
+{15, 0},
+{7, 1},
+{5, 0},
+{3, 3},
+{3, 0},
+{2, 3},
+{2, 1},
+{1, 7},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 15},
+// numerator = 16
+{-2, -14},
+{-2, -12},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -5},
+{-3, -2},
+{-4, -4},
+{-4, 0},
+{-6, -2},
+{-8, 0},
+{-16, 0},
+{0, 0},
+{16, 0},
+{8, 0},
+{5, 1},
+{4, 0},
+{3, 1},
+{2, 4},
+{2, 2},
+{2, 0},
+{1, 7},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
diff --git a/apps/plugins/sdl/progs/quake/anorm_dots.h b/apps/plugins/sdl/progs/quake/anorm_dots.h
new file mode 100644
index 0000000..2845fa2
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/anorm_dots.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+{
+{1.23,1.30,1.47,1.35,1.56,1.71,1.37,1.38,1.59,1.60,1.79,1.97,1.88,1.92,1.79,1.02,0.93,1.07,0.82,0.87,0.88,0.94,0.96,1.14,1.11,0.82,0.83,0.89,0.89,0.86,0.94,0.91,1.00,1.21,0.98,1.48,1.30,1.57,0.96,1.07,1.14,1.60,1.61,1.40,1.37,1.72,1.78,1.79,1.93,1.99,1.90,1.68,1.71,1.86,1.60,1.68,1.78,1.86,1.93,1.99,1.97,1.44,1.22,1.49,0.93,0.99,0.99,1.23,1.22,1.44,1.49,0.89,0.89,0.97,0.91,0.98,1.19,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.19,0.98,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.87,0.93,0.94,1.02,1.30,1.07,1.35,1.38,1.11,1.56,1.92,1.79,1.79,1.59,1.60,1.72,1.90,1.79,0.80,0.85,0.79,0.93,0.80,0.85,0.77,0.74,0.72,0.77,0.74,0.72,0.70,0.70,0.71,0.76,0.73,0.79,0.79,0.73,0.76,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.26,1.26,1.48,1.23,1.50,1.71,1.14,1.19,1.38,1.46,1.64,1.94,1.87,1.84,1.71,1.02,0.92,1.00,0.79,0.85,0.84,0.91,0.90,0.98,0.99,0.77,0.77,0.83,0.82,0.79,0.86,0.84,0.92,0.99,0.91,1.24,1.03,1.33,0.88,0.94,0.97,1.41,1.39,1.18,1.11,1.51,1.61,1.59,1.80,1.91,1.76,1.54,1.65,1.76,1.70,1.70,1.85,1.85,1.97,1.99,1.93,1.28,1.09,1.39,0.92,0.97,0.99,1.18,1.26,1.52,1.48,0.83,0.85,0.90,0.88,0.93,1.00,0.77,0.73,0.78,0.72,0.71,0.74,0.75,0.79,0.86,0.81,0.75,0.81,0.79,0.96,0.88,0.94,0.86,0.93,0.92,0.85,1.08,1.33,1.05,1.55,1.31,1.01,1.05,1.27,1.31,1.60,1.47,1.70,1.54,1.76,1.76,1.57,0.93,0.90,0.99,0.88,0.88,0.95,0.97,1.11,1.39,1.20,0.92,0.97,1.01,1.10,1.39,1.22,1.51,1.58,1.32,1.64,1.97,1.85,1.91,1.77,1.74,1.88,1.99,1.91,0.79,0.86,0.80,0.94,0.84,0.88,0.74,0.74,0.71,0.82,0.77,0.76,0.70,0.73,0.72,0.73,0.70,0.74,0.85,0.77,0.82,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.34,1.27,1.53,1.17,1.46,1.71,0.98,1.05,1.20,1.34,1.48,1.86,1.82,1.71,1.62,1.09,0.94,0.99,0.79,0.85,0.82,0.90,0.87,0.93,0.96,0.76,0.74,0.79,0.76,0.74,0.79,0.78,0.85,0.92,0.85,1.00,0.93,1.06,0.81,0.86,0.89,1.16,1.12,0.97,0.95,1.28,1.38,1.35,1.60,1.77,1.57,1.33,1.50,1.58,1.69,1.63,1.82,1.74,1.91,1.92,1.80,1.04,0.97,1.21,0.90,0.93,0.97,1.05,1.21,1.48,1.37,0.77,0.80,0.84,0.85,0.88,0.92,0.73,0.71,0.74,0.74,0.71,0.75,0.73,0.79,0.84,0.78,0.79,0.86,0.81,1.05,0.94,0.99,0.90,0.95,0.92,0.86,1.24,1.44,1.14,1.59,1.34,1.02,1.27,1.50,1.49,1.80,1.69,1.86,1.72,1.87,1.80,1.69,1.00,0.98,1.23,0.95,0.96,1.09,1.16,1.37,1.63,1.46,0.99,1.10,1.25,1.24,1.51,1.41,1.67,1.77,1.55,1.72,1.95,1.89,1.98,1.91,1.86,1.97,1.99,1.94,0.81,0.89,0.85,0.98,0.90,0.94,0.75,0.78,0.73,0.89,0.83,0.82,0.72,0.77,0.76,0.72,0.70,0.71,0.91,0.83,0.89,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.46,1.34,1.60,1.16,1.46,1.71,0.94,0.99,1.05,1.26,1.33,1.74,1.76,1.57,1.54,1.23,0.98,1.05,0.83,0.89,0.84,0.92,0.87,0.91,0.96,0.78,0.74,0.79,0.72,0.72,0.75,0.76,0.80,0.88,0.83,0.94,0.87,0.95,0.76,0.80,0.82,0.97,0.96,0.89,0.88,1.08,1.11,1.10,1.37,1.59,1.37,1.07,1.27,1.34,1.57,1.45,1.69,1.55,1.77,1.79,1.60,0.93,0.90,0.99,0.86,0.87,0.93,0.96,1.07,1.35,1.18,0.73,0.76,0.77,0.81,0.82,0.85,0.70,0.71,0.72,0.78,0.73,0.77,0.73,0.79,0.82,0.76,0.83,0.90,0.84,1.18,0.98,1.03,0.92,0.95,0.90,0.86,1.32,1.45,1.15,1.53,1.27,0.99,1.42,1.65,1.58,1.93,1.83,1.94,1.81,1.88,1.74,1.70,1.19,1.17,1.44,1.11,1.15,1.36,1.41,1.61,1.81,1.67,1.22,1.34,1.50,1.42,1.65,1.61,1.82,1.91,1.75,1.80,1.89,1.89,1.98,1.99,1.94,1.98,1.92,1.87,0.86,0.95,0.92,1.14,0.98,1.03,0.79,0.84,0.77,0.97,0.90,0.89,0.76,0.82,0.82,0.74,0.72,0.71,0.98,0.89,0.97,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.60,1.44,1.68,1.22,1.49,1.71,0.93,0.99,0.99,1.23,1.22,1.60,1.68,1.44,1.49,1.40,1.14,1.19,0.89,0.96,0.89,0.97,0.89,0.91,0.98,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.91,0.83,0.89,0.72,0.76,0.76,0.89,0.89,0.82,0.82,0.98,0.96,0.97,1.14,1.40,1.19,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.70,0.72,0.73,0.77,0.76,0.79,0.70,0.72,0.71,0.82,0.77,0.80,0.74,0.79,0.80,0.74,0.87,0.93,0.85,1.23,1.02,1.02,0.93,0.93,0.87,0.85,1.30,1.35,1.07,1.38,1.11,0.94,1.47,1.71,1.56,1.97,1.88,1.92,1.79,1.79,1.59,1.60,1.30,1.35,1.56,1.37,1.38,1.59,1.60,1.79,1.92,1.79,1.48,1.57,1.72,1.61,1.78,1.79,1.93,1.99,1.90,1.86,1.78,1.86,1.93,1.99,1.97,1.90,1.79,1.72,0.94,1.07,1.00,1.37,1.21,1.30,0.86,0.91,0.83,1.14,0.98,0.96,0.82,0.88,0.89,0.79,0.76,0.73,1.07,0.94,1.11,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.74,1.57,1.76,1.33,1.54,1.71,0.94,1.05,0.99,1.26,1.16,1.46,1.60,1.34,1.46,1.59,1.37,1.37,0.97,1.11,0.96,1.10,0.95,0.94,1.08,0.89,0.82,0.88,0.72,0.76,0.75,0.80,0.80,0.88,0.87,0.91,0.83,0.87,0.72,0.76,0.74,0.83,0.84,0.78,0.79,0.96,0.89,0.92,0.98,1.23,1.05,0.86,0.92,0.95,1.11,0.98,1.22,1.03,1.34,1.42,1.14,0.79,0.77,0.84,0.78,0.76,0.82,0.82,0.89,0.97,0.90,0.70,0.71,0.71,0.73,0.72,0.74,0.73,0.76,0.72,0.86,0.81,0.82,0.76,0.79,0.77,0.73,0.90,0.95,0.86,1.18,1.03,0.98,0.92,0.90,0.83,0.84,1.19,1.17,0.98,1.15,0.97,0.89,1.42,1.65,1.44,1.93,1.83,1.81,1.67,1.61,1.36,1.41,1.32,1.45,1.58,1.57,1.53,1.74,1.70,1.88,1.94,1.81,1.69,1.77,1.87,1.79,1.89,1.92,1.98,1.99,1.98,1.89,1.65,1.80,1.82,1.91,1.94,1.75,1.61,1.50,1.07,1.34,1.27,1.60,1.45,1.55,0.93,0.99,0.90,1.35,1.18,1.07,0.87,0.93,0.96,0.85,0.82,0.77,1.15,0.99,1.27,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.86,1.71,1.82,1.48,1.62,1.71,0.98,1.20,1.05,1.34,1.17,1.34,1.53,1.27,1.46,1.77,1.60,1.57,1.16,1.38,1.12,1.35,1.06,1.00,1.28,0.97,0.89,0.95,0.76,0.81,0.79,0.86,0.85,0.92,0.93,0.93,0.85,0.87,0.74,0.78,0.74,0.79,0.82,0.76,0.79,0.96,0.85,0.90,0.94,1.09,0.99,0.81,0.85,0.89,0.95,0.90,0.99,0.94,1.10,1.24,0.98,0.75,0.73,0.78,0.74,0.72,0.77,0.76,0.82,0.89,0.83,0.73,0.71,0.71,0.71,0.70,0.72,0.77,0.80,0.74,0.90,0.85,0.84,0.78,0.79,0.75,0.73,0.92,0.95,0.86,1.05,0.99,0.94,0.90,0.86,0.79,0.81,1.00,0.98,0.91,0.96,0.89,0.83,1.27,1.50,1.23,1.80,1.69,1.63,1.46,1.37,1.09,1.16,1.24,1.44,1.49,1.69,1.59,1.80,1.69,1.87,1.86,1.72,1.82,1.91,1.94,1.92,1.95,1.99,1.98,1.91,1.97,1.89,1.51,1.72,1.67,1.77,1.86,1.55,1.41,1.25,1.33,1.58,1.50,1.80,1.63,1.74,1.04,1.21,0.97,1.48,1.37,1.21,0.93,0.97,1.05,0.92,0.88,0.84,1.14,1.02,1.34,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.94,1.84,1.87,1.64,1.71,1.71,1.14,1.38,1.19,1.46,1.23,1.26,1.48,1.26,1.50,1.91,1.80,1.76,1.41,1.61,1.39,1.59,1.33,1.24,1.51,1.18,0.97,1.11,0.82,0.88,0.86,0.94,0.92,0.99,1.03,0.98,0.91,0.90,0.79,0.84,0.77,0.79,0.84,0.77,0.83,0.99,0.85,0.91,0.92,1.02,1.00,0.79,0.80,0.86,0.88,0.84,0.92,0.88,0.97,1.10,0.94,0.74,0.71,0.74,0.72,0.70,0.73,0.72,0.76,0.82,0.77,0.77,0.73,0.74,0.71,0.70,0.73,0.83,0.85,0.78,0.92,0.88,0.86,0.81,0.79,0.74,0.75,0.92,0.93,0.85,0.96,0.94,0.88,0.86,0.81,0.75,0.79,0.93,0.90,0.85,0.88,0.82,0.77,1.05,1.27,0.99,1.60,1.47,1.39,1.20,1.11,0.95,0.97,1.08,1.33,1.31,1.70,1.55,1.76,1.57,1.76,1.70,1.54,1.85,1.97,1.91,1.99,1.97,1.99,1.91,1.77,1.88,1.85,1.39,1.64,1.51,1.58,1.74,1.32,1.22,1.01,1.54,1.76,1.65,1.93,1.70,1.85,1.28,1.39,1.09,1.52,1.48,1.26,0.97,0.99,1.18,1.00,0.93,0.90,1.05,1.01,1.31,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.97,1.92,1.88,1.79,1.79,1.71,1.37,1.59,1.38,1.60,1.35,1.23,1.47,1.30,1.56,1.99,1.93,1.90,1.60,1.78,1.61,1.79,1.57,1.48,1.72,1.40,1.14,1.37,0.89,0.96,0.94,1.07,1.00,1.21,1.30,1.14,0.98,0.96,0.86,0.91,0.83,0.82,0.88,0.82,0.89,1.11,0.87,0.94,0.93,1.02,1.07,0.80,0.79,0.85,0.82,0.80,0.87,0.85,0.93,1.02,0.93,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.82,0.76,0.79,0.72,0.73,0.76,0.89,0.89,0.82,0.93,0.91,0.86,0.83,0.79,0.73,0.76,0.91,0.89,0.83,0.89,0.89,0.82,0.82,0.76,0.72,0.76,0.86,0.83,0.79,0.82,0.76,0.73,0.94,1.00,0.91,1.37,1.21,1.14,0.98,0.96,0.88,0.89,0.96,1.14,1.07,1.60,1.40,1.61,1.37,1.57,1.48,1.30,1.78,1.93,1.79,1.99,1.92,1.90,1.79,1.59,1.72,1.79,1.30,1.56,1.35,1.38,1.60,1.11,1.07,0.94,1.68,1.86,1.71,1.97,1.68,1.86,1.44,1.49,1.22,1.44,1.49,1.22,0.99,0.99,1.23,1.19,0.98,0.97,0.97,0.98,1.19,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.94,1.97,1.87,1.91,1.85,1.71,1.60,1.77,1.58,1.74,1.51,1.26,1.48,1.39,1.64,1.99,1.97,1.99,1.70,1.85,1.76,1.91,1.76,1.70,1.88,1.55,1.33,1.57,0.96,1.08,1.05,1.31,1.27,1.47,1.54,1.39,1.20,1.11,0.93,0.99,0.90,0.88,0.95,0.88,0.97,1.32,0.92,1.01,0.97,1.10,1.22,0.84,0.80,0.88,0.79,0.79,0.85,0.86,0.92,1.02,0.94,0.82,0.76,0.77,0.72,0.73,0.70,0.72,0.71,0.74,0.74,0.88,0.81,0.85,0.75,0.77,0.82,0.94,0.93,0.86,0.92,0.92,0.86,0.85,0.79,0.74,0.79,0.88,0.85,0.81,0.82,0.83,0.77,0.78,0.73,0.71,0.75,0.79,0.77,0.74,0.77,0.73,0.70,0.86,0.92,0.84,1.14,0.99,0.98,0.91,0.90,0.84,0.83,0.88,0.97,0.94,1.41,1.18,1.39,1.11,1.33,1.24,1.03,1.61,1.80,1.59,1.91,1.84,1.76,1.64,1.38,1.51,1.71,1.26,1.50,1.23,1.19,1.46,0.99,1.00,0.91,1.70,1.85,1.65,1.93,1.54,1.76,1.52,1.48,1.26,1.28,1.39,1.09,0.99,0.97,1.18,1.31,1.01,1.05,0.90,0.93,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.86,1.95,1.82,1.98,1.89,1.71,1.80,1.91,1.77,1.86,1.67,1.34,1.53,1.51,1.72,1.92,1.91,1.99,1.69,1.82,1.80,1.94,1.87,1.86,1.97,1.59,1.44,1.69,1.05,1.24,1.27,1.49,1.50,1.69,1.72,1.63,1.46,1.37,1.00,1.23,0.98,0.95,1.09,0.96,1.16,1.55,0.99,1.25,1.10,1.24,1.41,0.90,0.85,0.94,0.79,0.81,0.85,0.89,0.94,1.09,0.98,0.89,0.82,0.83,0.74,0.77,0.72,0.76,0.73,0.75,0.78,0.94,0.86,0.91,0.79,0.83,0.89,0.99,0.95,0.90,0.90,0.92,0.84,0.86,0.79,0.75,0.81,0.85,0.80,0.78,0.76,0.77,0.73,0.74,0.71,0.71,0.73,0.74,0.74,0.71,0.76,0.72,0.70,0.79,0.85,0.78,0.98,0.92,0.93,0.85,0.87,0.82,0.79,0.81,0.89,0.86,1.16,0.97,1.12,0.95,1.06,1.00,0.93,1.38,1.60,1.35,1.77,1.71,1.57,1.48,1.20,1.28,1.62,1.27,1.46,1.17,1.05,1.34,0.96,0.99,0.90,1.63,1.74,1.50,1.80,1.33,1.58,1.48,1.37,1.21,1.04,1.21,0.97,0.97,0.93,1.05,1.34,1.02,1.14,0.84,0.88,0.92,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.74,1.89,1.76,1.98,1.89,1.71,1.93,1.99,1.91,1.94,1.82,1.46,1.60,1.65,1.80,1.79,1.77,1.92,1.57,1.69,1.74,1.87,1.88,1.94,1.98,1.53,1.45,1.70,1.18,1.32,1.42,1.58,1.65,1.83,1.81,1.81,1.67,1.61,1.19,1.44,1.17,1.11,1.36,1.15,1.41,1.75,1.22,1.50,1.34,1.42,1.61,0.98,0.92,1.03,0.83,0.86,0.89,0.95,0.98,1.23,1.14,0.97,0.89,0.90,0.78,0.82,0.76,0.82,0.77,0.79,0.84,0.98,0.90,0.98,0.83,0.89,0.97,1.03,0.95,0.92,0.86,0.90,0.82,0.86,0.79,0.77,0.84,0.81,0.76,0.76,0.72,0.73,0.70,0.72,0.71,0.73,0.73,0.72,0.74,0.71,0.78,0.74,0.72,0.75,0.80,0.76,0.94,0.88,0.91,0.83,0.87,0.84,0.79,0.76,0.82,0.80,0.97,0.89,0.96,0.88,0.95,0.94,0.87,1.11,1.37,1.10,1.59,1.57,1.37,1.33,1.05,1.08,1.54,1.34,1.46,1.16,0.99,1.26,0.96,1.05,0.92,1.45,1.55,1.27,1.60,1.07,1.34,1.35,1.18,1.07,0.93,0.99,0.90,0.93,0.87,0.96,1.27,0.99,1.15,0.77,0.82,0.85,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.60,1.78,1.68,1.93,1.86,1.71,1.97,1.99,1.99,1.97,1.93,1.60,1.68,1.78,1.86,1.61,1.57,1.79,1.37,1.48,1.59,1.72,1.79,1.92,1.90,1.38,1.35,1.60,1.23,1.30,1.47,1.56,1.71,1.88,1.79,1.92,1.79,1.79,1.30,1.56,1.35,1.37,1.59,1.38,1.60,1.90,1.48,1.72,1.57,1.61,1.79,1.21,1.00,1.30,0.89,0.94,0.96,1.07,1.14,1.40,1.37,1.14,0.96,0.98,0.82,0.88,0.82,0.89,0.83,0.86,0.91,1.02,0.93,1.07,0.87,0.94,1.11,1.02,0.93,0.93,0.82,0.87,0.80,0.85,0.79,0.80,0.85,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.72,0.76,0.73,0.82,0.79,0.76,0.73,0.79,0.76,0.93,0.86,0.91,0.83,0.89,0.89,0.82,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.44,1.19,1.22,0.99,0.98,1.49,1.44,1.49,1.22,0.99,1.23,0.98,1.19,0.97,1.21,1.30,1.00,1.37,0.94,1.07,1.14,0.98,0.96,0.86,0.91,0.83,0.88,0.82,0.89,1.11,0.94,1.07,0.73,0.76,0.79,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.46,1.65,1.60,1.82,1.80,1.71,1.93,1.91,1.99,1.94,1.98,1.74,1.76,1.89,1.89,1.42,1.34,1.61,1.11,1.22,1.36,1.50,1.61,1.81,1.75,1.15,1.17,1.41,1.18,1.19,1.42,1.44,1.65,1.83,1.67,1.94,1.81,1.88,1.32,1.58,1.45,1.57,1.74,1.53,1.70,1.98,1.69,1.87,1.77,1.79,1.92,1.45,1.27,1.55,0.97,1.07,1.11,1.34,1.37,1.59,1.60,1.35,1.07,1.18,0.86,0.93,0.87,0.96,0.90,0.93,0.99,1.03,0.95,1.15,0.90,0.99,1.27,0.98,0.90,0.92,0.78,0.83,0.77,0.84,0.79,0.82,0.86,0.73,0.71,0.73,0.72,0.70,0.73,0.72,0.76,0.81,0.76,0.76,0.82,0.77,0.89,0.85,0.82,0.75,0.80,0.80,0.94,0.88,0.94,0.87,0.95,0.96,0.88,0.72,0.74,0.76,0.83,0.78,0.84,0.79,0.87,0.91,0.83,0.89,0.98,0.92,1.23,1.34,1.05,1.16,0.99,0.96,1.46,1.57,1.54,1.33,1.05,1.26,1.08,1.37,1.10,0.98,1.03,0.92,1.14,0.86,0.95,0.97,0.90,0.89,0.79,0.84,0.77,0.82,0.76,0.82,0.97,0.89,0.98,0.71,0.72,0.74,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.34,1.51,1.53,1.67,1.72,1.71,1.80,1.77,1.91,1.86,1.98,1.86,1.82,1.95,1.89,1.24,1.10,1.41,0.95,0.99,1.09,1.25,1.37,1.63,1.55,0.96,0.98,1.16,1.05,1.00,1.27,1.23,1.50,1.69,1.46,1.86,1.72,1.87,1.24,1.49,1.44,1.69,1.80,1.59,1.69,1.97,1.82,1.94,1.91,1.92,1.99,1.63,1.50,1.74,1.16,1.33,1.38,1.58,1.60,1.77,1.80,1.48,1.21,1.37,0.90,0.97,0.93,1.05,0.97,1.04,1.21,0.99,0.95,1.14,0.92,1.02,1.34,0.94,0.86,0.90,0.74,0.79,0.75,0.81,0.79,0.84,0.86,0.71,0.71,0.73,0.76,0.73,0.77,0.74,0.80,0.85,0.78,0.81,0.89,0.84,0.97,0.92,0.88,0.79,0.85,0.86,0.98,0.92,1.00,0.93,1.06,1.12,0.95,0.74,0.74,0.78,0.79,0.76,0.82,0.79,0.87,0.93,0.85,0.85,0.94,0.90,1.09,1.27,0.99,1.17,1.05,0.96,1.46,1.71,1.62,1.48,1.20,1.34,1.28,1.57,1.35,0.90,0.94,0.85,0.98,0.81,0.89,0.89,0.83,0.82,0.75,0.78,0.73,0.77,0.72,0.76,0.89,0.83,0.91,0.71,0.70,0.72,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00},
+{1.26,1.39,1.48,1.51,1.64,1.71,1.60,1.58,1.77,1.74,1.91,1.94,1.87,1.97,1.85,1.10,0.97,1.22,0.88,0.92,0.95,1.01,1.11,1.39,1.32,0.88,0.90,0.97,0.96,0.93,1.05,0.99,1.27,1.47,1.20,1.70,1.54,1.76,1.08,1.31,1.33,1.70,1.76,1.55,1.57,1.88,1.85,1.91,1.97,1.99,1.99,1.70,1.65,1.85,1.41,1.54,1.61,1.76,1.80,1.91,1.93,1.52,1.26,1.48,0.92,0.99,0.97,1.18,1.09,1.28,1.39,0.94,0.93,1.05,0.92,1.01,1.31,0.88,0.81,0.86,0.72,0.75,0.74,0.79,0.79,0.86,0.85,0.71,0.73,0.75,0.82,0.77,0.83,0.78,0.85,0.88,0.81,0.88,0.97,0.90,1.18,1.00,0.93,0.86,0.92,0.94,1.14,0.99,1.24,1.03,1.33,1.39,1.11,0.79,0.77,0.84,0.79,0.77,0.84,0.83,0.90,0.98,0.91,0.85,0.92,0.91,1.02,1.26,1.00,1.23,1.19,0.99,1.50,1.84,1.71,1.64,1.38,1.46,1.51,1.76,1.59,0.84,0.88,0.80,0.94,0.79,0.86,0.82,0.77,0.76,0.74,0.74,0.71,0.73,0.70,0.72,0.82,0.77,0.85,0.74,0.70,0.73,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}
+}
diff --git a/apps/plugins/sdl/progs/quake/anorms.h b/apps/plugins/sdl/progs/quake/anorms.h
new file mode 100644
index 0000000..11a9007
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/anorms.h
@@ -0,0 +1,181 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+{-0.525731, 0.000000, 0.850651}, 
+{-0.442863, 0.238856, 0.864188}, 
+{-0.295242, 0.000000, 0.955423}, 
+{-0.309017, 0.500000, 0.809017}, 
+{-0.162460, 0.262866, 0.951056}, 
+{0.000000, 0.000000, 1.000000}, 
+{0.000000, 0.850651, 0.525731}, 
+{-0.147621, 0.716567, 0.681718}, 
+{0.147621, 0.716567, 0.681718}, 
+{0.000000, 0.525731, 0.850651}, 
+{0.309017, 0.500000, 0.809017}, 
+{0.525731, 0.000000, 0.850651}, 
+{0.295242, 0.000000, 0.955423}, 
+{0.442863, 0.238856, 0.864188}, 
+{0.162460, 0.262866, 0.951056}, 
+{-0.681718, 0.147621, 0.716567}, 
+{-0.809017, 0.309017, 0.500000}, 
+{-0.587785, 0.425325, 0.688191}, 
+{-0.850651, 0.525731, 0.000000}, 
+{-0.864188, 0.442863, 0.238856}, 
+{-0.716567, 0.681718, 0.147621}, 
+{-0.688191, 0.587785, 0.425325}, 
+{-0.500000, 0.809017, 0.309017}, 
+{-0.238856, 0.864188, 0.442863}, 
+{-0.425325, 0.688191, 0.587785}, 
+{-0.716567, 0.681718, -0.147621}, 
+{-0.500000, 0.809017, -0.309017}, 
+{-0.525731, 0.850651, 0.000000}, 
+{0.000000, 0.850651, -0.525731}, 
+{-0.238856, 0.864188, -0.442863}, 
+{0.000000, 0.955423, -0.295242}, 
+{-0.262866, 0.951056, -0.162460}, 
+{0.000000, 1.000000, 0.000000}, 
+{0.000000, 0.955423, 0.295242}, 
+{-0.262866, 0.951056, 0.162460}, 
+{0.238856, 0.864188, 0.442863}, 
+{0.262866, 0.951056, 0.162460}, 
+{0.500000, 0.809017, 0.309017}, 
+{0.238856, 0.864188, -0.442863}, 
+{0.262866, 0.951056, -0.162460}, 
+{0.500000, 0.809017, -0.309017}, 
+{0.850651, 0.525731, 0.000000}, 
+{0.716567, 0.681718, 0.147621}, 
+{0.716567, 0.681718, -0.147621}, 
+{0.525731, 0.850651, 0.000000}, 
+{0.425325, 0.688191, 0.587785}, 
+{0.864188, 0.442863, 0.238856}, 
+{0.688191, 0.587785, 0.425325}, 
+{0.809017, 0.309017, 0.500000}, 
+{0.681718, 0.147621, 0.716567}, 
+{0.587785, 0.425325, 0.688191}, 
+{0.955423, 0.295242, 0.000000}, 
+{1.000000, 0.000000, 0.000000}, 
+{0.951056, 0.162460, 0.262866}, 
+{0.850651, -0.525731, 0.000000}, 
+{0.955423, -0.295242, 0.000000}, 
+{0.864188, -0.442863, 0.238856}, 
+{0.951056, -0.162460, 0.262866}, 
+{0.809017, -0.309017, 0.500000}, 
+{0.681718, -0.147621, 0.716567}, 
+{0.850651, 0.000000, 0.525731}, 
+{0.864188, 0.442863, -0.238856}, 
+{0.809017, 0.309017, -0.500000}, 
+{0.951056, 0.162460, -0.262866}, 
+{0.525731, 0.000000, -0.850651}, 
+{0.681718, 0.147621, -0.716567}, 
+{0.681718, -0.147621, -0.716567}, 
+{0.850651, 0.000000, -0.525731}, 
+{0.809017, -0.309017, -0.500000}, 
+{0.864188, -0.442863, -0.238856}, 
+{0.951056, -0.162460, -0.262866}, 
+{0.147621, 0.716567, -0.681718}, 
+{0.309017, 0.500000, -0.809017}, 
+{0.425325, 0.688191, -0.587785}, 
+{0.442863, 0.238856, -0.864188}, 
+{0.587785, 0.425325, -0.688191}, 
+{0.688191, 0.587785, -0.425325}, 
+{-0.147621, 0.716567, -0.681718}, 
+{-0.309017, 0.500000, -0.809017}, 
+{0.000000, 0.525731, -0.850651}, 
+{-0.525731, 0.000000, -0.850651}, 
+{-0.442863, 0.238856, -0.864188}, 
+{-0.295242, 0.000000, -0.955423}, 
+{-0.162460, 0.262866, -0.951056}, 
+{0.000000, 0.000000, -1.000000}, 
+{0.295242, 0.000000, -0.955423}, 
+{0.162460, 0.262866, -0.951056}, 
+{-0.442863, -0.238856, -0.864188}, 
+{-0.309017, -0.500000, -0.809017}, 
+{-0.162460, -0.262866, -0.951056}, 
+{0.000000, -0.850651, -0.525731}, 
+{-0.147621, -0.716567, -0.681718}, 
+{0.147621, -0.716567, -0.681718}, 
+{0.000000, -0.525731, -0.850651}, 
+{0.309017, -0.500000, -0.809017}, 
+{0.442863, -0.238856, -0.864188}, 
+{0.162460, -0.262866, -0.951056}, 
+{0.238856, -0.864188, -0.442863}, 
+{0.500000, -0.809017, -0.309017}, 
+{0.425325, -0.688191, -0.587785}, 
+{0.716567, -0.681718, -0.147621}, 
+{0.688191, -0.587785, -0.425325}, 
+{0.587785, -0.425325, -0.688191}, 
+{0.000000, -0.955423, -0.295242}, 
+{0.000000, -1.000000, 0.000000}, 
+{0.262866, -0.951056, -0.162460}, 
+{0.000000, -0.850651, 0.525731}, 
+{0.000000, -0.955423, 0.295242}, 
+{0.238856, -0.864188, 0.442863}, 
+{0.262866, -0.951056, 0.162460}, 
+{0.500000, -0.809017, 0.309017}, 
+{0.716567, -0.681718, 0.147621}, 
+{0.525731, -0.850651, 0.000000}, 
+{-0.238856, -0.864188, -0.442863}, 
+{-0.500000, -0.809017, -0.309017}, 
+{-0.262866, -0.951056, -0.162460}, 
+{-0.850651, -0.525731, 0.000000}, 
+{-0.716567, -0.681718, -0.147621}, 
+{-0.716567, -0.681718, 0.147621}, 
+{-0.525731, -0.850651, 0.000000}, 
+{-0.500000, -0.809017, 0.309017}, 
+{-0.238856, -0.864188, 0.442863}, 
+{-0.262866, -0.951056, 0.162460}, 
+{-0.864188, -0.442863, 0.238856}, 
+{-0.809017, -0.309017, 0.500000}, 
+{-0.688191, -0.587785, 0.425325}, 
+{-0.681718, -0.147621, 0.716567}, 
+{-0.442863, -0.238856, 0.864188}, 
+{-0.587785, -0.425325, 0.688191}, 
+{-0.309017, -0.500000, 0.809017}, 
+{-0.147621, -0.716567, 0.681718}, 
+{-0.425325, -0.688191, 0.587785}, 
+{-0.162460, -0.262866, 0.951056}, 
+{0.442863, -0.238856, 0.864188}, 
+{0.162460, -0.262866, 0.951056}, 
+{0.309017, -0.500000, 0.809017}, 
+{0.147621, -0.716567, 0.681718}, 
+{0.000000, -0.525731, 0.850651}, 
+{0.425325, -0.688191, 0.587785}, 
+{0.587785, -0.425325, 0.688191}, 
+{0.688191, -0.587785, 0.425325}, 
+{-0.955423, 0.295242, 0.000000}, 
+{-0.951056, 0.162460, 0.262866}, 
+{-1.000000, 0.000000, 0.000000}, 
+{-0.850651, 0.000000, 0.525731}, 
+{-0.955423, -0.295242, 0.000000}, 
+{-0.951056, -0.162460, 0.262866}, 
+{-0.864188, 0.442863, -0.238856}, 
+{-0.951056, 0.162460, -0.262866}, 
+{-0.809017, 0.309017, -0.500000}, 
+{-0.864188, -0.442863, -0.238856}, 
+{-0.951056, -0.162460, -0.262866}, 
+{-0.809017, -0.309017, -0.500000}, 
+{-0.681718, 0.147621, -0.716567}, 
+{-0.681718, -0.147621, -0.716567}, 
+{-0.850651, 0.000000, -0.525731}, 
+{-0.688191, 0.587785, -0.425325}, 
+{-0.587785, 0.425325, -0.688191}, 
+{-0.425325, 0.688191, -0.587785}, 
+{-0.425325, -0.688191, -0.587785}, 
+{-0.587785, -0.425325, -0.688191}, 
+{-0.688191, -0.587785, -0.425325}, 
diff --git a/apps/plugins/sdl/progs/quake/asm_draw.h b/apps/plugins/sdl/progs/quake/asm_draw.h
new file mode 100644
index 0000000..5534ab6
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/asm_draw.h
@@ -0,0 +1,151 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// asm_draw.h
+//
+// Include file for asm drawing routines.
+//
+
+//
+// !!! note that this file must match the corresponding C structures at all
+// times !!!
+//
+
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define	NEAR_CLIP	0.01
+
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define	CYCLE	128
+
+// espan_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define espan_t_u    	0
+#define espan_t_v	    4
+#define espan_t_count   8
+#define espan_t_pnext	12
+#define espan_t_size    16
+
+// sspan_t structure
+// !!! if this is changed, it must be changed in d_local.h too !!!
+#define sspan_t_u    	0
+#define sspan_t_v	    4
+#define sspan_t_count   8
+#define sspan_t_size    12
+
+// spanpackage_t structure
+// !!! if this is changed, it must be changed in d_polyset.c too !!!
+#define spanpackage_t_pdest				0
+#define spanpackage_t_pz				4
+#define spanpackage_t_count				8
+#define spanpackage_t_ptex				12
+#define spanpackage_t_sfrac				16
+#define spanpackage_t_tfrac				20
+#define spanpackage_t_light				24
+#define spanpackage_t_zi				28
+#define spanpackage_t_size				32 
+
+// edge_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define et_u			0
+#define et_u_step		4
+#define et_prev			8
+#define et_next			12
+#define et_surfs		16
+#define et_nextremove	20
+#define et_nearzi		24
+#define et_owner		28
+#define et_size			32
+
+// surf_t structure
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define SURF_T_SHIFT	6
+#define st_next			0
+#define st_prev			4
+#define st_spans		8
+#define st_key			12
+#define st_last_u		16
+#define st_spanstate	20
+#define st_flags		24
+#define st_data			28
+#define st_entity		32
+#define st_nearzi		36
+#define st_insubmodel	40
+#define st_d_ziorigin	44
+#define st_d_zistepu	48
+#define st_d_zistepv	52
+#define st_pad			56
+#define st_size			64
+
+// clipplane_t structure
+// !!! if this is changed, it must be changed in r_local.h too !!!
+#define cp_normal		0
+#define cp_dist			12
+#define cp_next			16
+#define cp_leftedge		20
+#define cp_rightedge	21
+#define cp_reserved		22
+#define cp_size			24
+
+// medge_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define me_v				0
+#define me_cachededgeoffset	4
+#define me_size				8
+
+// mvertex_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define mv_position		0
+#define mv_size			12
+
+// refdef_t structure
+// !!! if this is changed, it must be changed in render.h too !!!
+#define rd_vrect					0
+#define rd_aliasvrect				20
+#define rd_vrectright				40
+#define rd_vrectbottom				44
+#define rd_aliasvrectright			48
+#define rd_aliasvrectbottom			52
+#define rd_vrectrightedge			56
+#define rd_fvrectx					60
+#define rd_fvrecty					64
+#define rd_fvrectx_adj				68
+#define rd_fvrecty_adj				72
+#define rd_vrect_x_adj_shift20		76
+#define rd_vrectright_adj_shift20	80
+#define rd_fvrectright_adj			84
+#define rd_fvrectbottom_adj			88
+#define rd_fvrectright				92
+#define rd_fvrectbottom				96
+#define rd_horizontalFieldOfView	100
+#define rd_xOrigin					104
+#define rd_yOrigin					108
+#define rd_vieworg					112
+#define rd_viewangles				124
+#define rd_ambientlight				136
+#define rd_size						140
+
+// mtriangle_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define mtri_facesfront		0
+#define mtri_vertindex		4
+#define mtri_size			16	// !!! if this changes, array indexing in !!!
+								// !!! d_polysa.s must be changed to match !!!
+#define mtri_shift			4
+
diff --git a/apps/plugins/sdl/progs/quake/asm_i386.h b/apps/plugins/sdl/progs/quake/asm_i386.h
new file mode 100644
index 0000000..03dc2aa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/asm_i386.h
@@ -0,0 +1,97 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 __ASM_I386__
+#define __ASM_I386__
+
+#ifdef ELF
+#define C(label) label
+#else
+#define C(label) _##label
+#endif
+
+//
+// !!! note that this file must match the corresponding C structures at all
+// times !!!
+//
+
+// plane_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+// !!! if the size of this is changed, the array lookup in SV_HullPointContents
+//     must be changed too !!!
+#define pl_normal	0
+#define pl_dist		12
+#define pl_type		16
+#define pl_signbits	17
+#define pl_pad		18
+#define pl_size		20
+
+// hull_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define	hu_clipnodes		0
+#define	hu_planes			4
+#define	hu_firstclipnode	8
+#define	hu_lastclipnode		12
+#define	hu_clip_mins		16
+#define	hu_clip_maxs		28
+#define hu_size  			40
+
+// dnode_t structure
+// !!! if this is changed, it must be changed in bspfile.h too !!!
+#define	nd_planenum		0
+#define	nd_children		4
+#define	nd_mins			8
+#define	nd_maxs			20
+#define	nd_firstface	32
+#define	nd_numfaces		36
+#define nd_size			40
+
+// sfxcache_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define sfxc_length		0
+#define sfxc_loopstart	4
+#define sfxc_speed		8
+#define sfxc_width		12
+#define sfxc_stereo		16
+#define sfxc_data		20
+
+// channel_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define ch_sfx			0
+#define ch_leftvol		4
+#define ch_rightvol		8
+#define ch_end			12
+#define ch_pos			16
+#define ch_looping		20
+#define ch_entnum		24
+#define ch_entchannel	28
+#define ch_origin		32
+#define ch_dist_mult	44
+#define ch_master_vol	48
+#define ch_size			52
+
+// portable_samplepair_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define psp_left		0
+#define psp_right		4
+#define psp_size		8
+
+#endif
+
diff --git a/apps/plugins/sdl/progs/quake/block16.h b/apps/plugins/sdl/progs/quake/block16.h
new file mode 100644
index 0000000..96f222e
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/block16.h
@@ -0,0 +1,142 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+LEnter16_16:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch0:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch1:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch2:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch3:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch4:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch5:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch6:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch7:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+LEnter8_16:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch8:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch9:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch10:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch11:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+LEnter4_16:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch12:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch13:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
+
+LEnter2_16:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movw	0x12345678(,%eax,2),%ax
+LBPatch14:
+	addl	%ebp,%edx
+	movw	%ax,(%edi)
+	movw	0x12345678(,%ecx,2),%cx
+LBPatch15:
+	movw	%cx,2(%edi)
+	addl	$0x4,%edi
diff --git a/apps/plugins/sdl/progs/quake/block8.h b/apps/plugins/sdl/progs/quake/block8.h
new file mode 100644
index 0000000..eb9b03c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/block8.h
@@ -0,0 +1,143 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+LEnter16_8:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch0:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch1:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch2:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch3:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch4:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch5:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch6:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch7:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+LEnter8_8:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch8:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch9:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch10:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch11:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+LEnter4_8:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch12:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch13:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
+LEnter2_8:
+	movb	(%esi),%al
+	movb	(%esi,%ebx,),%cl
+	movb	%dh,%ah
+	addl	%ebp,%edx
+	movb	%dh,%ch
+	leal	(%esi,%ebx,2),%esi
+	movb	0x12345678(%eax),%al
+LBPatch14:
+	addl	%ebp,%edx
+	movb	%al,(%edi)
+	movb	0x12345678(%ecx),%cl
+LBPatch15:
+	movb	%cl,1(%edi)
+	addl	$0x2,%edi
+
diff --git a/apps/plugins/sdl/progs/quake/bspfile.h b/apps/plugins/sdl/progs/quake/bspfile.h
new file mode 100644
index 0000000..3089026
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/bspfile.h
@@ -0,0 +1,324 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+
+// upper design bounds
+
+#define	MAX_MAP_HULLS		4
+
+#define	MAX_MAP_MODELS		256
+#define	MAX_MAP_BRUSHES		4096
+#define	MAX_MAP_ENTITIES	1024
+#define	MAX_MAP_ENTSTRING	65536
+
+#define	MAX_MAP_PLANES		32767
+#define	MAX_MAP_NODES		32767		// because negative shorts are contents
+#define	MAX_MAP_CLIPNODES	32767		//
+#define	MAX_MAP_LEAFS		8192
+#define	MAX_MAP_VERTS		65535
+#define	MAX_MAP_FACES		65535
+#define	MAX_MAP_MARKSURFACES 65535
+#define	MAX_MAP_TEXINFO		4096
+#define	MAX_MAP_EDGES		256000
+#define	MAX_MAP_SURFEDGES	512000
+#define	MAX_MAP_TEXTURES	512
+#define	MAX_MAP_MIPTEX		0x200000
+#define	MAX_MAP_LIGHTING	0x100000
+#define	MAX_MAP_VISIBILITY	0x100000
+
+#define	MAX_MAP_PORTALS		65536
+
+// key / value pair sizes
+
+#define	MAX_KEY		32
+#define	MAX_VALUE	1024
+
+//=============================================================================
+
+
+#define BSPVERSION	29
+#define	TOOLVERSION	2
+
+typedef struct
+{
+	int		fileofs, filelen;
+} lump_t;
+
+#define	LUMP_ENTITIES	0
+#define	LUMP_PLANES		1
+#define	LUMP_TEXTURES	2
+#define	LUMP_VERTEXES	3
+#define	LUMP_VISIBILITY	4
+#define	LUMP_NODES		5
+#define	LUMP_TEXINFO	6
+#define	LUMP_FACES		7
+#define	LUMP_LIGHTING	8
+#define	LUMP_CLIPNODES	9
+#define	LUMP_LEAFS		10
+#define	LUMP_MARKSURFACES 11
+#define	LUMP_EDGES		12
+#define	LUMP_SURFEDGES	13
+#define	LUMP_MODELS		14
+
+#define	HEADER_LUMPS	15
+
+typedef struct
+{
+	float		mins[3], maxs[3];
+	float		origin[3];
+	int			headnode[MAX_MAP_HULLS];
+	int			visleafs;		// not including the solid leaf 0
+	int			firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+	int			version;	
+	lump_t		lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+	int			nummiptex;
+	int			dataofs[4];		// [nummiptex]
+} dmiptexlump_t;
+
+#define	MIPLEVELS	4
+typedef struct miptex_s
+{
+	char		name[16];
+	unsigned	width, height;
+	unsigned	offsets[MIPLEVELS];		// four mip maps stored
+} miptex_t;
+
+
+typedef struct
+{
+	float	point[3];
+} dvertex_t;
+
+
+// 0-2 are axial planes
+#define	PLANE_X			0
+#define	PLANE_Y			1
+#define	PLANE_Z			2
+
+// 3-5 are non-axial planes snapped to the nearest
+#define	PLANE_ANYX		3
+#define	PLANE_ANYY		4
+#define	PLANE_ANYZ		5
+
+typedef struct
+{
+	float	normal[3];
+	float	dist;
+	int		type;		// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+
+#define	CONTENTS_EMPTY		-1
+#define	CONTENTS_SOLID		-2
+#define	CONTENTS_WATER		-3
+#define	CONTENTS_SLIME		-4
+#define	CONTENTS_LAVA		-5
+#define	CONTENTS_SKY		-6
+#define	CONTENTS_ORIGIN		-7		// removed at csg time
+#define	CONTENTS_CLIP		-8		// changed to contents_solid
+
+#define	CONTENTS_CURRENT_0		-9
+#define	CONTENTS_CURRENT_90		-10
+#define	CONTENTS_CURRENT_180	-11
+#define	CONTENTS_CURRENT_270	-12
+#define	CONTENTS_CURRENT_UP		-13
+#define	CONTENTS_CURRENT_DOWN	-14
+
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+	int			planenum;
+	short		children[2];	// negative numbers are -(leafs+1), not nodes
+	short		mins[3];		// for sphere culling
+	short		maxs[3];
+	unsigned short	firstface;
+	unsigned short	numfaces;	// counting both sides
+} dnode_t;
+
+typedef struct
+{
+	int			planenum;
+	short		children[2];	// negative numbers are contents
+} dclipnode_t;
+
+
+typedef struct texinfo_s
+{
+	float		vecs[2][4];		// [s/t][xyz offset]
+	int			miptex;
+	int			flags;
+} texinfo_t;
+#define	TEX_SPECIAL		1		// sky or slime, no lightmap or 256 subdivision
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+	unsigned short	v[2];		// vertex numbers
+} dedge_t;
+
+#define	MAXLIGHTMAPS	4
+typedef struct
+{
+	short		planenum;
+	short		side;
+
+	int			firstedge;		// we must support > 64k edges
+	short		numedges;	
+	short		texinfo;
+
+// lighting info
+	byte		styles[MAXLIGHTMAPS];
+	int			lightofs;		// start of [numstyles*surfsize] samples
+} dface_t;
+
+
+
+#define	AMBIENT_WATER	0
+#define	AMBIENT_SKY		1
+#define	AMBIENT_SLIME	2
+#define	AMBIENT_LAVA	3
+
+#define	NUM_AMBIENTS			4		// automatic ambient sounds
+
+// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
+// all other leafs need visibility info
+typedef struct
+{
+	int			contents;
+	int			visofs;				// -1 = no visibility info
+
+	short		mins[3];			// for frustum culling
+	short		maxs[3];
+
+	unsigned short		firstmarksurface;
+	unsigned short		nummarksurfaces;
+
+	byte		ambient_level[NUM_AMBIENTS];
+} dleaf_t;
+
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+#define	ANGLE_UP	-1
+#define	ANGLE_DOWN	-2
+
+
+// the utilities get to be lazy and just use large static arrays
+
+extern	int			nummodels;
+extern	dmodel_t	dmodels[MAX_MAP_MODELS];
+
+extern	int			visdatasize;
+extern	byte		dvisdata[MAX_MAP_VISIBILITY];
+
+extern	int			lightdatasize;
+extern	byte		dlightdata[MAX_MAP_LIGHTING];
+
+extern	int			texdatasize;
+extern	byte		dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern	int			entdatasize;
+extern	char		dentdata[MAX_MAP_ENTSTRING];
+
+extern	int			numleafs;
+extern	dleaf_t		dleafs[MAX_MAP_LEAFS];
+
+extern	int			numplanes;
+extern	dplane_t	dplanes[MAX_MAP_PLANES];
+
+extern	int			numvertexes;
+extern	dvertex_t	dvertexes[MAX_MAP_VERTS];
+
+extern	int			numnodes;
+extern	dnode_t		dnodes[MAX_MAP_NODES];
+
+extern	int			numtexinfo;
+extern	texinfo_t	texinfo[MAX_MAP_TEXINFO];
+
+extern	int			numfaces;
+extern	dface_t		dfaces[MAX_MAP_FACES];
+
+extern	int			numclipnodes;
+extern	dclipnode_t	dclipnodes[MAX_MAP_CLIPNODES];
+
+extern	int			numedges;
+extern	dedge_t		dedges[MAX_MAP_EDGES];
+
+extern	int			nummarksurfaces;
+extern	unsigned short	dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+extern	int			numsurfedges;
+extern	int			dsurfedges[MAX_MAP_SURFEDGES];
+
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void	LoadBSPFile (char *filename);
+void	WriteBSPFile (char *filename);
+void	PrintBSPFileSizes (void);
+
+//===============
+
+
+typedef struct epair_s
+{
+	struct epair_s	*next;
+	char	*key;
+	char	*value;
+} epair_t;
+
+typedef struct
+{
+	vec3_t		origin;
+	int			firstbrush;
+	int			numbrushes;
+	epair_t		*epairs;
+} entity_t;
+
+extern	int			num_entities;
+extern	entity_t	entities[MAX_MAP_ENTITIES];
+
+void	ParseEntities (void);
+void	UnparseEntities (void);
+
+void 	SetKeyValue (entity_t *ent, char *key, char *value);
+char 	*ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+
+vec_t	FloatForKey (entity_t *ent, char *key);
+void 	GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+epair_t *ParseEpair (void);
+
+#endif
diff --git a/apps/plugins/sdl/progs/quake/cd_linux.c b/apps/plugins/sdl/progs/quake/cd_linux.c
new file mode 100644
index 0000000..0e23d96
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cd_linux.c
@@ -0,0 +1,416 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <linux/cdrom.h>
+
+#include "quakedef.h"
+
+static qboolean cdValid = false;
+static qboolean	playing = false;
+static qboolean	wasPlaying = false;
+static qboolean	initialized = false;
+static qboolean	enabled = true;
+static qboolean playLooping = false;
+static float	cdvolume;
+static byte 	remap[100];
+static byte		playTrack;
+static byte		maxTrack;
+
+static int cdfile = -1;
+static char cd_dev[64] = "/dev/cdrom";
+
+static void CDAudio_Eject(void)
+{
+	if (cdfile == -1 || !enabled)
+		return; // no cd init'd
+
+	if ( ioctl(cdfile, CDROMEJECT) == -1 ) 
+		Con_DPrintf("ioctl cdromeject failed\n");
+}
+
+
+static void CDAudio_CloseDoor(void)
+{
+	if (cdfile == -1 || !enabled)
+		return; // no cd init'd
+
+	if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) 
+		Con_DPrintf("ioctl cdromclosetray failed\n");
+}
+
+static int CDAudio_GetAudioDiskInfo(void)
+{
+	struct cdrom_tochdr tochdr;
+
+	cdValid = false;
+
+	if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) 
+    {
+      Con_DPrintf("ioctl cdromreadtochdr failed\n");
+	  return -1;
+    }
+
+	if (tochdr.cdth_trk0 < 1)
+	{
+		Con_DPrintf("CDAudio: no music tracks\n");
+		return -1;
+	}
+
+	cdValid = true;
+	maxTrack = tochdr.cdth_trk1;
+
+	return 0;
+}
+
+
+void CDAudio_Play(byte track, qboolean looping)
+{
+	struct cdrom_tocentry entry;
+	struct cdrom_ti ti;
+
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!cdValid)
+	{
+		CDAudio_GetAudioDiskInfo();
+		if (!cdValid)
+			return;
+	}
+
+	track = remap[track];
+
+	if (track < 1 || track > maxTrack)
+	{
+		Con_DPrintf("CDAudio: Bad track number %u.\n", track);
+		return;
+	}
+
+	// don't try to play a non-audio track
+	entry.cdte_track = track;
+	entry.cdte_format = CDROM_MSF;
+    if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
+	{
+		Con_DPrintf("ioctl cdromreadtocentry failed\n");
+		return;
+	}
+	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
+	{
+		Con_Printf("CDAudio: track %i is not audio\n", track);
+		return;
+	}
+
+	if (playing)
+	{
+		if (playTrack == track)
+			return;
+		CDAudio_Stop();
+	}
+
+	ti.cdti_trk0 = track;
+	ti.cdti_trk1 = track;
+	ti.cdti_ind0 = 1;
+	ti.cdti_ind1 = 99;
+
+	if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) 
+    {
+		Con_DPrintf("ioctl cdromplaytrkind failed\n");
+		return;
+    }
+
+	if ( ioctl(cdfile, CDROMRESUME) == -1 ) 
+		Con_DPrintf("ioctl cdromresume failed\n");
+
+	playLooping = looping;
+	playTrack = track;
+	playing = true;
+
+	if (cdvolume == 0.0)
+		CDAudio_Pause ();
+}
+
+
+void CDAudio_Stop(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!playing)
+		return;
+
+	if ( ioctl(cdfile, CDROMSTOP) == -1 )
+		Con_DPrintf("ioctl cdromstop failed (%d)\n", errno);
+
+	wasPlaying = false;
+	playing = false;
+}
+
+void CDAudio_Pause(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+
+	if (!playing)
+		return;
+
+	if ( ioctl(cdfile, CDROMPAUSE) == -1 ) 
+		Con_DPrintf("ioctl cdrompause failed\n");
+
+	wasPlaying = playing;
+	playing = false;
+}
+
+
+void CDAudio_Resume(void)
+{
+	if (cdfile == -1 || !enabled)
+		return;
+	
+	if (!cdValid)
+		return;
+
+	if (!wasPlaying)
+		return;
+	
+	if ( ioctl(cdfile, CDROMRESUME) == -1 ) 
+		Con_DPrintf("ioctl cdromresume failed\n");
+	playing = true;
+}
+
+static void CD_f (void)
+{
+	char	*command;
+	int		ret;
+	int		n;
+
+	if (Cmd_Argc() < 2)
+		return;
+
+	command = Cmd_Argv (1);
+
+	if (Q_strcasecmp(command, "on") == 0)
+	{
+		enabled = true;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "off") == 0)
+	{
+		if (playing)
+			CDAudio_Stop();
+		enabled = false;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "reset") == 0)
+	{
+		enabled = true;
+		if (playing)
+			CDAudio_Stop();
+		for (n = 0; n < 100; n++)
+			remap[n] = n;
+		CDAudio_GetAudioDiskInfo();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "remap") == 0)
+	{
+		ret = Cmd_Argc() - 2;
+		if (ret <= 0)
+		{
+			for (n = 1; n < 100; n++)
+				if (remap[n] != n)
+					Con_Printf("  %u -> %u\n", n, remap[n]);
+			return;
+		}
+		for (n = 1; n <= ret; n++)
+			remap[n] = Q_atoi(Cmd_Argv (n+1));
+		return;
+	}
+
+	if (Q_strcasecmp(command, "close") == 0)
+	{
+		CDAudio_CloseDoor();
+		return;
+	}
+
+	if (!cdValid)
+	{
+		CDAudio_GetAudioDiskInfo();
+		if (!cdValid)
+		{
+			Con_Printf("No CD in player.\n");
+			return;
+		}
+	}
+
+	if (Q_strcasecmp(command, "play") == 0)
+	{
+		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false);
+		return;
+	}
+
+	if (Q_strcasecmp(command, "loop") == 0)
+	{
+		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true);
+		return;
+	}
+
+	if (Q_strcasecmp(command, "stop") == 0)
+	{
+		CDAudio_Stop();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "pause") == 0)
+	{
+		CDAudio_Pause();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "resume") == 0)
+	{
+		CDAudio_Resume();
+		return;
+	}
+
+	if (Q_strcasecmp(command, "eject") == 0)
+	{
+		if (playing)
+			CDAudio_Stop();
+		CDAudio_Eject();
+		cdValid = false;
+		return;
+	}
+
+	if (Q_strcasecmp(command, "info") == 0)
+	{
+		Con_Printf("%u tracks\n", maxTrack);
+		if (playing)
+			Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+		else if (wasPlaying)
+			Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
+		Con_Printf("Volume is %f\n", cdvolume);
+		return;
+	}
+}
+
+void CDAudio_Update(void)
+{
+	struct cdrom_subchnl subchnl;
+	static time_t lastchk;
+
+	if (!enabled)
+		return;
+
+	if (bgmvolume.value != cdvolume)
+	{
+		if (cdvolume)
+		{
+			Cvar_SetValue ("bgmvolume", 0.0);
+			cdvolume = bgmvolume.value;
+			CDAudio_Pause ();
+		}
+		else
+		{
+			Cvar_SetValue ("bgmvolume", 1.0);
+			cdvolume = bgmvolume.value;
+			CDAudio_Resume ();
+		}
+	}
+
+	if (playing && lastchk < time(NULL)) {
+		lastchk = time(NULL) + 2; //two seconds between chks
+		subchnl.cdsc_format = CDROM_MSF;
+		if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) {
+			Con_DPrintf("ioctl cdromsubchnl failed\n");
+			playing = false;
+			return;
+		}
+		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
+			subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
+			playing = false;
+			if (playLooping)
+				CDAudio_Play(playTrack, true);
+		}
+	}
+}
+
+int CDAudio_Init(void)
+{
+	int i;
+
+	if (cls.state == ca_dedicated)
+		return -1;
+
+	if (COM_CheckParm("-nocdaudio"))
+		return -1;
+
+	if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) {
+		strncpy(cd_dev, com_argv[i + 1], sizeof(cd_dev));
+		cd_dev[sizeof(cd_dev) - 1] = 0;
+	}
+
+	if ((cdfile = open(cd_dev, O_RDONLY)) == -1) {
+		Con_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, errno);
+		cdfile = -1;
+		return -1;
+	}
+
+	for (i = 0; i < 100; i++)
+		remap[i] = i;
+	initialized = true;
+	enabled = true;
+
+	if (CDAudio_GetAudioDiskInfo())
+	{
+		Con_Printf("CDAudio_Init: No CD in player.\n");
+		cdValid = false;
+	}
+
+	Cmd_AddCommand ("cd", CD_f);
+
+	Con_Printf("CD Audio Initialized\n");
+
+	return 0;
+}
+
+
+void CDAudio_Shutdown(void)
+{
+	if (!initialized)
+		return;
+	CDAudio_Stop();
+	close(cdfile);
+	cdfile = -1;
+}
diff --git a/apps/plugins/sdl/progs/quake/cd_null.c b/apps/plugins/sdl/progs/quake/cd_null.c
new file mode 100644
index 0000000..d5eeec8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cd_null.c
@@ -0,0 +1,55 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+void CDAudio_Play(byte track, qboolean looping)
+{
+}
+
+
+void CDAudio_Stop(void)
+{
+}
+
+
+void CDAudio_Pause(void)
+{
+}
+
+
+void CDAudio_Resume(void)
+{
+}
+
+
+void CDAudio_Update(void)
+{
+}
+
+
+int CDAudio_Init(void)
+{
+	return 0;
+}
+
+
+void CDAudio_Shutdown(void)
+{
+}
\ No newline at end of file
diff --git a/apps/plugins/sdl/progs/quake/cd_sdl.c b/apps/plugins/sdl/progs/quake/cd_sdl.c
new file mode 100644
index 0000000..46b2aa2
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cd_sdl.c
@@ -0,0 +1,223 @@
+/*
+  Some of this may not work. I'm not overly familiar with SDL, I just sort
+  of podged this together from the SDL headers, and the other cd-rom code.
+
+  Mark Baker <homer1@together.net>
+*/
+
+#include <SDL.h>
+
+#include "quakedef.h"
+
+static qboolean cdValid = false;
+static qboolean initialized = false;
+static qboolean enabled = true;
+static qboolean playLooping = false;
+static SDL_CD *cd_id;
+static float cdvolume = 1.0;
+
+static void CD_f();
+
+static void CDAudio_Eject()
+{
+	if(!cd_id || !enabled) return;
+
+	if(SDL_CDEject(cd_id))
+		Con_DPrintf("Unable to eject CD-ROM tray.\n");
+}
+
+void CDAudio_Play(byte track, qboolean looping)
+{
+	CDstatus cd_stat;
+	if(!cd_id || !enabled) return;
+	
+	if(!cdValid)
+	{
+		if(!CD_INDRIVE(cd_stat=SDL_CDStatus(cd_id)) ||(!cd_id->numtracks)) return;
+		cdValid = true;
+	}
+
+	if((track < 1) || (track >= cd_id->numtracks))
+	{
+		Con_DPrintf("CDAudio: Bad track number: %d\n",track);
+		return;
+	}
+	track--; /* Convert track from person to SDL value */
+	if(cd_stat == CD_PLAYING)
+	{
+		if(cd_id->cur_track == track) return;
+		CDAudio_Stop();
+	}
+
+	if(SDL_CDPlay(cd_id,cd_id->track[track].offset,
+			  cd_id->track[track].length))
+	{
+		Con_DPrintf("CDAudio_Play: Unable to play track: %d\n",track+1);
+		return;
+	}
+	playLooping = looping;
+}
+
+
+void CDAudio_Stop()
+{
+	int cdstate;
+	if(!cd_id || !enabled) return;
+	cdstate = SDL_CDStatus(cd_id);
+	if((cdstate != CD_PLAYING) && (cdstate != CD_PAUSED)) return;
+
+	if(SDL_CDStop(cd_id))
+		Con_DPrintf("CDAudio_Stop: Failed to stop track.\n");
+}
+
+void CDAudio_Pause()
+{
+	if(!cd_id || !enabled) return;
+	if(SDL_CDStatus(cd_id) != CD_PLAYING) return;
+
+	if(SDL_CDPause(cd_id))
+		Con_DPrintf("CDAudio_Pause: Failed to pause track.\n");
+}
+
+
+void CDAudio_Resume()
+{
+	if(!cd_id || !enabled) return;
+	if(SDL_CDStatus(cd_id) != CD_PAUSED) return;
+
+	if(SDL_CDResume(cd_id))
+		Con_DPrintf("CDAudio_Resume: Failed tp resume track.\n");
+}
+
+void CDAudio_Update()
+{
+	if(!cd_id || !enabled) return;
+	if(bgmvolume.value != cdvolume)
+	{
+		if(cdvolume)
+		{
+			Cvar_SetValue("bgmvolume",0.0);
+			CDAudio_Pause();
+		}
+		else
+		{
+			Cvar_SetValue("bgmvolume",1.0);
+			CDAudio_Resume();
+		}
+		cdvolume = bgmvolume.value;
+		return;
+	}
+	if(playLooping && (SDL_CDStatus(cd_id) != CD_PLAYING)
+		 && (SDL_CDStatus(cd_id) != CD_PAUSED))
+		CDAudio_Play(cd_id->cur_track+1,true);
+}
+
+int CDAudio_Init()
+{
+	if((cls.state == ca_dedicated) || COM_CheckParm("-nocdaudio"))
+		return -1;
+		
+	cd_id = SDL_CDOpen(0);
+	if(!cd_id)
+	{
+		Con_Printf("CDAudio_Init: Unable to open default CD-ROM drive: %s\n",
+			SDL_GetError());
+		return -1;
+	}
+	
+	initialized = true;
+	enabled = true;
+	cdValid = true;
+	
+	if(!CD_INDRIVE(SDL_CDStatus(cd_id)))
+	{
+		Con_Printf("CDAudio_Init: No CD in drive.\n");
+		cdValid = false;
+	}
+	if(!cd_id->numtracks)
+	{
+		Con_Printf("CDAudio_Init: CD contains no audio tracks.\n");
+		cdValid = false;
+	}
+	Cmd_AddCommand("cd",CD_f);
+	Con_Printf("CD Audio Initialized.\n");
+	return 0;
+}
+
+
+void CDAudio_Shutdown()
+{
+	if(!cd_id) return;
+	CDAudio_Stop();
+	SDL_CDClose(cd_id);
+	cd_id = NULL;
+}
+
+static void CD_f()
+{
+	char *command;
+	int cdstate;
+	if(Cmd_Argc() < 2) return;
+
+	command = Cmd_Argv(1);
+	if(!Q_strcasecmp(command,"on"))
+	{
+		enabled = true;
+	}
+	if(!Q_strcasecmp(command,"off"))
+	{
+		if(!cd_id) return;
+		cdstate = SDL_CDStatus(cd_id);
+		if((cdstate == CD_PLAYING) || (cdstate == CD_PAUSED))
+			CDAudio_Stop();
+		enabled = false;
+		return;
+	}
+	if(!Q_strcasecmp(command,"play"))
+	{
+		CDAudio_Play(Q_atoi(Cmd_Argv(2)),false);
+		return;
+	}
+	if(!Q_strcasecmp(command,"loop"))
+	{
+		CDAudio_Play(Q_atoi(Cmd_Argv(2)),true);
+		return;
+	}
+	if(!Q_strcasecmp(command,"stop"))
+	{
+		CDAudio_Stop();
+		return;
+	}
+	if(!Q_strcasecmp(command,"pause"))
+	{
+		CDAudio_Pause();
+		return;
+	}
+	if(!Q_strcasecmp(command,"resume"))
+	{
+		CDAudio_Resume();
+		return;
+	}
+	if(!Q_strcasecmp(command,"eject"))
+	{
+		CDAudio_Eject();
+		return;
+	}
+	if(!Q_strcasecmp(command,"info"))
+	{
+		if(!cd_id) return;
+		cdstate = SDL_CDStatus(cd_id);
+		Con_Printf("%d tracks\n",cd_id->numtracks);
+		if(cdstate == CD_PLAYING)
+			Con_Printf("Currently %s track %d\n",
+				playLooping ? "looping" : "playing",
+				cd_id->cur_track+1);
+		else
+		if(cdstate == CD_PAUSED)
+			Con_Printf("Paused %s track %d\n",
+				playLooping ? "looping" : "playing",
+				cd_id->cur_track+1);
+		return;
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cdaudio.h b/apps/plugins/sdl/progs/quake/cdaudio.h
new file mode 100644
index 0000000..80e975b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cdaudio.h
@@ -0,0 +1,27 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+int CDAudio_Init(void);
+void CDAudio_Play(byte track, qboolean looping);
+void CDAudio_Stop(void);
+void CDAudio_Pause(void);
+void CDAudio_Resume(void);
+void CDAudio_Shutdown(void);
+void CDAudio_Update(void);
diff --git a/apps/plugins/sdl/progs/quake/chase.c b/apps/plugins/sdl/progs/quake/chase.c
new file mode 100644
index 0000000..23061e3
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/chase.c
@@ -0,0 +1,92 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// chase.c -- chase camera code
+
+#include "quakedef.h"
+
+cvar_t	chase_back = {"chase_back", "100"};
+cvar_t	chase_up = {"chase_up", "16"};
+cvar_t	chase_right = {"chase_right", "0"};
+cvar_t	chase_active = {"chase_active", "0"};
+
+vec3_t	chase_pos;
+vec3_t	chase_angles;
+
+vec3_t	chase_dest;
+vec3_t	chase_dest_angles;
+
+
+void Chase_Init (void)
+{
+	Cvar_RegisterVariable (&chase_back);
+	Cvar_RegisterVariable (&chase_up);
+	Cvar_RegisterVariable (&chase_right);
+	Cvar_RegisterVariable (&chase_active);
+}
+
+void Chase_Reset (void)
+{
+	// for respawning and teleporting
+//	start position 12 units behind head
+}
+
+void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
+{
+	trace_t	trace;
+
+	memset (&trace, 0, sizeof(trace));
+	SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
+
+	VectorCopy (trace.endpos, impact);
+}
+
+void Chase_Update (void)
+{
+	int		i;
+	float	dist;
+	vec3_t	forward, up, right;
+	vec3_t	dest, stop;
+
+
+	// if can't see player, reset
+	AngleVectors (cl.viewangles, forward, right, up);
+
+	// calc exact destination
+	for (i=0 ; i<3 ; i++)
+		chase_dest[i] = r_refdef.vieworg[i] 
+		- forward[i]*chase_back.value
+		- right[i]*chase_right.value;
+	chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
+
+	// find the spot the player is looking at
+	VectorMA (r_refdef.vieworg, 4096, forward, dest);
+	TraceLine (r_refdef.vieworg, dest, stop);
+
+	// calculate pitch to look at the same spot from camera
+	VectorSubtract (stop, r_refdef.vieworg, stop);
+	dist = DotProduct (stop, forward);
+	if (dist < 1)
+		dist = 1;
+	r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180;
+
+	// move towards destination
+	VectorCopy (chase_dest, r_refdef.vieworg);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cl_demo.c b/apps/plugins/sdl/progs/quake/cl_demo.c
new file mode 100644
index 0000000..45ddef31
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cl_demo.c
@@ -0,0 +1,367 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+void CL_FinishTimeDemo (void);
+
+/*
+==============================================================================
+
+DEMO CODE
+
+When a demo is playing back, all NET_SendMessages are skipped, and
+NET_GetMessages are read from the demo file.
+
+Whenever cl.time gets past the last received message, another message is
+read from the demo file.
+==============================================================================
+*/
+
+/*
+==============
+CL_StopPlayback
+
+Called when a demo file runs out, or the user starts a game
+==============
+*/
+void CL_StopPlayback (void)
+{
+	if (!cls.demoplayback)
+		return;
+
+	fclose (cls.demofile);
+	cls.demoplayback = false;
+	cls.demofile = NULL;
+	cls.state = ca_disconnected;
+
+	if (cls.timedemo)
+		CL_FinishTimeDemo ();
+}
+
+/*
+====================
+CL_WriteDemoMessage
+
+Dumps the current net message, prefixed by the length and view angles
+====================
+*/
+void CL_WriteDemoMessage (void)
+{
+	int		len;
+	int		i;
+	float	f;
+
+	len = LittleLong (net_message.cursize);
+	fwrite (&len, 4, 1, cls.demofile);
+	for (i=0 ; i<3 ; i++)
+	{
+		f = LittleFloat (cl.viewangles[i]);
+		fwrite (&f, 4, 1, cls.demofile);
+	}
+	fwrite (net_message.data, net_message.cursize, 1, cls.demofile);
+	fflush (cls.demofile);
+}
+
+/*
+====================
+CL_GetMessage
+
+Handles recording and playback of demos, on top of NET_ code
+====================
+*/
+int CL_GetMessage (void)
+{
+	int		r, i;
+	float	f;
+	
+	if	(cls.demoplayback)
+	{
+	// decide if it is time to grab the next message		
+		if (cls.signon == SIGNONS)	// allways grab until fully connected
+		{
+			if (cls.timedemo)
+			{
+				if (host_framecount == cls.td_lastframe)
+					return 0;		// allready read this frame's message
+				cls.td_lastframe = host_framecount;
+			// if this is the second frame, grab the real td_starttime
+			// so the bogus time on the first frame doesn't count
+				if (host_framecount == cls.td_startframe + 1)
+					cls.td_starttime = realtime;
+			}
+			else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0])
+			{
+					return 0;		// don't need another message yet
+			}
+		}
+		
+	// get the next message
+		fread (&net_message.cursize, 4, 1, cls.demofile);
+		VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
+		for (i=0 ; i<3 ; i++)
+		{
+			r = fread (&f, 4, 1, cls.demofile);
+			cl.mviewangles[0][i] = LittleFloat (f);
+		}
+		
+		net_message.cursize = LittleLong (net_message.cursize);
+		if (net_message.cursize > MAX_MSGLEN)
+			Sys_Error ("Demo message > MAX_MSGLEN");
+		r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
+		if (r != 1)
+		{
+			CL_StopPlayback ();
+			return 0;
+		}
+	
+		return 1;
+	}
+
+	while (1)
+	{
+		r = NET_GetMessage (cls.netcon);
+		
+		if (r != 1 && r != 2)
+			return r;
+	
+	// discard nop keepalive message
+		if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
+			Con_Printf ("<-- server to client keepalive\n");
+		else
+			break;
+	}
+
+	if (cls.demorecording)
+		CL_WriteDemoMessage ();
+	
+	return r;
+}
+
+
+/*
+====================
+CL_Stop_f
+
+stop recording a demo
+====================
+*/
+void CL_Stop_f (void)
+{
+	if (cmd_source != src_command)
+		return;
+
+	if (!cls.demorecording)
+	{
+		Con_Printf ("Not recording a demo.\n");
+		return;
+	}
+
+// write a disconnect message to the demo file
+	SZ_Clear (&net_message);
+	MSG_WriteByte (&net_message, svc_disconnect);
+	CL_WriteDemoMessage ();
+
+// finish up
+	fclose (cls.demofile);
+	cls.demofile = NULL;
+	cls.demorecording = false;
+	Con_Printf ("Completed demo\n");
+}
+
+/*
+====================
+CL_Record_f
+
+record <demoname> <map> [cd track]
+====================
+*/
+void CL_Record_f (void)
+{
+	int		c;
+	char	name[MAX_OSPATH];
+	int		track;
+
+	if (cmd_source != src_command)
+		return;
+
+	c = Cmd_Argc();
+	if (c != 2 && c != 3 && c != 4)
+	{
+		Con_Printf ("record <demoname> [<map> [cd track]]\n");
+		return;
+	}
+
+	if (strstr(Cmd_Argv(1), ".."))
+	{
+		Con_Printf ("Relative pathnames are not allowed.\n");
+		return;
+	}
+
+	if (c == 2 && cls.state == ca_connected)
+	{
+		Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
+		return;
+	}
+
+// write the forced cd track number, or -1
+	if (c == 4)
+	{
+		track = atoi(Cmd_Argv(3));
+		Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
+	}
+	else
+		track = -1;	
+
+	sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+	
+//
+// start the map up
+//
+	if (c > 2)
+		Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
+	
+//
+// open the demo file
+//
+	COM_DefaultExtension (name, ".dem");
+
+	Con_Printf ("recording to %s.\n", name);
+	cls.demofile = fopen (name, "wb");
+	if (!cls.demofile)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		return;
+	}
+
+	cls.forcetrack = track;
+	fprintf (cls.demofile, "%i\n", cls.forcetrack);
+	
+	cls.demorecording = true;
+}
+
+
+/*
+====================
+CL_PlayDemo_f
+
+play [demoname]
+====================
+*/
+void CL_PlayDemo_f (void)
+{
+	char	name[256];
+	int c;
+	qboolean neg = false;
+
+	if (cmd_source != src_command)
+		return;
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("play <demoname> : plays a demo\n");
+		return;
+	}
+
+//
+// disconnect from server
+//
+	CL_Disconnect ();
+	
+//
+// open the demo file
+//
+	strcpy (name, Cmd_Argv(1));
+	COM_DefaultExtension (name, ".dem");
+
+	Con_Printf ("Playing demo from %s.\n", name);
+	COM_FOpenFile (name, &cls.demofile);
+	if (!cls.demofile)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		cls.demonum = -1;		// stop demo loop
+		return;
+	}
+
+	cls.demoplayback = true;
+	cls.state = ca_connected;
+	cls.forcetrack = 0;
+
+	while ((c = getc(cls.demofile)) != '\n')
+		if (c == '-')
+			neg = true;
+		else
+			cls.forcetrack = cls.forcetrack * 10 + (c - '0');
+
+	if (neg)
+		cls.forcetrack = -cls.forcetrack;
+// ZOID, fscanf is evil
+	//fscanf (cls.demofile, "%i\n", &cls.forcetrack);
+}
+
+/*
+====================
+CL_FinishTimeDemo
+
+====================
+*/
+void CL_FinishTimeDemo (void)
+{
+	int		frames;
+	float	time;
+	
+	cls.timedemo = false;
+	
+// the first frame didn't count
+	frames = (host_framecount - cls.td_startframe) - 1;
+	time = realtime - cls.td_starttime;
+	if (!time)
+		time = 1;
+	Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
+}
+
+/*
+====================
+CL_TimeDemo_f
+
+timedemo [demoname]
+====================
+*/
+void CL_TimeDemo_f (void)
+{
+	if (cmd_source != src_command)
+		return;
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("timedemo <demoname> : gets demo speeds\n");
+		return;
+	}
+
+	CL_PlayDemo_f ();
+	
+// cls.td_starttime will be grabbed at the second frame of the demo, so
+// all the loading time doesn't get counted
+	
+	cls.timedemo = true;
+	cls.td_startframe = host_framecount;
+	cls.td_lastframe = -1;		// get a new message this frame
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cl_input.c b/apps/plugins/sdl/progs/quake/cl_input.c
new file mode 100644
index 0000000..5327b73
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cl_input.c
@@ -0,0 +1,448 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl.input.c  -- builds an intended movement command to send to the server
+
+// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
+// rights reserved.
+
+#include "quakedef.h"
+
+/*
+===============================================================================
+
+KEY BUTTONS
+
+Continuous button event tracking is complicated by the fact that two different
+input sources (say, mouse button 1 and the control key) can both press the
+same button, but the button should only be released when both of the
+pressing key have been released.
+
+When a key event issues a button command (+forward, +attack, etc), it appends
+its key number as a parameter to the command so it can be matched up with
+the release.
+
+state bit 0 is the current state of the key
+state bit 1 is edge triggered on the up to down transition
+state bit 2 is edge triggered on the down to up transition
+
+===============================================================================
+*/
+
+
+kbutton_t	in_mlook, in_klook;
+kbutton_t	in_left, in_right, in_forward, in_back;
+kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
+kbutton_t	in_strafe, in_speed, in_use, in_jump, in_attack;
+kbutton_t	in_up, in_down;
+
+int			in_impulse;
+
+
+void KeyDown (kbutton_t *b)
+{
+	int		k;
+	char	*c;
+
+	c = Cmd_Argv(1);
+	if (c[0])
+		k = atoi(c);
+	else
+		k = -1;		// typed manually at the console for continuous down
+
+	if (k == b->down[0] || k == b->down[1])
+		return;		// repeating key
+	
+	if (!b->down[0])
+		b->down[0] = k;
+	else if (!b->down[1])
+		b->down[1] = k;
+	else
+	{
+		Con_Printf ("Three keys down for a button!\n");
+		return;
+	}
+	
+	if (b->state & 1)
+		return;		// still down
+	b->state |= 1 + 2;	// down + impulse down
+}
+
+void KeyUp (kbutton_t *b)
+{
+	int		k;
+	char	*c;
+	
+	c = Cmd_Argv(1);
+	if (c[0])
+		k = atoi(c);
+	else
+	{ // typed manually at the console, assume for unsticking, so clear all
+		b->down[0] = b->down[1] = 0;
+		b->state = 4;	// impulse up
+		return;
+	}
+
+	if (b->down[0] == k)
+		b->down[0] = 0;
+	else if (b->down[1] == k)
+		b->down[1] = 0;
+	else
+		return;		// key up without coresponding down (menu pass through)
+	if (b->down[0] || b->down[1])
+		return;		// some other key is still holding it down
+
+	if (!(b->state & 1))
+		return;		// still up (this should not happen)
+	b->state &= ~1;		// now up
+	b->state |= 4; 		// impulse up
+}
+
+void IN_KLookDown (void) {KeyDown(&in_klook);}
+void IN_KLookUp (void) {KeyUp(&in_klook);}
+void IN_MLookDown (void) {KeyDown(&in_mlook);}
+void IN_MLookUp (void) {
+KeyUp(&in_mlook);
+if ( !(in_mlook.state&1) &&  lookspring.value)
+	V_StartPitchDrift();
+}
+void IN_UpDown(void) {KeyDown(&in_up);}
+void IN_UpUp(void) {KeyUp(&in_up);}
+void IN_DownDown(void) {KeyDown(&in_down);}
+void IN_DownUp(void) {KeyUp(&in_down);}
+void IN_LeftDown(void) {KeyDown(&in_left);}
+void IN_LeftUp(void) {KeyUp(&in_left);}
+void IN_RightDown(void) {KeyDown(&in_right);}
+void IN_RightUp(void) {KeyUp(&in_right);}
+void IN_ForwardDown(void) {KeyDown(&in_forward);}
+void IN_ForwardUp(void) {KeyUp(&in_forward);}
+void IN_BackDown(void) {KeyDown(&in_back);}
+void IN_BackUp(void) {KeyUp(&in_back);}
+void IN_LookupDown(void) {KeyDown(&in_lookup);}
+void IN_LookupUp(void) {KeyUp(&in_lookup);}
+void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
+void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
+void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
+void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
+void IN_MoverightDown(void) {KeyDown(&in_moveright);}
+void IN_MoverightUp(void) {KeyUp(&in_moveright);}
+
+void IN_SpeedDown(void) {KeyDown(&in_speed);}
+void IN_SpeedUp(void) {KeyUp(&in_speed);}
+void IN_StrafeDown(void) {KeyDown(&in_strafe);}
+void IN_StrafeUp(void) {KeyUp(&in_strafe);}
+
+void IN_AttackDown(void) {KeyDown(&in_attack);}
+void IN_AttackUp(void) {KeyUp(&in_attack);}
+
+void IN_UseDown (void) {KeyDown(&in_use);}
+void IN_UseUp (void) {KeyUp(&in_use);}
+void IN_JumpDown (void) {KeyDown(&in_jump);}
+void IN_JumpUp (void) {KeyUp(&in_jump);}
+
+void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));}
+
+/*
+===============
+CL_KeyState
+
+Returns 0.25 if a key was pressed and released during the frame,
+0.5 if it was pressed and held
+0 if held then released, and
+1.0 if held for the entire time
+===============
+*/
+float CL_KeyState (kbutton_t *key)
+{
+	float		val;
+	qboolean	impulsedown, impulseup, down;
+	
+	impulsedown = key->state & 2;
+	impulseup = key->state & 4;
+	down = key->state & 1;
+	val = 0;
+	
+	if (impulsedown && !impulseup)
+		if (down)
+			val = 0.5;	// pressed and held this frame
+		else
+			val = 0;	//	I_Error ();
+	if (impulseup && !impulsedown)
+		if (down)
+			val = 0;	//	I_Error ();
+		else
+			val = 0;	// released this frame
+	if (!impulsedown && !impulseup)
+		if (down)
+			val = 1.0;	// held the entire frame
+		else
+			val = 0;	// up the entire frame
+	if (impulsedown && impulseup)
+		if (down)
+			val = 0.75;	// released and re-pressed this frame
+		else
+			val = 0.25;	// pressed and released this frame
+
+	key->state &= 1;		// clear impulses
+	
+	return val;
+}
+
+
+
+
+//==========================================================================
+
+cvar_t	cl_upspeed = {"cl_upspeed","200"};
+cvar_t	cl_forwardspeed = {"cl_forwardspeed","200", true};
+cvar_t	cl_backspeed = {"cl_backspeed","200", true};
+cvar_t	cl_sidespeed = {"cl_sidespeed","350"};
+
+cvar_t	cl_movespeedkey = {"cl_movespeedkey","2.0"};
+
+cvar_t	cl_yawspeed = {"cl_yawspeed","140"};
+cvar_t	cl_pitchspeed = {"cl_pitchspeed","150"};
+
+cvar_t	cl_anglespeedkey = {"cl_anglespeedkey","1.5"};
+
+
+/*
+================
+CL_AdjustAngles
+
+Moves the local angle positions
+================
+*/
+void CL_AdjustAngles (void)
+{
+	float	speed;
+	float	up, down;
+	
+	if (in_speed.state & 1)
+		speed = host_frametime * cl_anglespeedkey.value;
+	else
+		speed = host_frametime;
+
+	if (!(in_strafe.state & 1))
+	{
+		cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
+		cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
+		cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
+	}
+	if (in_klook.state & 1)
+	{
+		V_StopPitchDrift ();
+		cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
+		cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
+	}
+	
+	up = CL_KeyState (&in_lookup);
+	down = CL_KeyState(&in_lookdown);
+	
+	cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
+	cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
+
+	if (up || down)
+		V_StopPitchDrift ();
+		
+	if (cl.viewangles[PITCH] > 80)
+		cl.viewangles[PITCH] = 80;
+	if (cl.viewangles[PITCH] < -70)
+		cl.viewangles[PITCH] = -70;
+
+	if (cl.viewangles[ROLL] > 50)
+		cl.viewangles[ROLL] = 50;
+	if (cl.viewangles[ROLL] < -50)
+		cl.viewangles[ROLL] = -50;
+		
+}
+
+/*
+================
+CL_BaseMove
+
+Send the intended movement message to the server
+================
+*/
+void CL_BaseMove (usercmd_t *cmd)
+{	
+	if (cls.signon != SIGNONS)
+		return;
+			
+	CL_AdjustAngles ();
+	
+	Q_memset (cmd, 0, sizeof(*cmd));
+	
+	if (in_strafe.state & 1)
+	{
+		cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
+		cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
+	}
+
+	cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
+	cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
+
+	cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
+	cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
+
+	if (! (in_klook.state & 1) )
+	{	
+		cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
+		cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
+	}	
+
+//
+// adjust for speed key
+//
+	if (in_speed.state & 1)
+	{
+		cmd->forwardmove *= cl_movespeedkey.value;
+		cmd->sidemove *= cl_movespeedkey.value;
+		cmd->upmove *= cl_movespeedkey.value;
+	}
+
+#ifdef QUAKE2
+	cmd->lightlevel = cl.light_level;
+#endif
+}
+
+
+
+/*
+==============
+CL_SendMove
+==============
+*/
+void CL_SendMove (usercmd_t *cmd)
+{
+	int		i;
+	int		bits;
+	sizebuf_t	buf;
+	byte	data[128];
+	
+	buf.maxsize = 128;
+	buf.cursize = 0;
+	buf.data = data;
+	
+	cl.cmd = *cmd;
+
+//
+// send the movement message
+//
+    MSG_WriteByte (&buf, clc_move);
+
+	MSG_WriteFloat (&buf, cl.mtime[0]);	// so server can get ping times
+
+	for (i=0 ; i<3 ; i++)
+		MSG_WriteAngle (&buf, cl.viewangles[i]);
+	
+    MSG_WriteShort (&buf, cmd->forwardmove);
+    MSG_WriteShort (&buf, cmd->sidemove);
+    MSG_WriteShort (&buf, cmd->upmove);
+
+//
+// send button bits
+//
+	bits = 0;
+	
+	if ( in_attack.state & 3 )
+		bits |= 1;
+	in_attack.state &= ~2;
+	
+	if (in_jump.state & 3)
+		bits |= 2;
+	in_jump.state &= ~2;
+	
+    MSG_WriteByte (&buf, bits);
+
+    MSG_WriteByte (&buf, in_impulse);
+	in_impulse = 0;
+
+#ifdef QUAKE2
+//
+// light level
+//
+	MSG_WriteByte (&buf, cmd->lightlevel);
+#endif
+
+//
+// deliver the message
+//
+	if (cls.demoplayback)
+		return;
+
+//
+// allways dump the first two message, because it may contain leftover inputs
+// from the last level
+//
+	if (++cl.movemessages <= 2)
+		return;
+	
+	if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
+	{
+		Con_Printf ("CL_SendMove: lost server connection\n");
+		CL_Disconnect ();
+	}
+}
+
+/*
+============
+CL_InitInput
+============
+*/
+void CL_InitInput (void)
+{
+	Cmd_AddCommand ("+moveup",IN_UpDown);
+	Cmd_AddCommand ("-moveup",IN_UpUp);
+	Cmd_AddCommand ("+movedown",IN_DownDown);
+	Cmd_AddCommand ("-movedown",IN_DownUp);
+	Cmd_AddCommand ("+left",IN_LeftDown);
+	Cmd_AddCommand ("-left",IN_LeftUp);
+	Cmd_AddCommand ("+right",IN_RightDown);
+	Cmd_AddCommand ("-right",IN_RightUp);
+	Cmd_AddCommand ("+forward",IN_ForwardDown);
+	Cmd_AddCommand ("-forward",IN_ForwardUp);
+	Cmd_AddCommand ("+back",IN_BackDown);
+	Cmd_AddCommand ("-back",IN_BackUp);
+	Cmd_AddCommand ("+lookup", IN_LookupDown);
+	Cmd_AddCommand ("-lookup", IN_LookupUp);
+	Cmd_AddCommand ("+lookdown", IN_LookdownDown);
+	Cmd_AddCommand ("-lookdown", IN_LookdownUp);
+	Cmd_AddCommand ("+strafe", IN_StrafeDown);
+	Cmd_AddCommand ("-strafe", IN_StrafeUp);
+	Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
+	Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
+	Cmd_AddCommand ("+moveright", IN_MoverightDown);
+	Cmd_AddCommand ("-moveright", IN_MoverightUp);
+	Cmd_AddCommand ("+speed", IN_SpeedDown);
+	Cmd_AddCommand ("-speed", IN_SpeedUp);
+	Cmd_AddCommand ("+attack", IN_AttackDown);
+	Cmd_AddCommand ("-attack", IN_AttackUp);
+	Cmd_AddCommand ("+use", IN_UseDown);
+	Cmd_AddCommand ("-use", IN_UseUp);
+	Cmd_AddCommand ("+jump", IN_JumpDown);
+	Cmd_AddCommand ("-jump", IN_JumpUp);
+	Cmd_AddCommand ("impulse", IN_Impulse);
+	Cmd_AddCommand ("+klook", IN_KLookDown);
+	Cmd_AddCommand ("-klook", IN_KLookUp);
+	Cmd_AddCommand ("+mlook", IN_MLookDown);
+	Cmd_AddCommand ("-mlook", IN_MLookUp);
+
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cl_main.c b/apps/plugins/sdl/progs/quake/cl_main.c
new file mode 100644
index 0000000..5812143
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cl_main.c
@@ -0,0 +1,757 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_main.c  -- client main loop
+
+#include "quakedef.h"
+
+// we need to declare some mouse variables here, because the menu system
+// references them even when on a unix system.
+
+// these two are not intended to be set directly
+cvar_t	cl_name = {"_cl_name", "player", true};
+cvar_t	cl_color = {"_cl_color", "0", true};
+
+cvar_t	cl_shownet = {"cl_shownet","0"};	// can be 0, 1, or 2
+cvar_t	cl_nolerp = {"cl_nolerp","0"};
+
+cvar_t	lookspring = {"lookspring","0", true};
+cvar_t	lookstrafe = {"lookstrafe","0", true};
+cvar_t	sensitivity = {"sensitivity","3", true};
+
+cvar_t	m_pitch = {"m_pitch","0.022", true};
+cvar_t	m_yaw = {"m_yaw","0.022", true};
+cvar_t	m_forward = {"m_forward","1", true};
+cvar_t	m_side = {"m_side","0.8", true};
+
+
+client_static_t	cls;
+client_state_t	cl;
+// FIXME: put these on hunk?
+efrag_t			cl_efrags[MAX_EFRAGS];
+entity_t		cl_entities[MAX_EDICTS];
+entity_t		cl_static_entities[MAX_STATIC_ENTITIES];
+lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
+dlight_t		cl_dlights[MAX_DLIGHTS];
+
+int				cl_numvisedicts;
+entity_t		*cl_visedicts[MAX_VISEDICTS];
+
+/*
+=====================
+CL_ClearState
+
+=====================
+*/
+void CL_ClearState (void)
+{
+	int			i;
+
+	if (!sv.active)
+		Host_ClearMemory ();
+
+// wipe the entire cl structure
+	memset (&cl, 0, sizeof(cl));
+
+	SZ_Clear (&cls.message);
+
+// clear other arrays	
+	memset (cl_efrags, 0, sizeof(cl_efrags));
+	memset (cl_entities, 0, sizeof(cl_entities));
+	memset (cl_dlights, 0, sizeof(cl_dlights));
+	memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
+	memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
+	memset (cl_beams, 0, sizeof(cl_beams));
+
+//
+// allocate the efrags and chain together into a free list
+//
+	cl.free_efrags = cl_efrags;
+	for (i=0 ; i<MAX_EFRAGS-1 ; i++)
+		cl.free_efrags[i].entnext = &cl.free_efrags[i+1];
+	cl.free_efrags[i].entnext = NULL;
+}
+
+/*
+=====================
+CL_Disconnect
+
+Sends a disconnect message to the server
+This is also called on Host_Error, so it shouldn't cause any errors
+=====================
+*/
+void CL_Disconnect (void)
+{
+// stop sounds (especially looping!)
+	S_StopAllSounds (true);
+	
+// bring the console down and fade the colors back to normal
+//	SCR_BringDownConsole ();
+
+// if running a local server, shut it down
+	if (cls.demoplayback)
+		CL_StopPlayback ();
+	else if (cls.state == ca_connected)
+	{
+		if (cls.demorecording)
+			CL_Stop_f ();
+
+		Con_DPrintf ("Sending clc_disconnect\n");
+		SZ_Clear (&cls.message);
+		MSG_WriteByte (&cls.message, clc_disconnect);
+		NET_SendUnreliableMessage (cls.netcon, &cls.message);
+		SZ_Clear (&cls.message);
+		NET_Close (cls.netcon);
+
+		cls.state = ca_disconnected;
+		if (sv.active)
+			Host_ShutdownServer(false);
+	}
+
+	cls.demoplayback = cls.timedemo = false;
+	cls.signon = 0;
+}
+
+void CL_Disconnect_f (void)
+{
+	CL_Disconnect ();
+	if (sv.active)
+		Host_ShutdownServer (false);
+}
+
+
+
+
+/*
+=====================
+CL_EstablishConnection
+
+Host should be either "local" or a net address to be passed on
+=====================
+*/
+void CL_EstablishConnection (char *host)
+{
+	if (cls.state == ca_dedicated)
+		return;
+
+	if (cls.demoplayback)
+		return;
+
+	CL_Disconnect ();
+
+	cls.netcon = NET_Connect (host);
+	if (!cls.netcon)
+		Host_Error ("CL_Connect: connect failed\n");
+	Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
+	
+	cls.demonum = -1;			// not in the demo loop now
+	cls.state = ca_connected;
+	cls.signon = 0;				// need all the signon messages before playing
+}
+
+/*
+=====================
+CL_SignonReply
+
+An svc_signonnum has been received, perform a client side setup
+=====================
+*/
+void CL_SignonReply (void)
+{
+	char 	str[8192];
+
+Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
+
+	switch (cls.signon)
+	{
+	case 1:
+		MSG_WriteByte (&cls.message, clc_stringcmd);
+		MSG_WriteString (&cls.message, "prespawn");
+		break;
+		
+	case 2:		
+		MSG_WriteByte (&cls.message, clc_stringcmd);
+		MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
+	
+		MSG_WriteByte (&cls.message, clc_stringcmd);
+		MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
+	
+		MSG_WriteByte (&cls.message, clc_stringcmd);
+		sprintf (str, "spawn %s", cls.spawnparms);
+		MSG_WriteString (&cls.message, str);
+		break;
+		
+	case 3:	
+		MSG_WriteByte (&cls.message, clc_stringcmd);
+		MSG_WriteString (&cls.message, "begin");
+		Cache_Report ();		// print remaining memory
+		break;
+		
+	case 4:
+		SCR_EndLoadingPlaque ();		// allow normal screen updates
+		break;
+	}
+}
+
+/*
+=====================
+CL_NextDemo
+
+Called to play the next demo in the demo loop
+=====================
+*/
+void CL_NextDemo (void)
+{
+	char	str[1024];
+
+	if (cls.demonum == -1)
+		return;		// don't play demos
+
+	SCR_BeginLoadingPlaque ();
+
+	if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
+	{
+		cls.demonum = 0;
+		if (!cls.demos[cls.demonum][0])
+		{
+			Con_Printf ("No demos listed with startdemos\n");
+			cls.demonum = -1;
+			return;
+		}
+	}
+
+	sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
+	Cbuf_InsertText (str);
+	cls.demonum++;
+}
+
+/*
+==============
+CL_PrintEntities_f
+==============
+*/
+void CL_PrintEntities_f (void)
+{
+	entity_t	*ent;
+	int			i;
+	
+	for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
+	{
+		Con_Printf ("%3i:",i);
+		if (!ent->model)
+		{
+			Con_Printf ("EMPTY\n");
+			continue;
+		}
+		Con_Printf ("%s:%2i  (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
+		,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
+	}
+}
+
+
+/*
+===============
+SetPal
+
+Debugging tool, just flashes the screen
+===============
+*/
+void SetPal (int i)
+{
+#if 0
+	static int old;
+	byte	pal[768];
+	int		c;
+	
+	if (i == old)
+		return;
+	old = i;
+
+	if (i==0)
+		VID_SetPalette (host_basepal);
+	else if (i==1)
+	{
+		for (c=0 ; c<768 ; c+=3)
+		{
+			pal[c] = 0;
+			pal[c+1] = 255;
+			pal[c+2] = 0;
+		}
+		VID_SetPalette (pal);
+	}
+	else
+	{
+		for (c=0 ; c<768 ; c+=3)
+		{
+			pal[c] = 0;
+			pal[c+1] = 0;
+			pal[c+2] = 255;
+		}
+		VID_SetPalette (pal);
+	}
+#endif
+}
+
+/*
+===============
+CL_AllocDlight
+
+===============
+*/
+dlight_t *CL_AllocDlight (int key)
+{
+	int		i;
+	dlight_t	*dl;
+
+// first look for an exact key match
+	if (key)
+	{
+		dl = cl_dlights;
+		for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+		{
+			if (dl->key == key)
+			{
+				memset (dl, 0, sizeof(*dl));
+				dl->key = key;
+				return dl;
+			}
+		}
+	}
+
+// then look for anything else
+	dl = cl_dlights;
+	for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+	{
+		if (dl->die < cl.time)
+		{
+			memset (dl, 0, sizeof(*dl));
+			dl->key = key;
+			return dl;
+		}
+	}
+
+	dl = &cl_dlights[0];
+	memset (dl, 0, sizeof(*dl));
+	dl->key = key;
+	return dl;
+}
+
+
+/*
+===============
+CL_DecayLights
+
+===============
+*/
+void CL_DecayLights (void)
+{
+	int			i;
+	dlight_t	*dl;
+	float		time;
+	
+	time = cl.time - cl.oldtime;
+
+	dl = cl_dlights;
+	for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+	{
+		if (dl->die < cl.time || !dl->radius)
+			continue;
+		
+		dl->radius -= time*dl->decay;
+		if (dl->radius < 0)
+			dl->radius = 0;
+	}
+}
+
+
+/*
+===============
+CL_LerpPoint
+
+Determines the fraction between the last two messages that the objects
+should be put at.
+===============
+*/
+float	CL_LerpPoint (void)
+{
+	float	f, frac;
+
+	f = cl.mtime[0] - cl.mtime[1];
+	
+	if (!f || cl_nolerp.value || cls.timedemo || sv.active)
+	{
+		cl.time = cl.mtime[0];
+		return 1;
+	}
+		
+	if (f > 0.1)
+	{	// dropped packet, or start of demo
+		cl.mtime[1] = cl.mtime[0] - 0.1;
+		f = 0.1;
+	}
+	frac = (cl.time - cl.mtime[1]) / f;
+//Con_Printf ("frac: %f\n",frac);
+	if (frac < 0)
+	{
+		if (frac < -0.01)
+		{
+SetPal(1);
+			cl.time = cl.mtime[1];
+//				Con_Printf ("low frac\n");
+		}
+		frac = 0;
+	}
+	else if (frac > 1)
+	{
+		if (frac > 1.01)
+		{
+SetPal(2);
+			cl.time = cl.mtime[0];
+//				Con_Printf ("high frac\n");
+		}
+		frac = 1;
+	}
+	else
+		SetPal(0);
+		
+	return frac;
+}
+
+
+/*
+===============
+CL_RelinkEntities
+===============
+*/
+void CL_RelinkEntities (void)
+{
+	entity_t	*ent;
+	int			i, j;
+	float		frac, f, d;
+	vec3_t		delta;
+	float		bobjrotate;
+	vec3_t		oldorg;
+	dlight_t	*dl;
+
+// determine partial update time	
+	frac = CL_LerpPoint ();
+
+	cl_numvisedicts = 0;
+
+//
+// interpolate player info
+//
+	for (i=0 ; i<3 ; i++)
+		cl.velocity[i] = cl.mvelocity[1][i] + 
+			frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
+
+	if (cls.demoplayback)
+	{
+	// interpolate the angles	
+		for (j=0 ; j<3 ; j++)
+		{
+			d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
+			if (d > 180)
+				d -= 360;
+			else if (d < -180)
+				d += 360;
+			cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
+		}
+	}
+	
+	bobjrotate = anglemod(100*cl.time);
+	
+// start on the entity after the world
+	for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
+	{
+		if (!ent->model)
+		{	// empty slot
+			if (ent->forcelink)
+				R_RemoveEfrags (ent);	// just became empty
+			continue;
+		}
+
+// if the object wasn't included in the last packet, remove it
+		if (ent->msgtime != cl.mtime[0])
+		{
+			ent->model = NULL;
+			continue;
+		}
+
+		VectorCopy (ent->origin, oldorg);
+
+		if (ent->forcelink)
+		{	// the entity was not updated in the last message
+			// so move to the final spot
+			VectorCopy (ent->msg_origins[0], ent->origin);
+			VectorCopy (ent->msg_angles[0], ent->angles);
+		}
+		else
+		{	// if the delta is large, assume a teleport and don't lerp
+			f = frac;
+			for (j=0 ; j<3 ; j++)
+			{
+				delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
+				if (delta[j] > 100 || delta[j] < -100)
+					f = 1;		// assume a teleportation, not a motion
+			}
+
+		// interpolate the origin and angles
+			for (j=0 ; j<3 ; j++)
+			{
+				ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
+
+				d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
+				if (d > 180)
+					d -= 360;
+				else if (d < -180)
+					d += 360;
+				ent->angles[j] = ent->msg_angles[1][j] + f*d;
+			}
+			
+		}
+
+// rotate binary objects locally
+		if (ent->model->flags & EF_ROTATE)
+			ent->angles[1] = bobjrotate;
+
+		if (ent->effects & EF_BRIGHTFIELD)
+			R_EntityParticles (ent);
+#ifdef QUAKE2
+		if (ent->effects & EF_DARKFIELD)
+			R_DarkFieldParticles (ent);
+#endif
+		if (ent->effects & EF_MUZZLEFLASH)
+		{
+			vec3_t		fv, rv, uv;
+
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin,  dl->origin);
+			dl->origin[2] += 16;
+			AngleVectors (ent->angles, fv, rv, uv);
+			 
+			VectorMA (dl->origin, 18, fv, dl->origin);
+			dl->radius = 200 + (rand()&31);
+			dl->minlight = 32;
+			dl->die = cl.time + 0.1;
+		}
+		if (ent->effects & EF_BRIGHTLIGHT)
+		{			
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin,  dl->origin);
+			dl->origin[2] += 16;
+			dl->radius = 400 + (rand()&31);
+			dl->die = cl.time + 0.001;
+		}
+		if (ent->effects & EF_DIMLIGHT)
+		{			
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin,  dl->origin);
+			dl->radius = 200 + (rand()&31);
+			dl->die = cl.time + 0.001;
+		}
+#ifdef QUAKE2
+		if (ent->effects & EF_DARKLIGHT)
+		{			
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin,  dl->origin);
+			dl->radius = 200.0 + (rand()&31);
+			dl->die = cl.time + 0.001;
+			dl->dark = true;
+		}
+		if (ent->effects & EF_LIGHT)
+		{			
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin,  dl->origin);
+			dl->radius = 200;
+			dl->die = cl.time + 0.001;
+		}
+#endif
+
+		if (ent->model->flags & EF_GIB)
+			R_RocketTrail (oldorg, ent->origin, 2);
+		else if (ent->model->flags & EF_ZOMGIB)
+			R_RocketTrail (oldorg, ent->origin, 4);
+		else if (ent->model->flags & EF_TRACER)
+			R_RocketTrail (oldorg, ent->origin, 3);
+		else if (ent->model->flags & EF_TRACER2)
+			R_RocketTrail (oldorg, ent->origin, 5);
+		else if (ent->model->flags & EF_ROCKET)
+		{
+			R_RocketTrail (oldorg, ent->origin, 0);
+			dl = CL_AllocDlight (i);
+			VectorCopy (ent->origin, dl->origin);
+			dl->radius = 200;
+			dl->die = cl.time + 0.01;
+		}
+		else if (ent->model->flags & EF_GRENADE)
+			R_RocketTrail (oldorg, ent->origin, 1);
+		else if (ent->model->flags & EF_TRACER3)
+			R_RocketTrail (oldorg, ent->origin, 6);
+
+		ent->forcelink = false;
+
+		if (i == cl.viewentity && !chase_active.value)
+			continue;
+
+#ifdef QUAKE2
+		if ( ent->effects & EF_NODRAW )
+			continue;
+#endif
+		if (cl_numvisedicts < MAX_VISEDICTS)
+		{
+			cl_visedicts[cl_numvisedicts] = ent;
+			cl_numvisedicts++;
+		}
+	}
+
+}
+
+
+/*
+===============
+CL_ReadFromServer
+
+Read all incoming data from the server
+===============
+*/
+int CL_ReadFromServer (void)
+{
+	int		ret;
+
+	cl.oldtime = cl.time;
+	cl.time += host_frametime;
+	
+	do
+	{
+		ret = CL_GetMessage ();
+		if (ret == -1)
+			Host_Error ("CL_ReadFromServer: lost server connection");
+		if (!ret)
+			break;
+		
+		cl.last_received_message = realtime;
+		CL_ParseServerMessage ();
+	} while (ret && cls.state == ca_connected);
+	
+	if (cl_shownet.value)
+		Con_Printf ("\n");
+
+	CL_RelinkEntities ();
+	CL_UpdateTEnts ();
+
+//
+// bring the links up to date
+//
+	return 0;
+}
+
+/*
+=================
+CL_SendCmd
+=================
+*/
+void CL_SendCmd (void)
+{
+	usercmd_t		cmd;
+
+	if (cls.state != ca_connected)
+		return;
+
+	if (cls.signon == SIGNONS)
+	{
+	// get basic movement from keyboard
+		CL_BaseMove (&cmd);
+	
+	// allow mice or other external controllers to add to the move
+		IN_Move (&cmd);
+	
+	// send the unreliable message
+		CL_SendMove (&cmd);
+	
+	}
+
+	if (cls.demoplayback)
+	{
+		SZ_Clear (&cls.message);
+		return;
+	}
+	
+// send the reliable message
+	if (!cls.message.cursize)
+		return;		// no message at all
+	
+	if (!NET_CanSendMessage (cls.netcon))
+	{
+		Con_DPrintf ("CL_WriteToServer: can't send\n");
+		return;
+	}
+
+	if (NET_SendMessage (cls.netcon, &cls.message) == -1)
+		Host_Error ("CL_WriteToServer: lost server connection");
+
+	SZ_Clear (&cls.message);
+}
+
+/*
+=================
+CL_Init
+=================
+*/
+void CL_Init (void)
+{	
+	SZ_Alloc (&cls.message, 1024);
+
+	CL_InitInput ();
+	CL_InitTEnts ();
+	
+//
+// register our commands
+//
+	Cvar_RegisterVariable (&cl_name);
+	Cvar_RegisterVariable (&cl_color);
+	Cvar_RegisterVariable (&cl_upspeed);
+	Cvar_RegisterVariable (&cl_forwardspeed);
+	Cvar_RegisterVariable (&cl_backspeed);
+	Cvar_RegisterVariable (&cl_sidespeed);
+	Cvar_RegisterVariable (&cl_movespeedkey);
+	Cvar_RegisterVariable (&cl_yawspeed);
+	Cvar_RegisterVariable (&cl_pitchspeed);
+	Cvar_RegisterVariable (&cl_anglespeedkey);
+	Cvar_RegisterVariable (&cl_shownet);
+	Cvar_RegisterVariable (&cl_nolerp);
+	Cvar_RegisterVariable (&lookspring);
+	Cvar_RegisterVariable (&lookstrafe);
+	Cvar_RegisterVariable (&sensitivity);
+
+	Cvar_RegisterVariable (&m_pitch);
+	Cvar_RegisterVariable (&m_yaw);
+	Cvar_RegisterVariable (&m_forward);
+	Cvar_RegisterVariable (&m_side);
+
+//	Cvar_RegisterVariable (&cl_autofire);
+	
+	Cmd_AddCommand ("entities", CL_PrintEntities_f);
+	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
+	Cmd_AddCommand ("record", CL_Record_f);
+	Cmd_AddCommand ("stop", CL_Stop_f);
+	Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
+	Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cl_parse.c b/apps/plugins/sdl/progs/quake/cl_parse.c
new file mode 100644
index 0000000..c79ca47
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cl_parse.c
@@ -0,0 +1,963 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_parse.c  -- parse a message received from the server
+
+#include "quakedef.h"
+
+char *svc_strings[] =
+{
+	"svc_bad",
+	"svc_nop",
+	"svc_disconnect",
+	"svc_updatestat",
+	"svc_version",		// [long] server version
+	"svc_setview",		// [short] entity number
+	"svc_sound",			// <see code>
+	"svc_time",			// [float] server time
+	"svc_print",			// [string] null terminated string
+	"svc_stufftext",		// [string] stuffed into client's console buffer
+						// the string should be \n terminated
+	"svc_setangle",		// [vec3] set the view angle to this absolute value
+	
+	"svc_serverinfo",		// [long] version
+						// [string] signon string
+						// [string]..[0]model cache [string]...[0]sounds cache
+						// [string]..[0]item cache
+	"svc_lightstyle",		// [byte] [string]
+	"svc_updatename",		// [byte] [string]
+	"svc_updatefrags",	// [byte] [short]
+	"svc_clientdata",		// <shortbits + data>
+	"svc_stopsound",		// <see code>
+	"svc_updatecolors",	// [byte] [byte]
+	"svc_particle",		// [vec3] <variable>
+	"svc_damage",			// [byte] impact [byte] blood [vec3] from
+	
+	"svc_spawnstatic",
+	"OBSOLETE svc_spawnbinary",
+	"svc_spawnbaseline",
+	
+	"svc_temp_entity",		// <variable>
+	"svc_setpause",
+	"svc_signonnum",
+	"svc_centerprint",
+	"svc_killedmonster",
+	"svc_foundsecret",
+	"svc_spawnstaticsound",
+	"svc_intermission",
+	"svc_finale",			// [string] music [string] text
+	"svc_cdtrack",			// [byte] track [byte] looptrack
+	"svc_sellscreen",
+	"svc_cutscene"
+};
+
+//=============================================================================
+
+/*
+===============
+CL_EntityNum
+
+This error checks and tracks the total number of entities
+===============
+*/
+entity_t	*CL_EntityNum (int num)
+{
+	if (num >= cl.num_entities)
+	{
+		if (num >= MAX_EDICTS)
+			Host_Error ("CL_EntityNum: %i is an invalid number",num);
+		while (cl.num_entities<=num)
+		{
+			cl_entities[cl.num_entities].colormap = vid.colormap;
+			cl.num_entities++;
+		}
+	}
+		
+	return &cl_entities[num];
+}
+
+
+/*
+==================
+CL_ParseStartSoundPacket
+==================
+*/
+void CL_ParseStartSoundPacket(void)
+{
+    vec3_t  pos;
+    int 	channel, ent;
+    int 	sound_num;
+    int 	volume;
+    int 	field_mask;
+    float 	attenuation;  
+ 	int		i;
+	           
+    field_mask = MSG_ReadByte(); 
+
+    if (field_mask & SND_VOLUME)
+		volume = MSG_ReadByte ();
+	else
+		volume = DEFAULT_SOUND_PACKET_VOLUME;
+	
+    if (field_mask & SND_ATTENUATION)
+		attenuation = MSG_ReadByte () / 64.0;
+	else
+		attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+	
+	channel = MSG_ReadShort ();
+	sound_num = MSG_ReadByte ();
+
+	ent = channel >> 3;
+	channel &= 7;
+
+	if (ent > MAX_EDICTS)
+		Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent);
+	
+	for (i=0 ; i<3 ; i++)
+		pos[i] = MSG_ReadCoord ();
+ 
+    S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
+}       
+
+/*
+==================
+CL_KeepaliveMessage
+
+When the client is taking a long time to load stuff, send keepalive messages
+so the server doesn't disconnect.
+==================
+*/
+void CL_KeepaliveMessage (void)
+{
+	float	time;
+	static float lastmsg;
+	int		ret;
+	sizebuf_t	old;
+	byte		olddata[8192];
+	
+	if (sv.active)
+		return;		// no need if server is local
+	if (cls.demoplayback)
+		return;
+
+// read messages from server, should just be nops
+	old = net_message;
+	memcpy (olddata, net_message.data, net_message.cursize);
+	
+	do
+	{
+		ret = CL_GetMessage ();
+		switch (ret)
+		{
+		default:
+			Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed");		
+		case 0:
+			break;	// nothing waiting
+		case 1:
+			Host_Error ("CL_KeepaliveMessage: received a message");
+			break;
+		case 2:
+			if (MSG_ReadByte() != svc_nop)
+				Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop");
+			break;
+		}
+	} while (ret);
+
+	net_message = old;
+	memcpy (net_message.data, olddata, net_message.cursize);
+
+// check time
+	time = Sys_FloatTime ();
+	if (time - lastmsg < 5)
+		return;
+	lastmsg = time;
+
+// write out a nop
+	Con_Printf ("--> client to server keepalive\n");
+
+	MSG_WriteByte (&cls.message, clc_nop);
+	NET_SendMessage (cls.netcon, &cls.message);
+	SZ_Clear (&cls.message);
+}
+
+/*
+==================
+CL_ParseServerInfo
+==================
+*/
+void CL_ParseServerInfo (void)
+{
+	char	*str;
+	int		i;
+	int		nummodels, numsounds;
+	char	model_precache[MAX_MODELS][MAX_QPATH];
+	char	sound_precache[MAX_SOUNDS][MAX_QPATH];
+	
+	Con_DPrintf ("Serverinfo packet received.\n");
+//
+// wipe the client_state_t struct
+//
+	CL_ClearState ();
+
+// parse protocol version number
+	i = MSG_ReadLong ();
+	if (i != PROTOCOL_VERSION)
+	{
+		Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION);
+		return;
+	}
+
+// parse maxclients
+	cl.maxclients = MSG_ReadByte ();
+	if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
+	{
+		Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
+		return;
+	}
+	cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores");
+
+// parse gametype
+	cl.gametype = MSG_ReadByte ();
+
+// parse signon message
+	str = MSG_ReadString ();
+	strncpy (cl.levelname, str, sizeof(cl.levelname)-1);
+
+// seperate the printfs so the server message can have a color
+	Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+	Con_Printf ("%c%s\n", 2, str);
+
+//
+// first we go through and touch all of the precache data that still
+// happens to be in the cache, so precaching something else doesn't
+// needlessly purge it
+//
+
+// precache models
+	memset (cl.model_precache, 0, sizeof(cl.model_precache));
+	for (nummodels=1 ; ; nummodels++)
+	{
+		str = MSG_ReadString ();
+		if (!str[0])
+			break;
+		if (nummodels==MAX_MODELS)
+		{
+			Con_Printf ("Server sent too many model precaches\n");
+			return;
+		}
+		strcpy (model_precache[nummodels], str);
+		Mod_TouchModel (str);
+	}
+
+// precache sounds
+	memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
+	for (numsounds=1 ; ; numsounds++)
+	{
+		str = MSG_ReadString ();
+		if (!str[0])
+			break;
+		if (numsounds==MAX_SOUNDS)
+		{
+			Con_Printf ("Server sent too many sound precaches\n");
+			return;
+		}
+		strcpy (sound_precache[numsounds], str);
+		S_TouchSound (str);
+	}
+
+//
+// now we try to load everything else until a cache allocation fails
+//
+
+	for (i=1 ; i<nummodels ; i++)
+	{
+		cl.model_precache[i] = Mod_ForName (model_precache[i], false);
+		if (cl.model_precache[i] == NULL)
+		{
+			Con_Printf("Model %s not found\n", model_precache[i]);
+			return;
+		}
+		CL_KeepaliveMessage ();
+	}
+
+	S_BeginPrecaching ();
+	for (i=1 ; i<numsounds ; i++)
+	{
+		cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]);
+		CL_KeepaliveMessage ();
+	}
+	S_EndPrecaching ();
+
+
+// local state
+	cl_entities[0].model = cl.worldmodel = cl.model_precache[1];
+	
+	R_NewMap ();
+
+	Hunk_Check ();		// make sure nothing is hurt
+	
+	noclip_anglehack = false;		// noclip is turned off at start	
+}
+
+
+/*
+==================
+CL_ParseUpdate
+
+Parse an entity update message from the server
+If an entities model or origin changes from frame to frame, it must be
+relinked.  Other attributes can change without relinking.
+==================
+*/
+int	bitcounts[16];
+
+void CL_ParseUpdate (int bits)
+{
+	int			i;
+	model_t		*model;
+	int			modnum;
+	qboolean	forcelink;
+	entity_t	*ent;
+	int			num;
+	int			skin;
+
+	if (cls.signon == SIGNONS - 1)
+	{	// first update is the final signon stage
+		cls.signon = SIGNONS;
+		CL_SignonReply ();
+	}
+
+	if (bits & U_MOREBITS)
+	{
+		i = MSG_ReadByte ();
+		bits |= (i<<8);
+	}
+
+	if (bits & U_LONGENTITY)	
+		num = MSG_ReadShort ();
+	else
+		num = MSG_ReadByte ();
+
+	ent = CL_EntityNum (num);
+
+for (i=0 ; i<16 ; i++)
+if (bits&(1<<i))
+	bitcounts[i]++;
+
+	if (ent->msgtime != cl.mtime[1])
+		forcelink = true;	// no previous frame to lerp from
+	else
+		forcelink = false;
+
+	ent->msgtime = cl.mtime[0];
+	
+	if (bits & U_MODEL)
+	{
+		modnum = MSG_ReadByte ();
+		if (modnum >= MAX_MODELS)
+			Host_Error ("CL_ParseModel: bad modnum");
+	}
+	else
+		modnum = ent->baseline.modelindex;
+		
+	model = cl.model_precache[modnum];
+	if (model != ent->model)
+	{
+		ent->model = model;
+	// automatic animation (torches, etc) can be either all together
+	// or randomized
+		if (model)
+		{
+			if (model->synctype == ST_RAND)
+				ent->syncbase = (float)(rand()&0x7fff) / 0x7fff;
+			else
+				ent->syncbase = 0.0;
+		}
+		else
+			forcelink = true;	// hack to make null model players work
+#ifdef GLQUAKE
+		if (num > 0 && num <= cl.maxclients)
+			R_TranslatePlayerSkin (num - 1);
+#endif
+	}
+	
+	if (bits & U_FRAME)
+		ent->frame = MSG_ReadByte ();
+	else
+		ent->frame = ent->baseline.frame;
+
+	if (bits & U_COLORMAP)
+		i = MSG_ReadByte();
+	else
+		i = ent->baseline.colormap;
+	if (!i)
+		ent->colormap = vid.colormap;
+	else
+	{
+		if (i > cl.maxclients)
+			Sys_Error ("i >= cl.maxclients");
+		ent->colormap = cl.scores[i-1].translations;
+	}
+
+#ifdef GLQUAKE
+	if (bits & U_SKIN)
+		skin = MSG_ReadByte();
+	else
+		skin = ent->baseline.skin;
+	if (skin != ent->skinnum) {
+		ent->skinnum = skin;
+		if (num > 0 && num <= cl.maxclients)
+			R_TranslatePlayerSkin (num - 1);
+	}
+
+#else
+
+	if (bits & U_SKIN)
+		ent->skinnum = MSG_ReadByte();
+	else
+		ent->skinnum = ent->baseline.skin;
+#endif
+
+	if (bits & U_EFFECTS)
+		ent->effects = MSG_ReadByte();
+	else
+		ent->effects = ent->baseline.effects;
+
+// shift the known values for interpolation
+	VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+	VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+
+	if (bits & U_ORIGIN1)
+		ent->msg_origins[0][0] = MSG_ReadCoord ();
+	else
+		ent->msg_origins[0][0] = ent->baseline.origin[0];
+	if (bits & U_ANGLE1)
+		ent->msg_angles[0][0] = MSG_ReadAngle();
+	else
+		ent->msg_angles[0][0] = ent->baseline.angles[0];
+
+	if (bits & U_ORIGIN2)
+		ent->msg_origins[0][1] = MSG_ReadCoord ();
+	else
+		ent->msg_origins[0][1] = ent->baseline.origin[1];
+	if (bits & U_ANGLE2)
+		ent->msg_angles[0][1] = MSG_ReadAngle();
+	else
+		ent->msg_angles[0][1] = ent->baseline.angles[1];
+
+	if (bits & U_ORIGIN3)
+		ent->msg_origins[0][2] = MSG_ReadCoord ();
+	else
+		ent->msg_origins[0][2] = ent->baseline.origin[2];
+	if (bits & U_ANGLE3)
+		ent->msg_angles[0][2] = MSG_ReadAngle();
+	else
+		ent->msg_angles[0][2] = ent->baseline.angles[2];
+
+	if ( bits & U_NOLERP )
+		ent->forcelink = true;
+
+	if ( forcelink )
+	{	// didn't have an update last message
+		VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
+		VectorCopy (ent->msg_origins[0], ent->origin);
+		VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
+		VectorCopy (ent->msg_angles[0], ent->angles);
+		ent->forcelink = true;
+	}
+}
+
+/*
+==================
+CL_ParseBaseline
+==================
+*/
+void CL_ParseBaseline (entity_t *ent)
+{
+	int			i;
+	
+	ent->baseline.modelindex = MSG_ReadByte ();
+	ent->baseline.frame = MSG_ReadByte ();
+	ent->baseline.colormap = MSG_ReadByte();
+	ent->baseline.skin = MSG_ReadByte();
+	for (i=0 ; i<3 ; i++)
+	{
+		ent->baseline.origin[i] = MSG_ReadCoord ();
+		ent->baseline.angles[i] = MSG_ReadAngle ();
+	}
+}
+
+
+/*
+==================
+CL_ParseClientdata
+
+Server information pertaining to this client only
+==================
+*/
+void CL_ParseClientdata (int bits)
+{
+	int		i, j;
+	
+	if (bits & SU_VIEWHEIGHT)
+		cl.viewheight = MSG_ReadChar ();
+	else
+		cl.viewheight = DEFAULT_VIEWHEIGHT;
+
+	if (bits & SU_IDEALPITCH)
+		cl.idealpitch = MSG_ReadChar ();
+	else
+		cl.idealpitch = 0;
+	
+	VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
+	for (i=0 ; i<3 ; i++)
+	{
+		if (bits & (SU_PUNCH1<<i) )
+			cl.punchangle[i] = MSG_ReadChar();
+		else
+			cl.punchangle[i] = 0;
+		if (bits & (SU_VELOCITY1<<i) )
+			cl.mvelocity[0][i] = MSG_ReadChar()*16;
+		else
+			cl.mvelocity[0][i] = 0;
+	}
+
+// [always sent]	if (bits & SU_ITEMS)
+		i = MSG_ReadLong ();
+
+	if (cl.items != i)
+	{	// set flash times
+		Sbar_Changed ();
+		for (j=0 ; j<32 ; j++)
+			if ( (i & (1<<j)) && !(cl.items & (1<<j)))
+				cl.item_gettime[j] = cl.time;
+		cl.items = i;
+	}
+		
+	cl.onground = (bits & SU_ONGROUND) != 0;
+	cl.inwater = (bits & SU_INWATER) != 0;
+
+	if (bits & SU_WEAPONFRAME)
+		cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte ();
+	else
+		cl.stats[STAT_WEAPONFRAME] = 0;
+
+	if (bits & SU_ARMOR)
+		i = MSG_ReadByte ();
+	else
+		i = 0;
+	if (cl.stats[STAT_ARMOR] != i)
+	{
+		cl.stats[STAT_ARMOR] = i;
+		Sbar_Changed ();
+	}
+
+	if (bits & SU_WEAPON)
+		i = MSG_ReadByte ();
+	else
+		i = 0;
+	if (cl.stats[STAT_WEAPON] != i)
+	{
+		cl.stats[STAT_WEAPON] = i;
+		Sbar_Changed ();
+	}
+	
+	i = MSG_ReadShort ();
+	if (cl.stats[STAT_HEALTH] != i)
+	{
+		cl.stats[STAT_HEALTH] = i;
+		Sbar_Changed ();
+	}
+
+	i = MSG_ReadByte ();
+	if (cl.stats[STAT_AMMO] != i)
+	{
+		cl.stats[STAT_AMMO] = i;
+		Sbar_Changed ();
+	}
+
+	for (i=0 ; i<4 ; i++)
+	{
+		j = MSG_ReadByte ();
+		if (cl.stats[STAT_SHELLS+i] != j)
+		{
+			cl.stats[STAT_SHELLS+i] = j;
+			Sbar_Changed ();
+		}
+	}
+
+	i = MSG_ReadByte ();
+
+	if (standard_quake)
+	{
+		if (cl.stats[STAT_ACTIVEWEAPON] != i)
+		{
+			cl.stats[STAT_ACTIVEWEAPON] = i;
+			Sbar_Changed ();
+		}
+	}
+	else
+	{
+		if (cl.stats[STAT_ACTIVEWEAPON] != (1<<i))
+		{
+			cl.stats[STAT_ACTIVEWEAPON] = (1<<i);
+			Sbar_Changed ();
+		}
+	}
+}
+
+/*
+=====================
+CL_NewTranslation
+=====================
+*/
+void CL_NewTranslation (int slot)
+{
+	int		i, j;
+	int		top, bottom;
+	byte	*dest, *source;
+	
+	if (slot > cl.maxclients)
+		Sys_Error ("CL_NewTranslation: slot > cl.maxclients");
+	dest = cl.scores[slot].translations;
+	source = vid.colormap;
+	memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations));
+	top = cl.scores[slot].colors & 0xf0;
+	bottom = (cl.scores[slot].colors &15)<<4;
+#ifdef GLQUAKE
+	R_TranslatePlayerSkin (slot);
+#endif
+
+	for (i=0 ; i<VID_GRADES ; i++, dest += 256, source+=256)
+	{
+		if (top < 128)	// the artists made some backwards ranges.  sigh.
+			memcpy (dest + TOP_RANGE, source + top, 16);
+		else
+			for (j=0 ; j<16 ; j++)
+				dest[TOP_RANGE+j] = source[top+15-j];
+				
+		if (bottom < 128)
+			memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+		else
+			for (j=0 ; j<16 ; j++)
+				dest[BOTTOM_RANGE+j] = source[bottom+15-j];		
+	}
+}
+
+/*
+=====================
+CL_ParseStatic
+=====================
+*/
+void CL_ParseStatic (void)
+{
+	entity_t *ent;
+	int		i;
+		
+	i = cl.num_statics;
+	if (i >= MAX_STATIC_ENTITIES)
+		Host_Error ("Too many static entities");
+	ent = &cl_static_entities[i];
+	cl.num_statics++;
+	CL_ParseBaseline (ent);
+
+// copy it to the current state
+	ent->model = cl.model_precache[ent->baseline.modelindex];
+	ent->frame = ent->baseline.frame;
+	ent->colormap = vid.colormap;
+	ent->skinnum = ent->baseline.skin;
+	ent->effects = ent->baseline.effects;
+
+	VectorCopy (ent->baseline.origin, ent->origin);
+	VectorCopy (ent->baseline.angles, ent->angles);	
+	R_AddEfrags (ent);
+}
+
+/*
+===================
+CL_ParseStaticSound
+===================
+*/
+void CL_ParseStaticSound (void)
+{
+	vec3_t		org;
+	int			sound_num, vol, atten;
+	int			i;
+	
+	for (i=0 ; i<3 ; i++)
+		org[i] = MSG_ReadCoord ();
+	sound_num = MSG_ReadByte ();
+	vol = MSG_ReadByte ();
+	atten = MSG_ReadByte ();
+	
+	S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
+}
+
+
+#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
+
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+void CL_ParseServerMessage (void)
+{
+	int			cmd;
+	int			i;
+	
+//
+// if recording demos, copy the message out
+//
+	if (cl_shownet.value == 1)
+		Con_Printf ("%i ",net_message.cursize);
+	else if (cl_shownet.value == 2)
+		Con_Printf ("------------------\n");
+	
+	cl.onground = false;	// unless the server says otherwise	
+//
+// parse the message
+//
+	MSG_BeginReading ();
+	
+	while (1)
+	{
+		if (msg_badread)
+			Host_Error ("CL_ParseServerMessage: Bad server message");
+
+		cmd = MSG_ReadByte ();
+
+		if (cmd == -1)
+		{
+			SHOWNET("END OF MESSAGE");
+			return;		// end of message
+		}
+
+	// if the high bit of the command byte is set, it is a fast update
+		if (cmd & 128)
+		{
+			SHOWNET("fast update");
+			CL_ParseUpdate (cmd&127);
+			continue;
+		}
+
+		SHOWNET(svc_strings[cmd]);
+	
+	// other commands
+		switch (cmd)
+		{
+		default:
+			Host_Error ("CL_ParseServerMessage: Illegible server message\n");
+			break;
+			
+		case svc_nop:
+//			Con_Printf ("svc_nop\n");
+			break;
+			
+		case svc_time:
+			cl.mtime[1] = cl.mtime[0];
+			cl.mtime[0] = MSG_ReadFloat ();			
+			break;
+			
+		case svc_clientdata:
+			i = MSG_ReadShort ();
+			CL_ParseClientdata (i);
+			break;
+		
+		case svc_version:
+			i = MSG_ReadLong ();
+			if (i != PROTOCOL_VERSION)
+				Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION);
+			break;
+			
+		case svc_disconnect:
+			Host_EndGame ("Server disconnected\n");
+
+		case svc_print:
+			Con_Printf ("%s", MSG_ReadString ());
+			break;
+			
+		case svc_centerprint:
+			SCR_CenterPrint (MSG_ReadString ());
+			break;
+			
+		case svc_stufftext:
+			Cbuf_AddText (MSG_ReadString ());
+			break;
+			
+		case svc_damage:
+			V_ParseDamage ();
+			break;
+			
+		case svc_serverinfo:
+			CL_ParseServerInfo ();
+			vid.recalc_refdef = true;	// leave intermission full screen
+			break;
+			
+		case svc_setangle:
+			for (i=0 ; i<3 ; i++)
+				cl.viewangles[i] = MSG_ReadAngle ();
+			break;
+			
+		case svc_setview:
+			cl.viewentity = MSG_ReadShort ();
+			break;
+					
+		case svc_lightstyle:
+			i = MSG_ReadByte ();
+			if (i >= MAX_LIGHTSTYLES)
+				Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES");
+			Q_strcpy (cl_lightstyle[i].map,  MSG_ReadString());
+			cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
+			break;
+			
+		case svc_sound:
+			CL_ParseStartSoundPacket();
+			break;
+			
+		case svc_stopsound:
+			i = MSG_ReadShort();
+			S_StopSound(i>>3, i&7);
+			break;
+		
+		case svc_updatename:
+			Sbar_Changed ();
+			i = MSG_ReadByte ();
+			if (i >= cl.maxclients)
+				Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
+			strcpy (cl.scores[i].name, MSG_ReadString ());
+			break;
+			
+		case svc_updatefrags:
+			Sbar_Changed ();
+			i = MSG_ReadByte ();
+			if (i >= cl.maxclients)
+				Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
+			cl.scores[i].frags = MSG_ReadShort ();
+			break;			
+
+		case svc_updatecolors:
+			Sbar_Changed ();
+			i = MSG_ReadByte ();
+			if (i >= cl.maxclients)
+				Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
+			cl.scores[i].colors = MSG_ReadByte ();
+			CL_NewTranslation (i);
+			break;
+			
+		case svc_particle:
+			R_ParseParticleEffect ();
+			break;
+
+		case svc_spawnbaseline:
+			i = MSG_ReadShort ();
+			// must use CL_EntityNum() to force cl.num_entities up
+			CL_ParseBaseline (CL_EntityNum(i));
+			break;
+		case svc_spawnstatic:
+			CL_ParseStatic ();
+			break;			
+		case svc_temp_entity:
+			CL_ParseTEnt ();
+			break;
+
+		case svc_setpause:
+			{
+				cl.paused = MSG_ReadByte ();
+
+				if (cl.paused)
+				{
+					CDAudio_Pause ();
+#ifdef _WIN32
+					VID_HandlePause (true);
+#endif
+				}
+				else
+				{
+					CDAudio_Resume ();
+#ifdef _WIN32
+					VID_HandlePause (false);
+#endif
+				}
+			}
+			break;
+			
+		case svc_signonnum:
+			i = MSG_ReadByte ();
+			if (i <= cls.signon)
+				Host_Error ("Received signon %i when at %i", i, cls.signon);
+			cls.signon = i;
+			CL_SignonReply ();
+			break;
+
+		case svc_killedmonster:
+			cl.stats[STAT_MONSTERS]++;
+			break;
+
+		case svc_foundsecret:
+			cl.stats[STAT_SECRETS]++;
+			break;
+
+		case svc_updatestat:
+			i = MSG_ReadByte ();
+			if (i < 0 || i >= MAX_CL_STATS)
+				Sys_Error ("svc_updatestat: %i is invalid", i);
+			cl.stats[i] = MSG_ReadLong ();;
+			break;
+			
+		case svc_spawnstaticsound:
+			CL_ParseStaticSound ();
+			break;
+
+		case svc_cdtrack:
+			cl.cdtrack = MSG_ReadByte ();
+			cl.looptrack = MSG_ReadByte ();
+			if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
+				CDAudio_Play ((byte)cls.forcetrack, true);
+			else
+				CDAudio_Play ((byte)cl.cdtrack, true);
+			break;
+
+		case svc_intermission:
+			cl.intermission = 1;
+			cl.completed_time = cl.time;
+			vid.recalc_refdef = true;	// go to full screen
+			break;
+
+		case svc_finale:
+			cl.intermission = 2;
+			cl.completed_time = cl.time;
+			vid.recalc_refdef = true;	// go to full screen
+			SCR_CenterPrint (MSG_ReadString ());			
+			break;
+
+		case svc_cutscene:
+			cl.intermission = 3;
+			cl.completed_time = cl.time;
+			vid.recalc_refdef = true;	// go to full screen
+			SCR_CenterPrint (MSG_ReadString ());			
+			break;
+
+		case svc_sellscreen:
+			Cmd_ExecuteString ("help", src_command);
+			break;
+		}
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cl_tent.c b/apps/plugins/sdl/progs/quake/cl_tent.c
new file mode 100644
index 0000000..546e832
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cl_tent.c
@@ -0,0 +1,394 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cl_tent.c -- client side temporary entities
+
+#include "quakedef.h"
+
+int			num_temp_entities;
+entity_t	cl_temp_entities[MAX_TEMP_ENTITIES];
+beam_t		cl_beams[MAX_BEAMS];
+
+sfx_t			*cl_sfx_wizhit;
+sfx_t			*cl_sfx_knighthit;
+sfx_t			*cl_sfx_tink1;
+sfx_t			*cl_sfx_ric1;
+sfx_t			*cl_sfx_ric2;
+sfx_t			*cl_sfx_ric3;
+sfx_t			*cl_sfx_r_exp3;
+#ifdef QUAKE2
+sfx_t			*cl_sfx_imp;
+sfx_t			*cl_sfx_rail;
+#endif
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+void CL_InitTEnts (void)
+{
+	cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
+	cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
+	cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
+	cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
+	cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
+	cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
+	cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
+#ifdef QUAKE2
+	cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav");
+	cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav");
+#endif
+}
+
+/*
+=================
+CL_ParseBeam
+=================
+*/
+void CL_ParseBeam (model_t *m)
+{
+	int		ent;
+	vec3_t	start, end;
+	beam_t	*b;
+	int		i;
+	
+	ent = MSG_ReadShort ();
+	
+	start[0] = MSG_ReadCoord ();
+	start[1] = MSG_ReadCoord ();
+	start[2] = MSG_ReadCoord ();
+	
+	end[0] = MSG_ReadCoord ();
+	end[1] = MSG_ReadCoord ();
+	end[2] = MSG_ReadCoord ();
+
+// override any beam with the same entity
+	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+		if (b->entity == ent)
+		{
+			b->entity = ent;
+			b->model = m;
+			b->endtime = cl.time + 0.2;
+			VectorCopy (start, b->start);
+			VectorCopy (end, b->end);
+			return;
+		}
+
+// find a free beam
+	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+	{
+		if (!b->model || b->endtime < cl.time)
+		{
+			b->entity = ent;
+			b->model = m;
+			b->endtime = cl.time + 0.2;
+			VectorCopy (start, b->start);
+			VectorCopy (end, b->end);
+			return;
+		}
+	}
+	Con_Printf ("beam list overflow!\n");	
+}
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+void CL_ParseTEnt (void)
+{
+	int		type;
+	vec3_t	pos;
+#ifdef QUAKE2
+	vec3_t	endpos;
+#endif
+	dlight_t	*dl;
+	int		rnd;
+	int		colorStart, colorLength;
+
+	type = MSG_ReadByte ();
+	switch (type)
+	{
+	case TE_WIZSPIKE:			// spike hitting wall
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_RunParticleEffect (pos, vec3_origin, 20, 30);
+		S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
+		break;
+		
+	case TE_KNIGHTSPIKE:			// spike hitting wall
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_RunParticleEffect (pos, vec3_origin, 226, 20);
+		S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
+		break;
+		
+	case TE_SPIKE:			// spike hitting wall
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+#ifdef GLTEST
+		Test_Spawn (pos);
+#else
+		R_RunParticleEffect (pos, vec3_origin, 0, 10);
+#endif
+		if ( rand() % 5 )
+			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+		else
+		{
+			rnd = rand() & 3;
+			if (rnd == 1)
+				S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+			else if (rnd == 2)
+				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+			else
+				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+		}
+		break;
+	case TE_SUPERSPIKE:			// super spike hitting wall
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_RunParticleEffect (pos, vec3_origin, 0, 20);
+
+		if ( rand() % 5 )
+			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+		else
+		{
+			rnd = rand() & 3;
+			if (rnd == 1)
+				S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
+			else if (rnd == 2)
+				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+			else
+				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+		}
+		break;
+		
+	case TE_GUNSHOT:			// bullet hitting wall
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_RunParticleEffect (pos, vec3_origin, 0, 20);
+		break;
+		
+	case TE_EXPLOSION:			// rocket explosion
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_ParticleExplosion (pos);
+		dl = CL_AllocDlight (0);
+		VectorCopy (pos, dl->origin);
+		dl->radius = 350;
+		dl->die = cl.time + 0.5;
+		dl->decay = 300;
+		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+		break;
+		
+	case TE_TAREXPLOSION:			// tarbaby explosion
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_BlobExplosion (pos);
+
+		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+		break;
+
+	case TE_LIGHTNING1:				// lightning bolts
+		CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
+		break;
+	
+	case TE_LIGHTNING2:				// lightning bolts
+		CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
+		break;
+	
+	case TE_LIGHTNING3:				// lightning bolts
+		CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
+		break;
+	
+// PGM 01/21/97 
+	case TE_BEAM:				// grappling hook beam
+		CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
+		break;
+// PGM 01/21/97
+
+	case TE_LAVASPLASH:	
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_LavaSplash (pos);
+		break;
+	
+	case TE_TELEPORT:
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		R_TeleportSplash (pos);
+		break;
+		
+	case TE_EXPLOSION2:				// color mapped explosion
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		colorStart = MSG_ReadByte ();
+		colorLength = MSG_ReadByte ();
+		R_ParticleExplosion2 (pos, colorStart, colorLength);
+		dl = CL_AllocDlight (0);
+		VectorCopy (pos, dl->origin);
+		dl->radius = 350;
+		dl->die = cl.time + 0.5;
+		dl->decay = 300;
+		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+		break;
+		
+#ifdef QUAKE2
+	case TE_IMPLOSION:
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1);
+		break;
+
+	case TE_RAILTRAIL:
+		pos[0] = MSG_ReadCoord ();
+		pos[1] = MSG_ReadCoord ();
+		pos[2] = MSG_ReadCoord ();
+		endpos[0] = MSG_ReadCoord ();
+		endpos[1] = MSG_ReadCoord ();
+		endpos[2] = MSG_ReadCoord ();
+		S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1);
+		S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1);
+		R_RocketTrail (pos, endpos, 0+128);
+		R_ParticleExplosion (endpos);
+		dl = CL_AllocDlight (-1);
+		VectorCopy (endpos, dl->origin);
+		dl->radius = 350;
+		dl->die = cl.time + 0.5;
+		dl->decay = 300;
+		break;
+#endif
+
+	default:
+		Sys_Error ("CL_ParseTEnt: bad type");
+	}
+}
+
+
+/*
+=================
+CL_NewTempEntity
+=================
+*/
+entity_t *CL_NewTempEntity (void)
+{
+	entity_t	*ent;
+
+	if (cl_numvisedicts == MAX_VISEDICTS)
+		return NULL;
+	if (num_temp_entities == MAX_TEMP_ENTITIES)
+		return NULL;
+	ent = &cl_temp_entities[num_temp_entities];
+	memset (ent, 0, sizeof(*ent));
+	num_temp_entities++;
+	cl_visedicts[cl_numvisedicts] = ent;
+	cl_numvisedicts++;
+
+	ent->colormap = vid.colormap;
+	return ent;
+}
+
+
+/*
+=================
+CL_UpdateTEnts
+=================
+*/
+void CL_UpdateTEnts (void)
+{
+	int			i;
+	beam_t		*b;
+	vec3_t		dist, org;
+	float		d;
+	entity_t	*ent;
+	float		yaw, pitch;
+	float		forward;
+
+	num_temp_entities = 0;
+
+// update lightning
+	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+	{
+		if (!b->model || b->endtime < cl.time)
+			continue;
+
+	// if coming from the player, update the start position
+		if (b->entity == cl.viewentity)
+		{
+			VectorCopy (cl_entities[cl.viewentity].origin, b->start);
+		}
+
+	// calculate pitch and yaw
+		VectorSubtract (b->end, b->start, dist);
+
+		if (dist[1] == 0 && dist[0] == 0)
+		{
+			yaw = 0;
+			if (dist[2] > 0)
+				pitch = 90;
+			else
+				pitch = 270;
+		}
+		else
+		{
+			yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
+			if (yaw < 0)
+				yaw += 360;
+	
+			forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+			pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
+			if (pitch < 0)
+				pitch += 360;
+		}
+
+	// add new entities for the lightning
+		VectorCopy (b->start, org);
+		d = VectorNormalize(dist);
+		while (d > 0)
+		{
+			ent = CL_NewTempEntity ();
+			if (!ent)
+				return;
+			VectorCopy (org, ent->origin);
+			ent->model = b->model;
+			ent->angles[0] = pitch;
+			ent->angles[1] = yaw;
+			ent->angles[2] = rand()%360;
+
+			for (i=0 ; i<3 ; i++)
+				org[i] += dist[i]*30;
+			d -= 30;
+		}
+	}
+	
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/clean.bat b/apps/plugins/sdl/progs/quake/clean.bat
new file mode 100644
index 0000000..8a5aad4
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/clean.bat
@@ -0,0 +1,18 @@
+rmdir /s /q debug
+rmdir /s /q release
+rmdir /s /q debug_gl
+rmdir /s /q release_gl
+
+rmdir /s /q gas2masm\debug
+rmdir /s /q gas2masm\release
+
+del gas2masm\gas2masm.opt
+del gas2masm\gas2masm.plg
+del gas2masm\gas2masm.ncb
+del gas2masm\gas2masm.stt
+
+del WinQuake.opt
+del WinQuake.plg
+del WinQuake.ncb
+del WinQuake.stt
+
diff --git a/apps/plugins/sdl/progs/quake/client.h b/apps/plugins/sdl/progs/quake/client.h
new file mode 100644
index 0000000..4d52bf9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/client.h
@@ -0,0 +1,376 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// client.h
+
+typedef struct
+{
+	vec3_t	viewangles;
+
+// intended velocities
+	float	forwardmove;
+	float	sidemove;
+	float	upmove;
+#ifdef QUAKE2
+	byte	lightlevel;
+#endif
+} usercmd_t;
+
+typedef struct
+{
+	int		length;
+	char	map[MAX_STYLESTRING];
+} lightstyle_t;
+
+typedef struct
+{
+	char	name[MAX_SCOREBOARDNAME];
+	float	entertime;
+	int		frags;
+	int		colors;			// two 4 bit fields
+	byte	translations[VID_GRADES*256];
+} scoreboard_t;
+
+typedef struct
+{
+	int		destcolor[3];
+	int		percent;		// 0-256
+} cshift_t;
+
+#define	CSHIFT_CONTENTS	0
+#define	CSHIFT_DAMAGE	1
+#define	CSHIFT_BONUS	2
+#define	CSHIFT_POWERUP	3
+#define	NUM_CSHIFTS		4
+
+#define	NAME_LENGTH	64
+
+
+//
+// client_state_t should hold all pieces of the client state
+//
+
+#define	SIGNONS		4			// signon messages to receive before connected
+
+#define	MAX_DLIGHTS		32
+typedef struct
+{
+	vec3_t	origin;
+	float	radius;
+	float	die;				// stop lighting after this time
+	float	decay;				// drop this each second
+	float	minlight;			// don't add when contributing less
+	int		key;
+#ifdef QUAKE2
+	qboolean	dark;			// subtracts light instead of adding
+#endif
+} dlight_t;
+
+
+#define	MAX_BEAMS	24
+typedef struct
+{
+	int		entity;
+	struct model_s	*model;
+	float	endtime;
+	vec3_t	start, end;
+} beam_t;
+
+#define	MAX_EFRAGS		640
+
+#define	MAX_MAPSTRING	2048
+#define	MAX_DEMOS		8
+#define	MAX_DEMONAME	16
+
+typedef int cactive_t;
+enum {
+ca_dedicated, 		// a dedicated server with no ability to start a client
+ca_disconnected, 	// full screen console with no connection
+ca_connected		// valid netcon, talking to a server
+};
+
+//
+// the client_static_t structure is persistant through an arbitrary number
+// of server connections
+//
+typedef struct
+{
+	cactive_t	state;
+
+// personalization data sent to server	
+	char		mapstring[MAX_QPATH];
+	char		spawnparms[MAX_MAPSTRING];	// to restart a level
+
+// demo loop control
+	int			demonum;		// -1 = don't play demos
+	char		demos[MAX_DEMOS][MAX_DEMONAME];		// when not playing
+
+// demo recording info must be here, because record is started before
+// entering a map (and clearing client_state_t)
+	qboolean	demorecording;
+	qboolean	demoplayback;
+	qboolean	timedemo;
+	int			forcetrack;			// -1 = use normal cd track
+	FILE		*demofile;
+	int			td_lastframe;		// to meter out one message a frame
+	int			td_startframe;		// host_framecount at start
+	float		td_starttime;		// realtime at second frame of timedemo
+
+
+// connection information
+	int			signon;			// 0 to SIGNONS
+	struct qsocket_s	*netcon;
+	sizebuf_t	message;		// writing buffer to send to server
+	
+} client_static_t;
+
+extern client_static_t	cls;
+
+//
+// the client_state_t structure is wiped completely at every
+// server signon
+//
+typedef struct
+{
+	int			movemessages;	// since connecting to this server
+								// throw out the first couple, so the player
+								// doesn't accidentally do something the 
+								// first frame
+	usercmd_t	cmd;			// last command sent to the server
+
+// information for local display
+	int			stats[MAX_CL_STATS];	// health, etc
+	int			items;			// inventory bit flags
+	float	item_gettime[32];	// cl.time of aquiring item, for blinking
+	float		faceanimtime;	// use anim frame if cl.time < this
+
+	cshift_t	cshifts[NUM_CSHIFTS];	// color shifts for damage, powerups
+	cshift_t	prev_cshifts[NUM_CSHIFTS];	// and content types
+
+// the client maintains its own idea of view angles, which are
+// sent to the server each frame.  The server sets punchangle when
+// the view is temporarliy offset, and an angle reset commands at the start
+// of each level and after teleporting.
+	vec3_t		mviewangles[2];	// during demo playback viewangles is lerped
+								// between these
+	vec3_t		viewangles;
+	
+	vec3_t		mvelocity[2];	// update by server, used for lean+bob
+								// (0 is newest)
+	vec3_t		velocity;		// lerped between mvelocity[0] and [1]
+
+	vec3_t		punchangle;		// temporary offset
+	
+// pitch drifting vars
+	float		idealpitch;
+	float		pitchvel;
+	qboolean	nodrift;
+	float		driftmove;
+	double		laststop;
+
+	float		viewheight;
+	float		crouch;			// local amount for smoothing stepups
+
+	qboolean	paused;			// send over by server
+	qboolean	onground;
+	qboolean	inwater;
+	
+	int			intermission;	// don't change view angle, full screen, etc
+	int			completed_time;	// latched at intermission start
+	
+	double		mtime[2];		// the timestamp of last two messages	
+	double		time;			// clients view of time, should be between
+								// servertime and oldservertime to generate
+								// a lerp point for other data
+	double		oldtime;		// previous cl.time, time-oldtime is used
+								// to decay light values and smooth step ups
+	
+
+	float		last_received_message;	// (realtime) for net trouble icon
+
+//
+// information that is static for the entire time connected to a server
+//
+	struct model_s		*model_precache[MAX_MODELS];
+	struct sfx_s		*sound_precache[MAX_SOUNDS];
+
+	char		levelname[40];	// for display on solo scoreboard
+	int			viewentity;		// cl_entitites[cl.viewentity] = player
+	int			maxclients;
+	int			gametype;
+
+// refresh related state
+	struct model_s	*worldmodel;	// cl_entitites[0].model
+	struct efrag_s	*free_efrags;
+	int			num_entities;	// held in cl_entities array
+	int			num_statics;	// held in cl_staticentities array
+	entity_t	viewent;			// the gun model
+
+	int			cdtrack, looptrack;	// cd audio
+
+// frag scoreboard
+	scoreboard_t	*scores;		// [cl.maxclients]
+
+#ifdef QUAKE2
+// light level at player's position including dlights
+// this is sent back to the server each frame
+// architectually ugly but it works
+	int			light_level;
+#endif
+} client_state_t;
+
+
+//
+// cvars
+//
+extern	cvar_t	cl_name;
+extern	cvar_t	cl_color;
+
+extern	cvar_t	cl_upspeed;
+extern	cvar_t	cl_forwardspeed;
+extern	cvar_t	cl_backspeed;
+extern	cvar_t	cl_sidespeed;
+
+extern	cvar_t	cl_movespeedkey;
+
+extern	cvar_t	cl_yawspeed;
+extern	cvar_t	cl_pitchspeed;
+
+extern	cvar_t	cl_anglespeedkey;
+
+extern	cvar_t	cl_autofire;
+
+extern	cvar_t	cl_shownet;
+extern	cvar_t	cl_nolerp;
+
+extern	cvar_t	cl_pitchdriftspeed;
+extern	cvar_t	lookspring;
+extern	cvar_t	lookstrafe;
+extern	cvar_t	sensitivity;
+
+extern	cvar_t	m_pitch;
+extern	cvar_t	m_yaw;
+extern	cvar_t	m_forward;
+extern	cvar_t	m_side;
+
+
+#define	MAX_TEMP_ENTITIES	64			// lightning bolts, etc
+#define	MAX_STATIC_ENTITIES	128			// torches, etc
+
+extern	client_state_t	cl;
+
+// FIXME, allocate dynamically
+extern	efrag_t			cl_efrags[MAX_EFRAGS];
+extern	entity_t		cl_entities[MAX_EDICTS];
+extern	entity_t		cl_static_entities[MAX_STATIC_ENTITIES];
+extern	lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
+extern	dlight_t		cl_dlights[MAX_DLIGHTS];
+extern	entity_t		cl_temp_entities[MAX_TEMP_ENTITIES];
+extern	beam_t			cl_beams[MAX_BEAMS];
+
+//=============================================================================
+
+//
+// cl_main
+//
+dlight_t *CL_AllocDlight (int key);
+void	CL_DecayLights (void);
+
+void CL_Init (void);
+
+void CL_EstablishConnection (char *host);
+void CL_Signon1 (void);
+void CL_Signon2 (void);
+void CL_Signon3 (void);
+void CL_Signon4 (void);
+
+void CL_Disconnect (void);
+void CL_Disconnect_f (void);
+void CL_NextDemo (void);
+
+#define			MAX_VISEDICTS	256
+extern	int				cl_numvisedicts;
+extern	entity_t		*cl_visedicts[MAX_VISEDICTS];
+
+//
+// cl_input
+//
+typedef struct
+{
+	int		down[2];		// key nums holding it down
+	int		state;			// low bit is down state
+} kbutton_t;
+
+extern	kbutton_t	in_mlook, in_klook;
+extern 	kbutton_t 	in_strafe;
+extern 	kbutton_t 	in_speed;
+
+void CL_InitInput (void);
+void CL_SendCmd (void);
+void CL_SendMove (usercmd_t *cmd);
+
+void CL_ParseTEnt (void);
+void CL_UpdateTEnts (void);
+
+void CL_ClearState (void);
+
+
+int  CL_ReadFromServer (void);
+void CL_WriteToServer (usercmd_t *cmd);
+void CL_BaseMove (usercmd_t *cmd);
+
+
+float CL_KeyState (kbutton_t *key);
+char *Key_KeynumToString (int keynum);
+
+//
+// cl_demo.c
+//
+void CL_StopPlayback (void);
+int CL_GetMessage (void);
+
+void CL_Stop_f (void);
+void CL_Record_f (void);
+void CL_PlayDemo_f (void);
+void CL_TimeDemo_f (void);
+
+//
+// cl_parse.c
+//
+void CL_ParseServerMessage (void);
+void CL_NewTranslation (int slot);
+
+//
+// view
+//
+void V_StartPitchDrift (void);
+void V_StopPitchDrift (void);
+
+void V_RenderView (void);
+void V_UpdatePalette (void);
+void V_Register (void);
+void V_ParseDamage (void);
+void V_SetContentsColor (int contents);
+
+
+//
+// cl_tent
+//
+void CL_InitTEnts (void);
+void CL_SignonReply (void);
diff --git a/apps/plugins/sdl/progs/quake/cmd.c b/apps/plugins/sdl/progs/quake/cmd.c
new file mode 100644
index 0000000..9580706
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cmd.c
@@ -0,0 +1,721 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cmd.c -- Quake script command processing module
+
+#include "quakedef.h"
+
+void Cmd_ForwardToServer (void);
+
+#define	MAX_ALIAS_NAME	32
+
+typedef struct cmdalias_s
+{
+	struct cmdalias_s	*next;
+	char	name[MAX_ALIAS_NAME];
+	char	*value;
+} cmdalias_t;
+
+cmdalias_t	*cmd_alias;
+
+int trashtest;
+int *trashspot;
+
+qboolean	cmd_wait;
+
+//=============================================================================
+
+/*
+============
+Cmd_Wait_f
+
+Causes execution of the remainder of the command buffer to be delayed until
+next frame.  This allows commands like:
+bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
+============
+*/
+void Cmd_Wait_f (void)
+{
+	cmd_wait = true;
+}
+
+/*
+=============================================================================
+
+						COMMAND BUFFER
+
+=============================================================================
+*/
+
+sizebuf_t	cmd_text;
+
+/*
+============
+Cbuf_Init
+============
+*/
+void Cbuf_Init (void)
+{
+	SZ_Alloc (&cmd_text, 8192);		// space for commands and script files
+}
+
+
+/*
+============
+Cbuf_AddText
+
+Adds command text at the end of the buffer
+============
+*/
+void Cbuf_AddText (char *text)
+{
+	int		l;
+	
+	l = Q_strlen (text);
+
+	if (cmd_text.cursize + l >= cmd_text.maxsize)
+	{
+		Con_Printf ("Cbuf_AddText: overflow\n");
+		return;
+	}
+
+	SZ_Write (&cmd_text, text, Q_strlen (text));
+}
+
+
+/*
+============
+Cbuf_InsertText
+
+FW19: before???
+
+Adds command text immediately after the current command
+Adds a \n to the text
+FIXME: actually change the command buffer to do less copying
+============
+*/
+void Cbuf_InsertText (char *text)
+{
+	char	*temp;
+	int		templen;
+
+// copy off any commands still remaining in the exec buffer
+	templen = cmd_text.cursize;
+	if (templen)
+	{
+		temp = Z_Malloc (templen);
+		Q_memcpy (temp, cmd_text.data, templen);
+		SZ_Clear (&cmd_text);
+	}
+	else
+		temp = NULL;	// shut up compiler
+		
+// add the entire text of the file
+	Cbuf_AddText (text);
+	
+// add the copied off data
+	if (templen)
+	{
+		SZ_Write (&cmd_text, temp, templen);
+		Z_Free (temp);
+	}
+}
+
+/*
+============
+Cbuf_Execute
+============
+*/
+void Cbuf_Execute (void)
+{
+	int		i;
+	char	*text;
+	char	line[1024];
+	int		quotes;
+	
+	while (cmd_text.cursize)
+	{
+// find a \n or ; line break
+		text = (char *)cmd_text.data;
+
+		quotes = 0;
+		for (i=0 ; i< cmd_text.cursize ; i++)
+		{
+			if (text[i] == '"')
+				quotes++;
+			if ( !(quotes&1) &&  text[i] == ';')
+				break;	// don't break if inside a quoted string
+			if (text[i] == '\n')
+				break;
+		}
+			
+				
+		memcpy (line, text, i);
+		line[i] = 0;
+		
+// delete the text from the command buffer and move remaining commands down
+// this is necessary because commands (exec, alias) can insert data at the
+// beginning of the text buffer
+
+		if (i == cmd_text.cursize)
+			cmd_text.cursize = 0;
+		else
+		{
+			i++;
+			cmd_text.cursize -= i;
+			Q_memcpy (text, text+i, cmd_text.cursize);
+		}
+
+// execute the command line
+		Cmd_ExecuteString (line, src_command);
+		
+		if (cmd_wait)
+		{	// skip out while text still remains in buffer, leaving it
+			// for next frame
+			cmd_wait = false;
+			break;
+		}
+	}
+}
+
+/*
+==============================================================================
+
+						SCRIPT COMMANDS
+
+==============================================================================
+*/
+
+/*
+===============
+Cmd_StuffCmds_f
+
+Adds command line parameters as script statements
+Commands lead with a +, and continue until a - or another +
+quake +prog jctest.qp +cmd amlev1
+quake -nosound +cmd amlev1
+===============
+*/
+void Cmd_StuffCmds_f (void)
+{
+	int		i, j;
+	int		s;
+	char	*text, *build, c;
+		
+	if (Cmd_Argc () != 1)
+	{
+		Con_Printf ("stuffcmds : execute command line parameters\n");
+		return;
+	}
+
+// build the combined string to parse from
+	s = 0;
+	for (i=1 ; i<com_argc ; i++)
+	{
+		if (!com_argv[i])
+			continue;		// NEXTSTEP nulls out -NXHost
+		s += Q_strlen (com_argv[i]) + 1;
+	}
+	if (!s)
+		return;
+		
+	text = Z_Malloc (s+1);
+	text[0] = 0;
+	for (i=1 ; i<com_argc ; i++)
+	{
+		if (!com_argv[i])
+			continue;		// NEXTSTEP nulls out -NXHost
+		Q_strcat (text,com_argv[i]);
+		if (i != com_argc-1)
+			Q_strcat (text, " ");
+	}
+	
+// pull out the commands
+	build = Z_Malloc (s+1);
+	build[0] = 0;
+	
+	for (i=0 ; i<s-1 ; i++)
+	{
+		if (text[i] == '+')
+		{
+			i++;
+
+			for (j=i ; (text[j] != '+') && (text[j] != '-') && (text[j] != 0) ; j++)
+				;
+
+			c = text[j];
+			text[j] = 0;
+			
+			Q_strcat (build, text+i);
+			Q_strcat (build, "\n");
+			text[j] = c;
+			i = j-1;
+		}
+	}
+	
+	if (build[0])
+		Cbuf_InsertText (build);
+	
+	Z_Free (text);
+	Z_Free (build);
+}
+
+extern int bind_nooverride;
+
+/*
+===============
+Cmd_Exec_f
+===============
+*/
+void Cmd_Exec_f (void)
+{
+	char	*f;
+	int		mark;
+
+	if (Cmd_Argc () != 2)
+	{
+		Con_Printf ("exec <filename> : execute a script file\n");
+		return;
+	}
+        
+        
+	mark = Hunk_LowMark ();
+	f = (char *)COM_LoadHunkFile (Cmd_Argv(1));
+	if (!f)
+	{
+		Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
+		return;
+	}
+	Con_Printf ("execing %s\n",Cmd_Argv(1));
+	
+	Cbuf_InsertText (f); // insert to next space in buffer
+	Hunk_FreeToLowMark (mark);
+
+        // if we are running config, tell bind not to override defaults (see keys.c)
+        if(!strcmp(Cmd_Argv(1), "config.cfg"))
+        {
+            // force binds to continue
+            bind_nooverride = 1;
+        }
+        else if(!strcmp(Cmd_Argv(1), "default.cfg"))
+        {
+            // allow override
+            bind_nooverride = 0;
+        }
+}
+
+
+/*
+===============
+Cmd_Echo_f
+
+Just prints the rest of the line to the console
+===============
+*/
+void Cmd_Echo_f (void)
+{
+	int		i;
+	
+	for (i=1 ; i<Cmd_Argc() ; i++)
+		Con_Printf ("%s ",Cmd_Argv(i));
+	Con_Printf ("\n");
+}
+
+/*
+===============
+Cmd_Alias_f
+
+Creates a new command that executes a command string (possibly ; seperated)
+===============
+*/
+
+char *CopyString (char *in)
+{
+	char	*out;
+	
+	out = Z_Malloc (strlen(in)+1);
+	strcpy (out, in);
+	return out;
+}
+
+void Cmd_Alias_f (void)
+{
+	cmdalias_t	*a;
+	char		cmd[1024];
+	int			i, c;
+	char		*s;
+
+	if (Cmd_Argc() == 1)
+	{
+		Con_Printf ("Current alias commands:\n");
+		for (a = cmd_alias ; a ; a=a->next)
+			Con_Printf ("%s : %s\n", a->name, a->value);
+		return;
+	}
+
+	s = Cmd_Argv(1);
+	if (strlen(s) >= MAX_ALIAS_NAME)
+	{
+		Con_Printf ("Alias name is too long\n");
+		return;
+	}
+
+	// if the alias allready exists, reuse it
+	for (a = cmd_alias ; a ; a=a->next)
+	{
+		if (!strcmp(s, a->name))
+		{
+			Z_Free (a->value);
+			break;
+		}
+	}
+
+	if (!a)
+	{
+		a = Z_Malloc (sizeof(cmdalias_t));
+		a->next = cmd_alias;
+		cmd_alias = a;
+	}
+	strcpy (a->name, s);	
+
+// copy the rest of the command line
+	cmd[0] = 0;		// start out with a null string
+	c = Cmd_Argc();
+	for (i=2 ; i< c ; i++)
+	{
+		strcat (cmd, Cmd_Argv(i));
+		if (i != c)
+			strcat (cmd, " ");
+	}
+	strcat (cmd, "\n");
+	
+	a->value = CopyString (cmd);
+}
+
+/*
+=============================================================================
+
+					COMMAND EXECUTION
+
+=============================================================================
+*/
+
+typedef struct cmd_function_s
+{
+	struct cmd_function_s	*next;
+	char					*name;
+	xcommand_t				function;
+} cmd_function_t;
+
+
+#define	MAX_ARGS		80
+
+static	int			cmd_argc;
+static	char		*cmd_argv[MAX_ARGS];
+static	char		*cmd_null_string = "";
+static	char		*cmd_args = NULL;
+
+cmd_source_t	cmd_source;
+
+
+static	cmd_function_t	*cmd_functions;		// possible commands to execute
+
+/*
+============
+Cmd_Init
+============
+*/
+void Cmd_Init (void)
+{
+//
+// register our commands
+//
+	Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
+	Cmd_AddCommand ("exec",Cmd_Exec_f);
+	Cmd_AddCommand ("echo",Cmd_Echo_f);
+	Cmd_AddCommand ("alias",Cmd_Alias_f);
+	Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
+	Cmd_AddCommand ("wait", Cmd_Wait_f);
+}
+
+/*
+============
+Cmd_Argc
+============
+*/
+int		Cmd_Argc (void)
+{
+	return cmd_argc;
+}
+
+/*
+============
+Cmd_Argv
+============
+*/
+char	*Cmd_Argv (int arg)
+{
+	if ( (unsigned)arg >= cmd_argc )
+		return cmd_null_string;
+	return cmd_argv[arg];	
+}
+
+/*
+============
+Cmd_Args
+============
+*/
+char		*Cmd_Args (void)
+{
+	return cmd_args;
+}
+
+
+/*
+============
+Cmd_TokenizeString
+
+Parses the given string into command line tokens.
+============
+*/
+void Cmd_TokenizeString (char *text)
+{
+	int		i;
+	
+// clear the args from the last string
+	for (i=0 ; i<cmd_argc ; i++)
+		Z_Free (cmd_argv[i]);
+		
+	cmd_argc = 0;
+	cmd_args = NULL;
+	
+	while (1)
+	{
+// skip whitespace up to a /n
+		while (*text && *text <= ' ' && *text != '\n')
+		{
+			text++;
+		}
+		
+		if (*text == '\n')
+		{	// a newline seperates commands in the buffer
+			text++;
+			break;
+		}
+
+		if (!*text)
+			return;
+	
+		if (cmd_argc == 1)
+			 cmd_args = text;
+			
+		text = COM_Parse (text);
+		if (!text)
+			return;
+
+		if (cmd_argc < MAX_ARGS)
+		{
+			cmd_argv[cmd_argc] = Z_Malloc (Q_strlen(com_token)+1);
+			Q_strcpy (cmd_argv[cmd_argc], com_token);
+			cmd_argc++;
+		}
+	}
+	
+}
+
+
+/*
+============
+Cmd_AddCommand
+============
+*/
+void	Cmd_AddCommand (char *cmd_name, xcommand_t function)
+{
+	cmd_function_t	*cmd;
+	
+	if (host_initialized)	// because hunk allocation would get stomped
+		Sys_Error ("Cmd_AddCommand after host_initialized");
+		
+// fail if the command is a variable name
+	if (Cvar_VariableString(cmd_name)[0])
+	{
+		Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
+		return;
+	}
+	
+// fail if the command already exists
+	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+	{
+		if (!Q_strcmp (cmd_name, cmd->name))
+		{
+			Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
+			return;
+		}
+	}
+
+	cmd = Hunk_Alloc (sizeof(cmd_function_t));
+	cmd->name = cmd_name;
+	cmd->function = function;
+	cmd->next = cmd_functions;
+	cmd_functions = cmd;
+}
+
+/*
+============
+Cmd_Exists
+============
+*/
+qboolean	Cmd_Exists (char *cmd_name)
+{
+	cmd_function_t	*cmd;
+
+	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+	{
+		if (!Q_strcmp (cmd_name,cmd->name))
+			return true;
+	}
+
+	return false;
+}
+
+
+
+/*
+============
+Cmd_CompleteCommand
+============
+*/
+char *Cmd_CompleteCommand (char *partial)
+{
+	cmd_function_t	*cmd;
+	int				len;
+	
+	len = Q_strlen(partial);
+	
+	if (!len)
+		return NULL;
+		
+// check functions
+	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+		if (!Q_strncmp (partial,cmd->name, len))
+			return cmd->name;
+
+	return NULL;
+}
+
+/*
+============
+Cmd_ExecuteString
+
+A complete command line has been parsed, so try to execute it
+FIXME: lookupnoadd the token to speed search?
+============
+*/
+void	Cmd_ExecuteString (char *text, cmd_source_t src)
+{	
+	cmd_function_t	*cmd;
+	cmdalias_t		*a;
+
+	cmd_source = src;
+	Cmd_TokenizeString (text);
+			
+// execute the command line
+	if (!Cmd_Argc())
+		return;		// no tokens
+
+// check functions
+	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
+	{
+		if (!Q_strcasecmp (cmd_argv[0],cmd->name))
+		{
+			cmd->function ();
+			return;
+		}
+	}
+
+// check alias
+	for (a=cmd_alias ; a ; a=a->next)
+	{
+		if (!Q_strcasecmp (cmd_argv[0], a->name))
+		{
+			Cbuf_InsertText (a->value);
+			return;
+		}
+	}
+	
+// check cvars
+	if (!Cvar_Command ())
+		Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
+	
+}
+
+
+/*
+===================
+Cmd_ForwardToServer
+
+Sends the entire command line over to the server
+===================
+*/
+void Cmd_ForwardToServer (void)
+{
+	if (cls.state != ca_connected)
+	{
+		Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
+		return;
+	}
+	
+	if (cls.demoplayback)
+		return;		// not really connected
+
+	MSG_WriteByte (&cls.message, clc_stringcmd);
+	if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
+	{
+		SZ_Print (&cls.message, Cmd_Argv(0));
+		SZ_Print (&cls.message, " ");
+	}
+	if (Cmd_Argc() > 1)
+		SZ_Print (&cls.message, Cmd_Args());
+	else
+		SZ_Print (&cls.message, "\n");
+}
+
+
+/*
+================
+Cmd_CheckParm
+
+Returns the position (1 to argc-1) in the command's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+
+int Cmd_CheckParm (char *parm)
+{
+	int i;
+	
+	if (!parm)
+		Sys_Error ("Cmd_CheckParm: NULL");
+
+	for (i = 1; i < Cmd_Argc (); i++)
+		if (! Q_strcasecmp (parm, Cmd_Argv (i)))
+			return i;
+			
+	return 0;
+}
diff --git a/apps/plugins/sdl/progs/quake/cmd.h b/apps/plugins/sdl/progs/quake/cmd.h
new file mode 100644
index 0000000..d4a0dd9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cmd.h
@@ -0,0 +1,123 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// cmd.h -- Command buffer and command execution
+
+//===========================================================================
+
+/*
+
+Any number of commands can be added in a frame, from several different sources.
+Most commands come from either keybindings or console line input, but remote
+servers can also send across commands and entire text files can be execed.
+
+The + command line options are also added to the command buffer.
+
+The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
+
+*/
+
+
+void Cbuf_Init (void);
+// allocates an initial text buffer that will grow as needed
+
+void Cbuf_AddText (char *text);
+// as new commands are generated from the console or keybindings,
+// the text is added to the end of the command buffer.
+
+void Cbuf_InsertText (char *text);
+// when a command wants to issue other commands immediately, the text is
+// inserted at the beginning of the buffer, before any remaining unexecuted
+// commands.
+
+void Cbuf_Execute (void);
+// Pulls off \n terminated lines of text from the command buffer and sends
+// them through Cmd_ExecuteString.  Stops when the buffer is empty.
+// Normally called once per frame, but may be explicitly invoked.
+// Do not call inside a command function!
+
+//===========================================================================
+
+/*
+
+Command execution takes a null terminated string, breaks it into tokens,
+then searches for a command or variable that matches the first token.
+
+Commands can come from three sources, but the handler functions may choose
+to dissallow the action or forward it to a remote server if the source is
+not apropriate.
+
+*/
+
+typedef void (*xcommand_t) (void);
+
+typedef int cmd_source_t;
+
+enum
+{
+	src_client,		// came in over a net connection as a clc_stringcmd
+					// host_client will be valid during this state.
+	src_command		// from the command buffer
+};
+
+extern	cmd_source_t	cmd_source;
+
+void	Cmd_Init (void);
+
+void	Cmd_AddCommand (char *cmd_name, xcommand_t function);
+// called by the init functions of other parts of the program to
+// register commands and functions to call for them.
+// The cmd_name is referenced later, so it should not be in temp memory
+
+qboolean Cmd_Exists (char *cmd_name);
+// used by the cvar code to check for cvar / command name overlap
+
+char 	*Cmd_CompleteCommand (char *partial);
+// attempts to match a partial command for automatic command line completion
+// returns NULL if nothing fits
+
+int		Cmd_Argc (void);
+char	*Cmd_Argv (int arg);
+char	*Cmd_Args (void);
+// The functions that execute commands get their parameters with these
+// functions. Cmd_Argv () will return an empty string, not a NULL
+// if arg > argc, so string operations are allways safe.
+
+int Cmd_CheckParm (char *parm);
+// Returns the position (1 to argc-1) in the command's argument list
+// where the given parameter apears, or 0 if not present
+
+void Cmd_TokenizeString (char *text);
+// Takes a null terminated string.  Does not need to be /n terminated.
+// breaks the string up into arg tokens.
+
+void	Cmd_ExecuteString (char *text, cmd_source_t src);
+// Parses a single line of text into arguments and tries to execute it.
+// The text can come from the command buffer, a remote client, or stdin.
+
+void	Cmd_ForwardToServer (void);
+// adds the current command line as a clc_stringcmd to the client message.
+// things like godmode, noclip, etc, are commands directed to the server,
+// so when they are typed in at the console, they will need to be forwarded.
+
+void	Cmd_Print (char *text);
+// used by command functions to send output to either the graphics console or
+// passed as a print message to the client
+
diff --git a/apps/plugins/sdl/progs/quake/common.c b/apps/plugins/sdl/progs/quake/common.c
new file mode 100644
index 0000000..11be234
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/common.c
@@ -0,0 +1,1856 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// common.c -- misc functions used in client and server
+
+#include "quakedef.h"
+
+#define NUM_SAFE_ARGVS  7
+
+static char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
+static char     *argvdummy = " ";
+
+static char     *safeargvs[NUM_SAFE_ARGVS] =
+	{"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
+
+cvar_t  registered = {"registered","0"};
+cvar_t  cmdline = {"cmdline","0", false, true};
+
+qboolean        com_modified;   // set true if using non-id files
+
+qboolean		proghack;
+
+int             static_registered = 1;  // only for startup check, then set
+
+qboolean		msg_suppress_1 = 0;
+
+void COM_InitFilesystem (void);
+
+// if a packfile directory differs from this, it is assumed to be hacked
+#define PAK0_COUNT              339
+#define PAK0_CRC                32981
+
+char	com_token[1024];
+int		com_argc;
+char	**com_argv;
+
+#define CMDLINE_LENGTH	256
+char	com_cmdline[CMDLINE_LENGTH];
+
+qboolean		standard_quake = true, rogue, hipnotic;
+
+// this graphic needs to be in the pak file to use registered features
+unsigned short pop[] =
+{
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000
+,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000
+,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600
+,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563
+,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564
+,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564
+,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563
+,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500
+,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200
+,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000
+,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000
+,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000
+};
+
+/*
+
+
+All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
+
+The "base directory" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.  The base directory is
+only used during filesystem initialization.
+
+The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to.  This can be overridden with the "-game" command line parameter.  The game directory can never be changed while quake is executing.  This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
+
+The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory
+specified, when a file is found by the normal search path, it will be mirrored
+into the cache directory, then opened there.
+
+
+
+FIXME:
+The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently.  This could be used to add a "-sspeed 22050" for the high quality sound edition.  Because they are added at the end, they will not override an explicit setting on the original command line.
+	
+*/
+
+//============================================================================
+
+
+// ClearLink is used for new headnodes
+void ClearLink (link_t *l)
+{
+	l->prev = l->next = l;
+}
+
+void RemoveLink (link_t *l)
+{
+	l->next->prev = l->prev;
+	l->prev->next = l->next;
+}
+
+void InsertLinkBefore (link_t *l, link_t *before)
+{
+	l->next = before;
+	l->prev = before->prev;
+	l->prev->next = l;
+	l->next->prev = l;
+}
+void InsertLinkAfter (link_t *l, link_t *after)
+{
+	l->next = after->next;
+	l->prev = after;
+	l->prev->next = l;
+	l->next->prev = l;
+}
+
+/*
+============================================================================
+
+					LIBRARY REPLACEMENT FUNCTIONS
+
+============================================================================
+*/
+
+void Q_memset (void *dest, int fill, int count)
+{
+	int             i;
+	
+	if ( (((long)dest | count) & 3) == 0)
+	{
+		count >>= 2;
+		fill = fill | (fill<<8) | (fill<<16) | (fill<<24);
+		for (i=0 ; i<count ; i++)
+			((int *)dest)[i] = fill;
+	}
+	else
+		for (i=0 ; i<count ; i++)
+			((byte *)dest)[i] = fill;
+}
+
+void Q_memcpy (void *dest, void *src, int count)
+{
+	int             i;
+	
+	if (( ( (long)dest | (long)src | count) & 3) == 0 )
+	{
+		count>>=2;
+		for (i=0 ; i<count ; i++)
+			((int *)dest)[i] = ((int *)src)[i];
+	}
+	else
+		for (i=0 ; i<count ; i++)
+			((byte *)dest)[i] = ((byte *)src)[i];
+}
+
+int Q_memcmp (void *m1, void *m2, int count)
+{
+	while(count)
+	{
+		count--;
+		if (((byte *)m1)[count] != ((byte *)m2)[count])
+			return -1;
+	}
+	return 0;
+}
+
+void Q_strcpy (char *dest, char *src)
+{
+	while (*src)
+	{
+		*dest++ = *src++;
+	}
+	*dest++ = 0;
+}
+
+void Q_strncpy (char *dest, char *src, int count)
+{
+	while (*src && count--)
+	{
+		*dest++ = *src++;
+	}
+	if (count)
+		*dest++ = 0;
+}
+
+int Q_strlen (char *str)
+{
+	int             count;
+	
+	count = 0;
+	while (str[count])
+		count++;
+
+	return count;
+}
+
+char *Q_strrchr(char *s, char c)
+{
+    int len = Q_strlen(s);
+    s += len;
+    while (len--)
+	if (*--s == c) return s;
+    return 0;
+}
+
+void Q_strcat (char *dest, char *src)
+{
+	dest += Q_strlen(dest);
+	Q_strcpy (dest, src);
+}
+
+int Q_strcmp (char *s1, char *s2)
+{
+	while (1)
+	{
+		if (*s1 != *s2)
+			return -1;              // strings not equal    
+		if (!*s1)
+			return 0;               // strings are equal
+		s1++;
+		s2++;
+	}
+	
+	return -1;
+}
+
+int Q_strncmp (char *s1, char *s2, int count)
+{
+	while (1)
+	{
+		if (!count--)
+			return 0;
+		if (*s1 != *s2)
+			return -1;              // strings not equal    
+		if (!*s1)
+			return 0;               // strings are equal
+		s1++;
+		s2++;
+	}
+	
+	return -1;
+}
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+	int             c1, c2;
+	
+	while (1)
+	{
+		c1 = *s1++;
+		c2 = *s2++;
+
+		if (!n--)
+			return 0;               // strings are equal until end point
+		
+		if (c1 != c2)
+		{
+			if (c1 >= 'a' && c1 <= 'z')
+				c1 -= ('a' - 'A');
+			if (c2 >= 'a' && c2 <= 'z')
+				c2 -= ('a' - 'A');
+			if (c1 != c2)
+				return -1;              // strings not equal
+		}
+		if (!c1)
+			return 0;               // strings are equal
+//              s1++;
+//              s2++;
+	}
+	
+	return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+	return Q_strncasecmp (s1, s2, 99999);
+}
+
+int Q_atoi (char *str)
+{
+	int             val;
+	int             sign;
+	int             c;
+	
+	if (*str == '-')
+	{
+		sign = -1;
+		str++;
+	}
+	else
+		sign = 1;
+		
+	val = 0;
+
+//
+// check for hex
+//
+	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+	{
+		str += 2;
+		while (1)
+		{
+			c = *str++;
+			if (c >= '0' && c <= '9')
+				val = (val<<4) + c - '0';
+			else if (c >= 'a' && c <= 'f')
+				val = (val<<4) + c - 'a' + 10;
+			else if (c >= 'A' && c <= 'F')
+				val = (val<<4) + c - 'A' + 10;
+			else
+				return val*sign;
+		}
+	}
+	
+//
+// check for character
+//
+	if (str[0] == '\'')
+	{
+		return sign * str[1];
+	}
+	
+//
+// assume decimal
+//
+	while (1)
+	{
+		c = *str++;
+		if (c <'0' || c > '9')
+			return val*sign;
+		val = val*10 + c - '0';
+	}
+	
+	return 0;
+}
+
+
+float Q_atof (char *str)
+{
+	double			val;
+	int             sign;
+	int             c;
+	int             decimal, total;
+	
+	if (*str == '-')
+	{
+		sign = -1;
+		str++;
+	}
+	else
+		sign = 1;
+		
+	val = 0;
+
+//
+// check for hex
+//
+	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+	{
+		str += 2;
+		while (1)
+		{
+			c = *str++;
+			if (c >= '0' && c <= '9')
+				val = (val*16) + c - '0';
+			else if (c >= 'a' && c <= 'f')
+				val = (val*16) + c - 'a' + 10;
+			else if (c >= 'A' && c <= 'F')
+				val = (val*16) + c - 'A' + 10;
+			else
+				return val*sign;
+		}
+	}
+	
+//
+// check for character
+//
+	if (str[0] == '\'')
+	{
+		return sign * str[1];
+	}
+	
+//
+// assume decimal
+//
+	decimal = -1;
+	total = 0;
+	while (1)
+	{
+		c = *str++;
+		if (c == '.')
+		{
+			decimal = total;
+			continue;
+		}
+		if (c <'0' || c > '9')
+			break;
+		val = val*10 + c - '0';
+		total++;
+	}
+
+	if (decimal == -1)
+		return val*sign;
+	while (total > decimal)
+	{
+		val /= 10;
+		total--;
+	}
+	
+	return val*sign;
+}
+
+/*
+============================================================================
+
+					BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef SDL
+#include "SDL_byteorder.h"
+#endif
+
+qboolean        bigendien;
+
+short   (*BigShort) (short l);
+short   (*LittleShort) (short l);
+int     (*BigLong) (int l);
+int     (*LittleLong) (int l);
+float   (*BigFloat) (float l);
+float   (*LittleFloat) (float l);
+
+short   ShortSwap (short l)
+{
+	byte    b1,b2;
+
+	b1 = l&255;
+	b2 = (l>>8)&255;
+
+	return (b1<<8) + b2;
+}
+
+short   ShortNoSwap (short l)
+{
+	return l;
+}
+
+int    LongSwap (int l)
+{
+	byte    b1,b2,b3,b4;
+
+	b1 = l&255;
+	b2 = (l>>8)&255;
+	b3 = (l>>16)&255;
+	b4 = (l>>24)&255;
+
+	return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int     LongNoSwap (int l)
+{
+	return l;
+}
+
+float FloatSwap (float f)
+{
+	union
+	{
+		float   f;
+		byte    b[4];
+	} dat1, dat2;
+	
+	
+	dat1.f = f;
+	dat2.b[0] = dat1.b[3];
+	dat2.b[1] = dat1.b[2];
+	dat2.b[2] = dat1.b[1];
+	dat2.b[3] = dat1.b[0];
+	return dat2.f;
+}
+
+float FloatNoSwap (float f)
+{
+	return f;
+}
+
+/*
+==============================================================================
+
+			MESSAGE IO FUNCTIONS
+
+Handles byte ordering and avoids alignment errors
+==============================================================================
+*/
+
+//
+// writing functions
+//
+
+void MSG_WriteChar (sizebuf_t *sb, int c)
+{
+	byte    *buf;
+	
+#ifdef PARANOID
+	if (c < -128 || c > 127)
+		Sys_Error ("MSG_WriteChar: range error");
+#endif
+
+	buf = SZ_GetSpace (sb, 1);
+	buf[0] = c;
+}
+
+void MSG_WriteByte (sizebuf_t *sb, int c)
+{
+	byte    *buf;
+	
+#ifdef PARANOID
+	if (c < 0 || c > 255)
+		Sys_Error ("MSG_WriteByte: range error");
+#endif
+
+	buf = SZ_GetSpace (sb, 1);
+	buf[0] = c;
+}
+
+void MSG_WriteShort (sizebuf_t *sb, int c)
+{
+	byte    *buf;
+	
+#ifdef PARANOID
+	if (c < ((short)0x8000) || c > (short)0x7fff)
+		Sys_Error ("MSG_WriteShort: range error");
+#endif
+
+	buf = SZ_GetSpace (sb, 2);
+	buf[0] = c&0xff;
+	buf[1] = c>>8;
+}
+
+void MSG_WriteLong (sizebuf_t *sb, int c)
+{
+	byte    *buf;
+	
+	buf = SZ_GetSpace (sb, 4);
+	buf[0] = c&0xff;
+	buf[1] = (c>>8)&0xff;
+	buf[2] = (c>>16)&0xff;
+	buf[3] = c>>24;
+}
+
+void MSG_WriteFloat (sizebuf_t *sb, float f)
+{
+	union
+	{
+		float   f;
+		int     l;
+	} dat;
+	
+	
+	dat.f = f;
+	dat.l = LittleLong (dat.l);
+	
+	SZ_Write (sb, &dat.l, 4);
+}
+
+void MSG_WriteString (sizebuf_t *sb, char *s)
+{
+	if (!s)
+		SZ_Write (sb, "", 1);
+	else
+		SZ_Write (sb, s, Q_strlen(s)+1);
+}
+
+void MSG_WriteCoord (sizebuf_t *sb, float f)
+{
+	MSG_WriteShort (sb, (int)(f*8));
+}
+
+void MSG_WriteAngle (sizebuf_t *sb, float f)
+{
+	MSG_WriteByte (sb, ((int)f*256/360) & 255);
+}
+
+//
+// reading functions
+//
+int                     msg_readcount;
+qboolean        msg_badread;
+
+void MSG_BeginReading (void)
+{
+	msg_readcount = 0;
+	msg_badread = false;
+}
+
+// returns -1 and sets msg_badread if no more characters are available
+int MSG_ReadChar (void)
+{
+	int     c;
+	
+	if (msg_readcount+1 > net_message.cursize)
+	{
+		msg_badread = true;
+		return -1;
+	}
+		
+	c = (signed char)net_message.data[msg_readcount];
+	msg_readcount++;
+	
+	return c;
+}
+
+int MSG_ReadByte (void)
+{
+	int     c;
+	
+	if (msg_readcount+1 > net_message.cursize)
+	{
+		msg_badread = true;
+		return -1;
+	}
+		
+	c = (unsigned char)net_message.data[msg_readcount];
+	msg_readcount++;
+	
+	return c;
+}
+
+int MSG_ReadShort (void)
+{
+	int     c;
+	
+	if (msg_readcount+2 > net_message.cursize)
+	{
+		msg_badread = true;
+		return -1;
+	}
+		
+	c = (short)(net_message.data[msg_readcount]
+	+ (net_message.data[msg_readcount+1]<<8));
+	
+	msg_readcount += 2;
+	
+	return c;
+}
+
+int MSG_ReadLong (void)
+{
+	int     c;
+	
+	if (msg_readcount+4 > net_message.cursize)
+	{
+		msg_badread = true;
+		return -1;
+	}
+		
+	c = net_message.data[msg_readcount]
+	+ (net_message.data[msg_readcount+1]<<8)
+	+ (net_message.data[msg_readcount+2]<<16)
+	+ (net_message.data[msg_readcount+3]<<24);
+	
+	msg_readcount += 4;
+	
+	return c;
+}
+
+float MSG_ReadFloat (void)
+{
+	union
+	{
+		byte    b[4];
+		float   f;
+		int     l;
+	} dat;
+	
+	dat.b[0] =      net_message.data[msg_readcount];
+	dat.b[1] =      net_message.data[msg_readcount+1];
+	dat.b[2] =      net_message.data[msg_readcount+2];
+	dat.b[3] =      net_message.data[msg_readcount+3];
+	msg_readcount += 4;
+	
+	dat.l = LittleLong (dat.l);
+
+	return dat.f;   
+}
+
+char *MSG_ReadString (void)
+{
+	static char     string[2048];
+	int             l,c;
+	
+	l = 0;
+	do
+	{
+		c = MSG_ReadChar ();
+		if (c == -1 || c == 0)
+			break;
+		string[l] = c;
+		l++;
+	} while (l < sizeof(string)-1);
+	
+	string[l] = 0;
+	
+	return string;
+}
+
+float MSG_ReadCoord (void)
+{
+	return MSG_ReadShort() * (1.0/8);
+}
+
+float MSG_ReadAngle (void)
+{
+	return MSG_ReadChar() * (360.0/256);
+}
+
+
+
+//===========================================================================
+
+void SZ_Alloc (sizebuf_t *buf, int startsize)
+{
+	if (startsize < 256)
+		startsize = 256;
+	buf->data = Hunk_AllocName (startsize, "sizebuf");
+	buf->maxsize = startsize;
+	buf->cursize = 0;
+}
+
+
+void SZ_Free (sizebuf_t *buf)
+{
+//      Z_Free (buf->data);
+//      buf->data = NULL;
+//      buf->maxsize = 0;
+	buf->cursize = 0;
+}
+
+void SZ_Clear (sizebuf_t *buf)
+{
+	buf->cursize = 0;
+}
+
+void *SZ_GetSpace (sizebuf_t *buf, int length)
+{
+	void    *data;
+	
+	if (buf->cursize + length > buf->maxsize)
+	{
+		if (!buf->allowoverflow)
+			Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");
+		
+		if (length > buf->maxsize)
+			Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);
+			
+		buf->overflowed = true;
+		Con_Printf ("SZ_GetSpace: overflow");
+		SZ_Clear (buf); 
+	}
+
+	data = buf->data + buf->cursize;
+	buf->cursize += length;
+	
+	return data;
+}
+
+void SZ_Write (sizebuf_t *buf, void *data, int length)
+{
+	Q_memcpy (SZ_GetSpace(buf,length),data,length);         
+}
+
+void SZ_Print (sizebuf_t *buf, char *data)
+{
+	int             len;
+	
+	len = Q_strlen(data)+1;
+
+// byte * cast to keep VC++ happy
+	if (buf->data[buf->cursize-1])
+		Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
+	else
+		Q_memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
+}
+
+
+//============================================================================
+
+
+/*
+============
+COM_SkipPath
+============
+*/
+char *COM_SkipPath (char *pathname)
+{
+	char    *last;
+	
+	last = pathname;
+	while (*pathname)
+	{
+		if (*pathname=='/')
+			last = pathname+1;
+		pathname++;
+	}
+	return last;
+}
+
+/*
+============
+COM_StripExtension
+============
+*/
+void COM_StripExtension (char *in, char *out)
+{
+	while (*in && *in != '.')
+		*out++ = *in++;
+	*out = 0;
+}
+
+/*
+============
+COM_FileExtension
+============
+*/
+char *COM_FileExtension (char *in)
+{
+	static char exten[8];
+	int             i;
+
+	while (*in && *in != '.')
+		in++;
+	if (!*in)
+		return "";
+	in++;
+	for (i=0 ; i<7 && *in ; i++,in++)
+		exten[i] = *in;
+	exten[i] = 0;
+	return exten;
+}
+
+/*
+============
+COM_FileBase
+============
+*/
+int *check_ptr = NULL;
+
+void COM_FileBase (char *in, char *out)
+{
+	char *s, *s2;
+
+	s = in + strlen(in) - 1;
+	
+	while (s != in && *s != '.')
+		s--;
+	
+	for (s2 = s ; *s2 && *s2 != '/' && s2 != in; s2--)
+	;
+	
+	if (s-s2 < 2)
+        {
+		strcpy (out,"?model?");
+        }
+	else
+	{
+            /* BUG */
+		s--;
+                //printf("writing %d bytes to outbuf", s-s2);
+		Q_strncpy (out,s2+1, s-s2);
+		out[s-s2] = 0;
+	}
+}
+
+
+/*
+==================
+COM_DefaultExtension
+==================
+*/
+void COM_DefaultExtension (char *path, char *extension)
+{
+	char    *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+	src = path + strlen(path) - 1;
+
+	while (*src != '/' && src != path)
+	{
+		if (*src == '.')
+			return;                 // it has an extension
+		src--;
+	}
+
+	strcat (path, extension);
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+	int             c;
+	int             len;
+	
+	len = 0;
+	com_token[0] = 0;
+	
+	if (!data)
+		return NULL;
+		
+// skip whitespace
+skipwhite:
+	while ( (c = *data) <= ' ')
+	{
+		if (c == 0)
+			return NULL;                    // end of file;
+		data++;
+	}
+	
+// skip // comments
+	if (c=='/' && data[1] == '/')
+	{
+		while (*data && *data != '\n')
+			data++;
+		goto skipwhite;
+	}
+	
+
+// handle quoted strings specially
+	if (c == '\"')
+	{
+		data++;
+		while (1)
+		{
+			c = *data++;
+			if (c=='\"' || !c)
+			{
+				com_token[len] = 0;
+				return data;
+			}
+			com_token[len] = c;
+			len++;
+		}
+	}
+
+// parse single characters
+	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+	{
+		com_token[len] = c;
+		len++;
+		com_token[len] = 0;
+		return data+1;
+	}
+
+// parse a regular word
+	do
+	{
+		com_token[len] = c;
+		data++;
+		len++;
+		c = *data;
+	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+			break;
+	} while (c>32);
+	
+	com_token[len] = 0;
+	return data;
+}
+
+
+/*
+================
+COM_CheckParm
+
+Returns the position (1 to argc-1) in the program's argument list
+where the given parameter apears, or 0 if not present
+================
+*/
+int COM_CheckParm (char *parm)
+{
+	int             i;
+	
+	for (i=1 ; i<com_argc ; i++)
+	{
+		if (!com_argv[i])
+			continue;               // NEXTSTEP sometimes clears appkit vars.
+		if (!Q_strcmp (parm,com_argv[i]))
+			return i;
+	}
+		
+	return 0;
+}
+
+/*
+================
+COM_CheckRegistered
+
+Looks for the pop.txt file and verifies it.
+Sets the "registered" cvar.
+Immediately exits out if an alternate game was attempted to be started without
+being registered.
+================
+*/
+void COM_CheckRegistered (void)
+{
+	int             h;
+	unsigned short  check[128];
+	int                     i;
+
+	COM_OpenFile("gfx/pop.lmp", &h);
+	static_registered = 0;
+
+	if (h == -1)
+	{
+#if WINDED
+	Sys_Error ("This dedicated server requires a full registered copy of Quake");
+#endif
+		Con_Printf ("Playing shareware version.\n");
+		if (com_modified)
+			Sys_Error ("You must have the registered version to use modified games");
+		return;
+	}
+
+	Sys_FileRead (h, check, sizeof(check));
+	COM_CloseFile (h);
+	
+	for (i=0 ; i<128 ; i++)
+		if (pop[i] != (unsigned short)BigShort (check[i]))
+			Sys_Error ("Corrupted data file.");
+	
+	Cvar_Set ("cmdline", com_cmdline);
+	Cvar_Set ("registered", "1");
+	static_registered = 1;
+	Con_Printf ("Playing registered version.\n");
+}
+
+
+void COM_Path_f (void);
+
+
+/*
+================
+COM_InitArgv
+================
+*/
+void COM_InitArgv (int argc, char **argv)
+{
+	qboolean        safe;
+	int             i, j, n;
+
+// reconstitute the command line for the cmdline externally visible cvar
+	n = 0;
+
+	for (j=0 ; (j<MAX_NUM_ARGVS) && (j< argc) ; j++)
+	{
+		i = 0;
+
+		while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
+		{
+			com_cmdline[n++] = argv[j][i++];
+		}
+
+		if (n < (CMDLINE_LENGTH - 1))
+			com_cmdline[n++] = ' ';
+		else
+			break;
+	}
+
+	com_cmdline[n] = 0;
+
+	safe = false;
+
+	for (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;
+		 com_argc++)
+	{
+		largv[com_argc] = argv[com_argc];
+		if (!Q_strcmp ("-safe", argv[com_argc]))
+			safe = true;
+	}
+
+	if (safe)
+	{
+	// force all the safe-mode switches. Note that we reserved extra space in
+	// case we need to add these, so we don't need an overflow check
+		for (i=0 ; i<NUM_SAFE_ARGVS ; i++)
+		{
+			largv[com_argc] = safeargvs[i];
+			com_argc++;
+		}
+	}
+
+	largv[com_argc] = argvdummy;
+	com_argv = largv;
+
+	if (COM_CheckParm ("-rogue"))
+	{
+		rogue = true;
+		standard_quake = false;
+	}
+
+	if (COM_CheckParm ("-hipnotic"))
+	{
+		hipnotic = true;
+		standard_quake = false;
+	}
+}
+
+
+/*
+================
+COM_Init
+================
+*/
+void COM_Init (char *basedir)
+{
+	byte    swaptest[2] = {1,0};
+
+// set the byte swapping variables in a portable manner 
+#ifdef SDL
+	// This is necessary because egcs 1.1.1 mis-compiles swaptest with -O2
+	if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
+#else
+	if ( *(short *)swaptest == 1)
+#endif
+	{
+		bigendien = false;
+		BigShort = ShortSwap;
+		LittleShort = ShortNoSwap;
+		BigLong = LongSwap;
+		LittleLong = LongNoSwap;
+		BigFloat = FloatSwap;
+		LittleFloat = FloatNoSwap;
+	}
+	else
+	{
+		bigendien = true;
+		BigShort = ShortNoSwap;
+		LittleShort = ShortSwap;
+		BigLong = LongNoSwap;
+		LittleLong = LongSwap;
+		BigFloat = FloatNoSwap;
+		LittleFloat = FloatSwap;
+	}
+
+	Cvar_RegisterVariable (&registered);
+	Cvar_RegisterVariable (&cmdline);
+	Cmd_AddCommand ("path", COM_Path_f);
+
+	COM_InitFilesystem ();
+	COM_CheckRegistered ();
+}
+
+
+/*
+============
+va
+
+does a varargs printf into a temp buffer, so I don't need to have
+varargs versions of all text functions.
+FIXME: make this buffer size safe someday
+============
+*/
+char    *va(char *format, ...)
+{
+	va_list         argptr;
+	static char             string[1024];
+	
+	va_start (argptr, format);
+	vsprintf (string, format,argptr);
+	va_end (argptr);
+
+	return string;  
+}
+
+
+/// just for debugging
+int     memsearch (byte *start, int count, int search)
+{
+	int             i;
+	
+	for (i=0 ; i<count ; i++)
+		if (start[i] == search)
+			return i;
+	return -1;
+}
+
+/*
+=============================================================================
+
+QUAKE FILESYSTEM
+
+=============================================================================
+*/
+
+int     com_filesize;
+
+
+//
+// in memory
+//
+
+typedef struct
+{
+	char    name[MAX_QPATH];
+	int             filepos, filelen;
+} packfile_t;
+
+typedef struct pack_s
+{
+	char    filename[MAX_OSPATH];
+	int             handle;
+	int             numfiles;
+	packfile_t      *files;
+} pack_t;
+
+//
+// on disk
+//
+typedef struct
+{
+	char    name[56];
+	int             filepos, filelen;
+} dpackfile_t;
+
+typedef struct
+{
+	char    id[4];
+	int             dirofs;
+	int             dirlen;
+} dpackheader_t;
+
+#define MAX_FILES_IN_PACK       2048
+
+char    com_cachedir[MAX_OSPATH];
+char    com_gamedir[MAX_OSPATH];
+
+typedef struct searchpath_s
+{
+	char    filename[MAX_OSPATH];
+	pack_t  *pack;          // only one of filename / pack will be used
+	struct searchpath_s *next;
+} searchpath_t;
+
+searchpath_t    *com_searchpaths;
+
+/*
+============
+COM_Path_f
+
+============
+*/
+void COM_Path_f (void)
+{
+	searchpath_t    *s;
+	
+	Con_Printf ("Current search path:\n");
+	for (s=com_searchpaths ; s ; s=s->next)
+	{
+		if (s->pack)
+		{
+			Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
+		}
+		else
+			Con_Printf ("%s\n", s->filename);
+	}
+}
+
+/*
+============
+COM_WriteFile
+
+The filename will be prefixed by the current game directory
+============
+*/
+void COM_WriteFile (char *filename, void *data, int len)
+{
+	int             handle;
+	char    name[MAX_OSPATH];
+	
+	sprintf (name, "%s/%s", com_gamedir, filename);
+
+	handle = Sys_FileOpenWrite (name);
+	if (handle == -1)
+	{
+		Sys_Printf ("COM_WriteFile: failed on %s\n", name);
+		return;
+	}
+	
+	Sys_Printf ("COM_WriteFile: %s\n", name);
+	Sys_FileWrite (handle, data, len);
+	Sys_FileClose (handle);
+}
+
+
+/*
+============
+COM_CreatePath
+
+Only used for CopyFile
+============
+*/
+void    COM_CreatePath (char *path)
+{
+	char    *ofs;
+	
+	for (ofs = path+1 ; *ofs ; ofs++)
+	{
+		if (*ofs == '/')
+		{       // create the directory
+			*ofs = 0;
+			Sys_mkdir (path);
+			*ofs = '/';
+		}
+	}
+}
+
+
+/*
+===========
+COM_CopyFile
+
+Copies a file over from the net to the local cache, creating any directories
+needed.  This is for the convenience of developers using ISDN from home.
+===========
+*/
+void COM_CopyFile (char *netpath, char *cachepath)
+{
+	int             in, out;
+	int             remaining, count;
+	char    buf[4096];
+	
+	remaining = Sys_FileOpenRead (netpath, &in);            
+	COM_CreatePath (cachepath);     // create directories up to the cache file
+	out = Sys_FileOpenWrite (cachepath);
+	
+	while (remaining)
+	{
+		if (remaining < sizeof(buf))
+			count = remaining;
+		else
+			count = sizeof(buf);
+		Sys_FileRead (in, buf, count);
+		Sys_FileWrite (out, buf, count);
+		remaining -= count;
+	}
+
+	Sys_FileClose (in);
+	Sys_FileClose (out);    
+}
+
+/*
+===========
+COM_FindFile
+
+Finds the file in the search path.
+Sets com_filesize and one of handle or file
+===========
+*/
+int COM_FindFile (char *filename, int *handle, FILE **file)
+{
+	searchpath_t    *search;
+	char            netpath[MAX_OSPATH];
+	char            cachepath[MAX_OSPATH];
+	pack_t          *pak;
+	int                     i;
+	int                     findtime, cachetime;
+
+	if (file && handle)
+		Sys_Error ("COM_FindFile: both handle and file set");
+	if (!file && !handle)
+		Sys_Error ("COM_FindFile: neither handle or file set");
+		
+//
+// search through the path, one element at a time
+//
+	search = com_searchpaths;
+	if (proghack)
+	{	// gross hack to use quake 1 progs with quake 2 maps
+		if (!strcmp(filename, "progs.dat"))
+			search = search->next;
+	}
+
+	for ( ; search ; search = search->next)
+	{
+	// is the element a pak file?
+		if (search->pack)
+		{
+		// look through all the pak file elements
+			pak = search->pack;
+			for (i=0 ; i<pak->numfiles ; i++)
+				if (!strcmp (pak->files[i].name, filename))
+				{       // found it!
+					Con_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
+					if (handle)
+					{
+						*handle = pak->handle;
+						Sys_FileSeek (pak->handle, pak->files[i].filepos);
+                                                //if(*handle != 1)
+                                                    //rb->splashf(HZ, "handle found: %d", *handle);
+					}
+					else
+					{       // open a new file on the pakfile
+						*file = fopen (pak->filename, "rb");
+						if (*file)
+							fseek (*file, pak->files[i].filepos, SEEK_SET);
+                                                else
+                                                {
+                                                    Sys_Error("open failed: %d", 0);
+                                                }
+					}
+					com_filesize = pak->files[i].filelen;
+					return com_filesize;
+				}
+		}
+		else
+		{               
+	// check a file in the directory tree
+			if (!static_registered)
+			{       // if not a registered version, don't ever go beyond base
+				if ( strchr (filename, '/') || strchr (filename,'\\'))
+					continue;
+			}
+			
+			sprintf (netpath, "%s/%s",search->filename, filename);
+			
+			findtime = Sys_FileTime (netpath);
+			if (findtime == -1)
+				continue;
+				
+		// see if the file needs to be updated in the cache
+			if (!com_cachedir[0])
+				strcpy (cachepath, netpath);
+			else
+			{	
+#if defined(_WIN32)
+				if ((strlen(netpath) < 2) || (netpath[1] != ':'))
+					sprintf (cachepath,"%s%s", com_cachedir, netpath);
+				else
+					sprintf (cachepath,"%s%s", com_cachedir, netpath+2);
+#else
+				sprintf (cachepath,"%s/%s", com_cachedir, netpath);
+#endif
+
+				cachetime = Sys_FileTime (cachepath);
+			
+				if (cachetime < findtime)
+					COM_CopyFile (netpath, cachepath);
+				strcpy (netpath, cachepath);
+			}	
+
+			Sys_Printf ("FindFile: %s\n",netpath);
+			com_filesize = Sys_FileOpenRead (netpath, &i);
+			if (handle)
+				*handle = i;
+			else
+			{
+				Sys_FileClose (i);
+				*file = fopen (netpath, "rb");
+			}
+			return com_filesize;
+		}
+		
+	}
+	
+	Sys_Printf ("FindFile: can't find %s\n", filename);
+	
+	if (handle)
+		*handle = -1;
+	else
+		*file = NULL;
+	com_filesize = -1;
+	return -1;
+}
+
+
+/*
+===========
+COM_OpenFile
+
+filename never has a leading slash, but may contain directory walks
+returns a handle and a length
+it may actually be inside a pak file
+===========
+*/
+int COM_OpenFile (char *filename, int *handle)
+{
+	return COM_FindFile (filename, handle, NULL);
+}
+
+/*
+===========
+COM_FOpenFile
+
+If the requested file is inside a packfile, a new FILE * will be opened
+into the file.
+===========
+*/
+int COM_FOpenFile (char *filename, FILE **file)
+{
+	return COM_FindFile (filename, NULL, file);
+}
+
+/*
+============
+COM_CloseFile
+
+If it is a pak file handle, don't really close it
+============
+*/
+void COM_CloseFile (int h)
+{
+	searchpath_t    *s;
+	
+	for (s = com_searchpaths ; s ; s=s->next)
+		if (s->pack && s->pack->handle == h)
+			return;
+			
+	Sys_FileClose (h);
+}
+
+
+/*
+============
+COM_LoadFile
+
+Filename are reletive to the quake directory.
+Allways appends a 0 byte.
+============
+*/
+cache_user_t *loadcache;
+byte    *loadbuf;
+int             loadsize;
+byte *COM_LoadFile (char *path, int usehunk)
+{
+    //printf("COM_LoadFile(%s)", path);
+	int             h;
+	byte    *buf;
+	char    base[32];
+	int             len;
+
+	buf = NULL;     // quiet compiler warning
+
+// look for it in the filesystem or pack files
+	len = COM_OpenFile (path, &h);
+	if (h == -1)
+		return NULL;
+
+        check_ptr = &h;
+        
+        //printf("handle %d", h);
+// extract the filename base name for hunk tag
+        /* BUG IS HERE */
+	COM_FileBase (path, base);
+
+        //printf("handle %d base \"%s\"", h, base);
+        //printf("");
+        
+	if (usehunk == 1)
+		buf = Hunk_AllocName (len+1, base);
+	else if (usehunk == 2)
+		buf = Hunk_TempAlloc (len+1);
+	else if (usehunk == 0)
+		buf = Z_Malloc (len+1);
+	else if (usehunk == 3)
+		buf = Cache_Alloc (loadcache, len+1, base);
+	else if (usehunk == 4)
+	{
+		if (len+1 > loadsize)
+			buf = Hunk_TempAlloc (len+1);
+		else
+			buf = loadbuf;
+	}
+	else
+		Sys_Error ("COM_LoadFile: bad usehunk");
+        
+	if (!buf)
+		Sys_Error ("COM_LoadFile: not enough space for %s", path);
+		
+	((byte *)buf)[len] = 0;
+
+	Draw_BeginDisc ();
+	Sys_FileRead (h, buf, len);
+	COM_CloseFile (h);
+	Draw_EndDisc ();
+        
+	return buf;
+}
+
+byte *COM_LoadHunkFile (char *path)
+{
+	return COM_LoadFile (path, 1);
+}
+
+byte *COM_LoadTempFile (char *path)
+{
+	return COM_LoadFile (path, 2);
+}
+
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu)
+{
+	loadcache = cu;
+	COM_LoadFile (path, 3);
+}
+
+// uses temp hunk if larger than bufsize
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize)
+{
+	byte    *buf;
+	
+	loadbuf = (byte *)buffer;
+	loadsize = bufsize;
+	buf = COM_LoadFile (path, 4);
+	
+	return buf;
+}
+
+/*
+=================
+COM_LoadPackFile
+
+Takes an explicit (not game tree related) path to a pak file.
+
+Loads the header and directory, adding the files at the beginning
+of the list so they override previous pack files.
+=================
+*/
+pack_t *COM_LoadPackFile (char *packfile)
+{
+	dpackheader_t   header;
+	int                             i;
+	packfile_t              *newfiles;
+	int                             numpackfiles;
+	pack_t                  *pack;
+	int                             packhandle;
+	dpackfile_t             info[MAX_FILES_IN_PACK];
+	unsigned short          crc;
+
+	if (Sys_FileOpenRead (packfile, &packhandle) == -1)
+	{
+//              Con_Printf ("Couldn't open %s\n", packfile);
+		return NULL;
+	}
+	Sys_FileRead (packhandle, (void *)&header, sizeof(header));
+	if (header.id[0] != 'P' || header.id[1] != 'A'
+	|| header.id[2] != 'C' || header.id[3] != 'K')
+		Sys_Error ("%s is not a packfile", packfile);
+	header.dirofs = LittleLong (header.dirofs);
+	header.dirlen = LittleLong (header.dirlen);
+
+	numpackfiles = header.dirlen / sizeof(dpackfile_t);
+
+	if (numpackfiles > MAX_FILES_IN_PACK)
+		Sys_Error ("%s has %i files", packfile, numpackfiles);
+
+	if (numpackfiles != PAK0_COUNT)
+		com_modified = true;    // not the original file
+
+	newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile");
+
+	Sys_FileSeek (packhandle, header.dirofs);
+	Sys_FileRead (packhandle, (void *)info, header.dirlen);
+
+// crc the directory to check for modifications
+	CRC_Init (&crc);
+	for (i=0 ; i<header.dirlen ; i++)
+		CRC_ProcessByte (&crc, ((byte *)info)[i]);
+	if (crc != PAK0_CRC)
+		com_modified = true;
+
+// parse the directory
+	for (i=0 ; i<numpackfiles ; i++)
+	{
+		strcpy (newfiles[i].name, info[i].name);
+		newfiles[i].filepos = LittleLong(info[i].filepos);
+		newfiles[i].filelen = LittleLong(info[i].filelen);
+	}
+
+	pack = Hunk_Alloc (sizeof (pack_t));
+	strcpy (pack->filename, packfile);
+	pack->handle = packhandle;
+	pack->numfiles = numpackfiles;
+	pack->files = newfiles;
+	
+	Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
+	return pack;
+}
+
+
+/*
+================
+COM_AddGameDirectory
+
+Sets com_gamedir, adds the directory to the head of the path,
+then loads and adds pak1.pak pak2.pak ... 
+================
+*/
+void COM_AddGameDirectory (char *dir)
+{
+	int                             i;
+	searchpath_t    *search;
+	pack_t                  *pak;
+	char                    pakfile[MAX_OSPATH];
+
+	strcpy (com_gamedir, dir);
+
+//
+// add the directory to the search path
+//
+	search = Hunk_Alloc (sizeof(searchpath_t));
+	strcpy (search->filename, dir);
+	search->next = com_searchpaths;
+	com_searchpaths = search;
+
+//
+// add any pak files in the format pak0.pak pak1.pak, ...
+//
+	for (i=0 ; ; i++)
+	{
+		sprintf (pakfile, "%s/pak%i.pak", dir, i);
+		pak = COM_LoadPackFile (pakfile);
+		if (!pak)
+			break;
+		search = Hunk_Alloc (sizeof(searchpath_t));
+		search->pack = pak;
+		search->next = com_searchpaths;
+		com_searchpaths = search;               
+	}
+
+//
+// add the contents of the parms.txt file to the end of the command line
+//
+
+}
+
+/*
+================
+COM_InitFilesystem
+================
+*/
+void COM_InitFilesystem (void)
+{
+	int             i, j;
+	char    basedir[MAX_OSPATH];
+	searchpath_t    *search;
+
+//
+// -basedir <path>
+// Overrides the system supplied base directory (under GAMENAME)
+//
+	i = COM_CheckParm ("-basedir");
+	if (i && i < com_argc-1)
+		strcpy (basedir, com_argv[i+1]);
+	else
+		strcpy (basedir, host_parms.basedir);
+
+	j = strlen (basedir);
+
+	if (j > 0)
+	{
+		if ((basedir[j-1] == '\\') || (basedir[j-1] == '/'))
+			basedir[j-1] = 0;
+	}
+
+//
+// -cachedir <path>
+// Overrides the system supplied cache directory (NULL or /qcache)
+// -cachedir - will disable caching.
+//
+	i = COM_CheckParm ("-cachedir");
+	if (i && i < com_argc-1)
+	{
+		if (com_argv[i+1][0] == '-')
+			com_cachedir[0] = 0;
+		else
+			strcpy (com_cachedir, com_argv[i+1]);
+	}
+	else if (host_parms.cachedir)
+		strcpy (com_cachedir, host_parms.cachedir);
+	else
+		com_cachedir[0] = 0;
+
+//
+// start up with GAMENAME by default (id1)
+//
+	COM_AddGameDirectory (va("%s/"GAMENAME, basedir) );
+
+	if (COM_CheckParm ("-rogue"))
+		COM_AddGameDirectory (va("%s/rogue", basedir) );
+	if (COM_CheckParm ("-hipnotic"))
+		COM_AddGameDirectory (va("%s/hipnotic", basedir) );
+
+//
+// -game <gamedir>
+// Adds basedir/gamedir as an override game
+//
+	i = COM_CheckParm ("-game");
+	if (i && i < com_argc-1)
+	{
+		com_modified = true;
+		COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1]));
+	}
+
+//
+// -path <dir or packfile> [<dir or packfile>] ...
+// Fully specifies the exact serach path, overriding the generated one
+//
+	i = COM_CheckParm ("-path");
+	if (i)
+	{
+		com_modified = true;
+		com_searchpaths = NULL;
+		while (++i < com_argc)
+		{
+			if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
+				break;
+			
+			search = Hunk_Alloc (sizeof(searchpath_t));
+			if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
+			{
+				search->pack = COM_LoadPackFile (com_argv[i]);
+				if (!search->pack)
+					Sys_Error ("Couldn't load packfile: %s", com_argv[i]);
+			}
+			else
+				strcpy (search->filename, com_argv[i]);
+			search->next = com_searchpaths;
+			com_searchpaths = search;
+		}
+	}
+
+	if (COM_CheckParm ("-proghack"))
+		proghack = true;
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/common.h b/apps/plugins/sdl/progs/quake/common.h
new file mode 100644
index 0000000..a1ac227
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/common.h
@@ -0,0 +1,184 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// comndef.h  -- general definitions
+
+#if !defined BYTE_DEFINED
+typedef unsigned char 		byte;
+#define BYTE_DEFINED 1
+#endif
+
+#undef true
+#undef false
+
+typedef	int qboolean;
+enum {false, true};
+
+//============================================================================
+
+typedef struct sizebuf_s
+{
+	qboolean	allowoverflow;	// if false, do a Sys_Error
+	qboolean	overflowed;		// set to true if the buffer size failed
+	byte	*data;
+	int		maxsize;
+	int		cursize;
+} sizebuf_t;
+
+void SZ_Alloc (sizebuf_t *buf, int startsize);
+void SZ_Free (sizebuf_t *buf);
+void SZ_Clear (sizebuf_t *buf);
+void *SZ_GetSpace (sizebuf_t *buf, int length);
+void SZ_Write (sizebuf_t *buf, void *data, int length);
+void SZ_Print (sizebuf_t *buf, char *data);	// strcats onto the sizebuf
+
+//============================================================================
+
+typedef struct link_s
+{
+	struct link_s	*prev, *next;
+} link_t;
+
+
+void ClearLink (link_t *l);
+void RemoveLink (link_t *l);
+void InsertLinkBefore (link_t *l, link_t *before);
+void InsertLinkAfter (link_t *l, link_t *after);
+
+// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
+// ent = STRUCT_FROM_LINK(link,entity_t,order)
+// FIXME: remove this mess!
+#define	STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
+
+//============================================================================
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define Q_MAXCHAR ((char)0x7f)
+#define Q_MAXSHORT ((short)0x7fff)
+#define Q_MAXINT	((int)0x7fffffff)
+#define Q_MAXLONG ((int)0x7fffffff)
+#define Q_MAXFLOAT ((int)0x7fffffff)
+
+#define Q_MINCHAR ((char)0x80)
+#define Q_MINSHORT ((short)0x8000)
+#define Q_MININT 	((int)0x80000000)
+#define Q_MINLONG ((int)0x80000000)
+#define Q_MINFLOAT ((int)0x7fffffff)
+
+//============================================================================
+
+extern	qboolean		bigendien;
+
+extern	short	(*BigShort) (short l);
+extern	short	(*LittleShort) (short l);
+extern	int	(*BigLong) (int l);
+extern	int	(*LittleLong) (int l);
+extern	float	(*BigFloat) (float l);
+extern	float	(*LittleFloat) (float l);
+
+//============================================================================
+
+void MSG_WriteChar (sizebuf_t *sb, int c);
+void MSG_WriteByte (sizebuf_t *sb, int c);
+void MSG_WriteShort (sizebuf_t *sb, int c);
+void MSG_WriteLong (sizebuf_t *sb, int c);
+void MSG_WriteFloat (sizebuf_t *sb, float f);
+void MSG_WriteString (sizebuf_t *sb, char *s);
+void MSG_WriteCoord (sizebuf_t *sb, float f);
+void MSG_WriteAngle (sizebuf_t *sb, float f);
+
+extern	int			msg_readcount;
+extern	qboolean	msg_badread;		// set if a read goes beyond end of message
+
+void MSG_BeginReading (void);
+int MSG_ReadChar (void);
+int MSG_ReadByte (void);
+int MSG_ReadShort (void);
+int MSG_ReadLong (void);
+float MSG_ReadFloat (void);
+char *MSG_ReadString (void);
+
+float MSG_ReadCoord (void);
+float MSG_ReadAngle (void);
+
+//============================================================================
+
+void Q_memset (void *dest, int fill, int count);
+void Q_memcpy (void *dest, void *src, int count);
+int Q_memcmp (void *m1, void *m2, int count);
+void Q_strcpy (char *dest, char *src);
+void Q_strncpy (char *dest, char *src, int count);
+int Q_strlen (char *str);
+char *Q_strrchr (char *s, char c);
+void Q_strcat (char *dest, char *src);
+int Q_strcmp (char *s1, char *s2);
+int Q_strncmp (char *s1, char *s2, int count);
+int Q_strcasecmp (char *s1, char *s2);
+int Q_strncasecmp (char *s1, char *s2, int n);
+int	Q_atoi (char *str);
+float Q_atof (char *str);
+
+//============================================================================
+
+extern	char		com_token[1024];
+extern	qboolean	com_eof;
+
+char *COM_Parse (char *data);
+
+
+extern	int		com_argc;
+extern	char	**com_argv;
+
+int COM_CheckParm (char *parm);
+void COM_Init (char *path);
+void COM_InitArgv (int argc, char **argv);
+
+char *COM_SkipPath (char *pathname);
+void COM_StripExtension (char *in, char *out);
+void COM_FileBase (char *in, char *out);
+void COM_DefaultExtension (char *path, char *extension);
+
+char	*va(char *format, ...);
+// does a varargs printf into a temp buffer
+
+
+//============================================================================
+
+extern int com_filesize;
+struct cache_user_s;
+
+extern	char	com_gamedir[MAX_OSPATH];
+
+void COM_WriteFile (char *filename, void *data, int len);
+int COM_OpenFile (char *filename, int *hndl);
+int COM_FOpenFile (char *filename, FILE **file);
+void COM_CloseFile (int h);
+
+byte *COM_LoadStackFile (char *path, void *buffer, int bufsize);
+byte *COM_LoadTempFile (char *path);
+byte *COM_LoadHunkFile (char *path);
+void COM_LoadCacheFile (char *path, struct cache_user_s *cu);
+
+
+extern	struct cvar_s	registered;
+
+extern qboolean		standard_quake, rogue, hipnotic;
diff --git a/apps/plugins/sdl/progs/quake/conproc.h b/apps/plugins/sdl/progs/quake/conproc.h
new file mode 100644
index 0000000..743526f
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/conproc.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// conproc.h
+
+#define CCOM_WRITE_TEXT		0x2
+// Param1 : Text
+
+#define CCOM_GET_TEXT		0x3
+// Param1 : Begin line
+// Param2 : End line
+
+#define CCOM_GET_SCR_LINES	0x4
+// No params
+
+#define CCOM_SET_SCR_LINES	0x5
+// Param1 : Number of lines
+
+void InitConProc (HANDLE hFile, HANDLE heventParent, HANDLE heventChild);
+void DeinitConProc (void);
+
diff --git a/apps/plugins/sdl/progs/quake/console.c b/apps/plugins/sdl/progs/quake/console.c
new file mode 100644
index 0000000..59c7de3
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/console.c
@@ -0,0 +1,644 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// console.c
+
+#include "quakedef.h"
+
+int 		con_linewidth;
+
+float		con_cursorspeed = 4;
+
+#define		CON_TEXTSIZE	16384
+
+qboolean 	con_forcedup;		// because no entities to refresh
+
+int			con_totallines;		// total lines in console scrollback
+int			con_backscroll;		// lines up from bottom to display
+int			con_current;		// where next message will be printed
+int			con_x;				// offset in current line for next print
+char		*con_text=0;
+
+cvar_t		con_notifytime = {"con_notifytime","3"};		//seconds
+
+#define	NUM_CON_TIMES 4
+float		con_times[NUM_CON_TIMES];	// realtime time the line was generated
+								// for transparent notify lines
+
+int			con_vislines;
+
+qboolean	con_debuglog;
+
+#define		MAXCMDLINE	256
+extern	char	key_lines[32][MAXCMDLINE];
+extern	int		edit_line;
+extern	int		key_linepos;
+		
+
+qboolean	con_initialized;
+
+int			con_notifylines;		// scan lines to clear for notify lines
+
+extern void M_Menu_Main_f (void);
+
+/*
+================
+Con_ToggleConsole_f
+================
+*/
+void Con_ToggleConsole_f (void)
+{
+	if (key_dest == key_console)
+	{
+		if (cls.state == ca_connected)
+		{
+			key_dest = key_game;
+			key_lines[edit_line][1] = 0;	// clear any typing
+			key_linepos = 1;
+		}
+		else
+		{
+			M_Menu_Main_f ();
+		}
+	}
+	else
+		key_dest = key_console;
+	
+	SCR_EndLoadingPlaque ();
+	memset (con_times, 0, sizeof(con_times));
+}
+
+/*
+================
+Con_Clear_f
+================
+*/
+void Con_Clear_f (void)
+{
+	if (con_text)
+		Q_memset (con_text, ' ', CON_TEXTSIZE);
+}
+
+						
+/*
+================
+Con_ClearNotify
+================
+*/
+void Con_ClearNotify (void)
+{
+	int		i;
+	
+	for (i=0 ; i<NUM_CON_TIMES ; i++)
+		con_times[i] = 0;
+}
+
+						
+/*
+================
+Con_MessageMode_f
+================
+*/
+extern qboolean team_message;
+
+void Con_MessageMode_f (void)
+{
+	key_dest = key_message;
+	team_message = false;
+}
+
+						
+/*
+================
+Con_MessageMode2_f
+================
+*/
+void Con_MessageMode2_f (void)
+{
+	key_dest = key_message;
+	team_message = true;
+}
+
+						
+/*
+================
+Con_CheckResize
+
+If the line width has changed, reformat the buffer.
+================
+*/
+void Con_CheckResize (void)
+{
+	int		i, j, width, oldwidth, oldtotallines, numlines, numchars;
+	char	tbuf[CON_TEXTSIZE];
+
+	width = (vid.width >> 3) - 2;
+
+	if (width == con_linewidth)
+		return;
+
+	if (width < 1)			// video hasn't been initialized yet
+	{
+		width = 38;
+		con_linewidth = width;
+		con_totallines = CON_TEXTSIZE / con_linewidth;
+		Q_memset (con_text, ' ', CON_TEXTSIZE);
+	}
+	else
+	{
+		oldwidth = con_linewidth;
+		con_linewidth = width;
+		oldtotallines = con_totallines;
+		con_totallines = CON_TEXTSIZE / con_linewidth;
+		numlines = oldtotallines;
+
+		if (con_totallines < numlines)
+			numlines = con_totallines;
+
+		numchars = oldwidth;
+	
+		if (con_linewidth < numchars)
+			numchars = con_linewidth;
+
+		Q_memcpy (tbuf, con_text, CON_TEXTSIZE);
+		Q_memset (con_text, ' ', CON_TEXTSIZE);
+
+		for (i=0 ; i<numlines ; i++)
+		{
+			for (j=0 ; j<numchars ; j++)
+			{
+				con_text[(con_totallines - 1 - i) * con_linewidth + j] =
+						tbuf[((con_current - i + oldtotallines) %
+							  oldtotallines) * oldwidth + j];
+			}
+		}
+
+		Con_ClearNotify ();
+	}
+
+	con_backscroll = 0;
+	con_current = con_totallines - 1;
+}
+
+
+/*
+================
+Con_Init
+================
+*/
+void Con_Init (void)
+{
+#define MAXGAMEDIRLEN	1000
+	char	temp[MAXGAMEDIRLEN+1];
+	char	*t2 = "/qconsole.log";
+
+	con_debuglog = COM_CheckParm("-condebug");
+
+	if (con_debuglog)
+	{
+		if (strlen (com_gamedir) < (MAXGAMEDIRLEN - strlen (t2)))
+		{
+			sprintf (temp, "%s%s", com_gamedir, t2);
+			unlink (temp);
+		}
+	}
+
+	con_text = Hunk_AllocName (CON_TEXTSIZE, "context");
+	Q_memset (con_text, ' ', CON_TEXTSIZE);
+	con_linewidth = -1;
+	Con_CheckResize ();
+	
+	Con_Printf ("Console initialized.\n");
+
+//
+// register our commands
+//
+	Cvar_RegisterVariable (&con_notifytime);
+
+	Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
+	Cmd_AddCommand ("messagemode", Con_MessageMode_f);
+	Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
+	Cmd_AddCommand ("clear", Con_Clear_f);
+	con_initialized = true;
+}
+
+
+/*
+===============
+Con_Linefeed
+===============
+*/
+void Con_Linefeed (void)
+{
+	if ( ! con_initialized ) return;
+	con_x = 0;
+	con_current++;
+	Q_memset (&con_text[(con_current%con_totallines)*con_linewidth]
+	, ' ', con_linewidth);
+}
+
+/*
+================
+Con_Print
+
+Handles cursor positioning, line wrapping, etc
+All console printing must go through this in order to be logged to disk
+If no console is visible, the notify window will pop up.
+================
+*/
+void Con_Print (char *txt)
+{
+	int		y;
+	int		c, l;
+	static int	cr;
+	int		mask;
+	
+	if ( ! con_initialized ) return;
+	con_backscroll = 0;
+
+	if (txt[0] == 1)
+	{
+		mask = 128;		// go to colored text
+		S_LocalSound ("misc/talk.wav");
+	// play talk wav
+		txt++;
+	}
+	else if (txt[0] == 2)
+	{
+		mask = 128;		// go to colored text
+		txt++;
+	}
+	else
+		mask = 0;
+
+
+	while ( (c = *txt) )
+	{
+	// count word length
+		for (l=0 ; l< con_linewidth ; l++)
+			if ( txt[l] <= ' ')
+				break;
+
+	// word wrap
+		if (l != con_linewidth && (con_x + l > con_linewidth) )
+			con_x = 0;
+
+		txt++;
+
+		if (cr)
+		{
+			con_current--;
+			cr = false;
+		}
+
+		
+		if (!con_x)
+		{
+			Con_Linefeed ();
+		// mark time for transparent overlay
+			if (con_current >= 0)
+				con_times[con_current % NUM_CON_TIMES] = realtime;
+		}
+
+		switch (c)
+		{
+		case '\n':
+			con_x = 0;
+			break;
+
+		case '\r':
+			con_x = 0;
+			cr = 1;
+			break;
+
+		default:	// display character and advance
+			y = con_current % con_totallines;
+			con_text[y*con_linewidth+con_x] = c | mask;
+			con_x++;
+			if (con_x >= con_linewidth)
+				con_x = 0;
+			break;
+		}
+		
+	}
+}
+
+
+/*
+================
+Con_DebugLog
+================
+*/
+void Con_DebugLog(char *file, char *fmt, ...)
+{
+    va_list argptr; 
+    static char data[1024];
+    int fd;
+
+    va_start(argptr, fmt);
+    vsprintf(data, fmt, argptr);
+    va_end(argptr);
+    fd = rb->open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    rb->write(fd, data, strlen(data));
+    rb->close(fd);
+}
+
+
+/*
+================
+Con_Printf
+
+Handles cursor positioning, line wrapping, etc
+================
+*/
+#define	MAXPRINTMSG	4096
+// FIXME: make a buffer size safe vsprintf?
+void Con_Printf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		msg[MAXPRINTMSG];
+	static qboolean	inupdate;
+	
+	va_start (argptr,fmt);
+	vsprintf (msg,fmt,argptr);
+	va_end (argptr);
+	
+// also echo to debugging console
+	Sys_Printf ("%s", msg);	// also echo to debugging console
+
+// log all messages to file
+	if (con_debuglog)
+		Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg);
+
+	if (!con_initialized)
+		return;
+		
+	if (cls.state == ca_dedicated)
+		return;		// no graphics mode
+
+// write it to the scrollable buffer
+	Con_Print (msg);
+	
+// update the screen if the console is displayed
+	if (cls.signon != SIGNONS && !scr_disabled_for_loading )
+	{
+	// protect against infinite loop if something in SCR_UpdateScreen calls
+	// Con_Printd
+		if (!inupdate)
+		{
+			inupdate = true;
+			SCR_UpdateScreen ();
+			inupdate = false;
+		}
+	}
+}
+
+/*
+================
+Con_DPrintf
+
+A Con_Printf that only shows up if the "developer" cvar is set
+================
+*/
+void Con_DPrintf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		msg[MAXPRINTMSG];
+        
+	if (!developer.value)
+            return;			// don't confuse non-developers with techie stuff...
+
+	va_start (argptr,fmt);
+	vsprintf (msg,fmt,argptr);
+	va_end (argptr);
+	
+	Con_Printf ("%s", msg);
+}
+
+
+/*
+==================
+Con_SafePrintf
+
+Okay to call even when the screen can't be updated
+==================
+*/
+void Con_SafePrintf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		msg[1024];
+	int			temp;
+		
+	va_start (argptr,fmt);
+	vsprintf (msg,fmt,argptr);
+	va_end (argptr);
+
+	temp = scr_disabled_for_loading;
+	scr_disabled_for_loading = true;
+	Con_Printf ("%s", msg);
+	scr_disabled_for_loading = temp;
+}
+
+
+/*
+==============================================================================
+
+DRAWING
+
+==============================================================================
+*/
+
+
+/*
+================
+Con_DrawInput
+
+The input line scrolls horizontally if typing goes beyond the right edge
+================
+*/
+void Con_DrawInput (void)
+{
+	int		y;
+	int		i;
+	char	*text;
+
+	if (key_dest != key_console && !con_forcedup)
+		return;		// don't draw anything
+
+	text = key_lines[edit_line];
+	
+// add the cursor frame
+	text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1);
+	
+// fill out remainder with spaces
+	for (i=key_linepos+1 ; i< con_linewidth ; i++)
+		text[i] = ' ';
+		
+//	prestep if horizontally scrolling
+	if (key_linepos >= con_linewidth)
+		text += 1 + key_linepos - con_linewidth;
+		
+// draw it
+	y = con_vislines-16;
+
+	for (i=0 ; i<con_linewidth ; i++)
+		Draw_Character ( (i+1)<<3, con_vislines - 16, text[i]);
+
+// remove cursor
+	key_lines[edit_line][key_linepos] = 0;
+}
+
+
+/*
+================
+Con_DrawNotify
+
+Draws the last few lines of output transparently over the game top
+================
+*/
+void Con_DrawNotify (void)
+{
+	int		x, v;
+	char	*text;
+	int		i;
+	float	time;
+	extern char chat_buffer[];
+
+	v = 0;
+	for (i= con_current-NUM_CON_TIMES+1 ; i<=con_current ; i++)
+	{
+		if (i < 0)
+			continue;
+		time = con_times[i % NUM_CON_TIMES];
+		if (time == 0)
+			continue;
+		time = realtime - time;
+		if (time > con_notifytime.value)
+			continue;
+		text = con_text + (i % con_totallines)*con_linewidth;
+		
+		clearnotify = 0;
+		scr_copytop = 1;
+
+		for (x = 0 ; x < con_linewidth ; x++)
+			Draw_Character ( (x+1)<<3, v, text[x]);
+
+		v += 8;
+	}
+
+
+	if (key_dest == key_message)
+	{
+		clearnotify = 0;
+		scr_copytop = 1;
+	
+		x = 0;
+		
+		Draw_String (8, v, "say:");
+		while(chat_buffer[x])
+		{
+			Draw_Character ( (x+5)<<3, v, chat_buffer[x]);
+			x++;
+		}
+		Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1));
+		v += 8;
+	}
+	
+	if (v > con_notifylines)
+		con_notifylines = v;
+}
+
+/*
+================
+Con_DrawConsole
+
+Draws the console with the solid background
+The typing input line at the bottom should only be drawn if typing is allowed
+================
+*/
+void Con_DrawConsole (int lines, qboolean drawinput)
+{
+	int				i, x, y;
+	int				rows;
+	char			*text;
+	int				j;
+	
+	if (lines <= 0)
+		return;
+
+// draw the background
+	Draw_ConsoleBackground (lines);
+
+// draw the text
+	con_vislines = lines;
+
+	rows = (lines-16)>>3;		// rows of text to draw
+	y = lines - 16 - (rows<<3);	// may start slightly negative
+
+	for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 )
+	{
+		j = i - con_backscroll;
+		if (j<0)
+			j = 0;
+		text = con_text + (j % con_totallines)*con_linewidth;
+
+		for (x=0 ; x<con_linewidth ; x++)
+			Draw_Character ( (x+1)<<3, y, text[x]);
+	}
+
+// draw the input prompt, user text, and cursor if desired
+	if (drawinput)
+		Con_DrawInput ();
+}
+
+
+/*
+==================
+Con_NotifyBox
+==================
+*/
+void Con_NotifyBox (char *text)
+{
+	double		t1, t2;
+
+// during startup for sound / cd warnings
+	Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
+
+	Con_Printf (text);
+
+	Con_Printf ("Press a key.\n");
+	Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
+
+	key_count = -2;		// wait for a key down and up
+	key_dest = key_console;
+
+	do
+	{
+		t1 = Sys_FloatTime ();
+		SCR_UpdateScreen ();
+		Sys_SendKeyEvents ();
+		t2 = Sys_FloatTime ();
+		realtime += t2-t1;		// make the cursor blink
+	} while (key_count < 0);
+
+	Con_Printf ("\n");
+	key_dest = key_game;
+	realtime = 0;				// put the cursor back to invisible
+}
+
diff --git a/apps/plugins/sdl/progs/quake/console.h b/apps/plugins/sdl/progs/quake/console.h
new file mode 100644
index 0000000..ef01f00
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/console.h
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// console
+//
+extern int con_totallines;
+extern int con_backscroll;
+extern	qboolean con_forcedup;	// because no entities to refresh
+extern qboolean con_initialized;
+extern byte *con_chars;
+extern	int	con_notifylines;		// scan lines to clear for notify lines
+
+void Con_DrawCharacter (int cx, int line, int num);
+
+void Con_CheckResize (void);
+void Con_Init (void);
+void Con_DrawConsole (int lines, qboolean drawinput);
+void Con_Print (char *txt);
+void Con_Printf (char *fmt, ...);
+void Con_DPrintf (char *fmt, ...);
+void Con_SafePrintf (char *fmt, ...);
+void Con_Clear_f (void);
+void Con_DrawNotify (void);
+void Con_ClearNotify (void);
+void Con_ToggleConsole_f (void);
+
+void Con_NotifyBox (char *text);	// during startup for sound / cd warnings
+
diff --git a/apps/plugins/sdl/progs/quake/crc.c b/apps/plugins/sdl/progs/quake/crc.c
new file mode 100644
index 0000000..ed4880c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/crc.c
@@ -0,0 +1,81 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/* crc.c */
+
+#include "quakedef.h"
+#include "crc.h"
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below...  in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE	0xffff
+#define CRC_XOR_VALUE	0x0000
+
+static unsigned short crctable[256] =
+{
+	0x0000,	0x1021,	0x2042,	0x3063,	0x4084,	0x50a5,	0x60c6,	0x70e7,
+	0x8108,	0x9129,	0xa14a,	0xb16b,	0xc18c,	0xd1ad,	0xe1ce,	0xf1ef,
+	0x1231,	0x0210,	0x3273,	0x2252,	0x52b5,	0x4294,	0x72f7,	0x62d6,
+	0x9339,	0x8318,	0xb37b,	0xa35a,	0xd3bd,	0xc39c,	0xf3ff,	0xe3de,
+	0x2462,	0x3443,	0x0420,	0x1401,	0x64e6,	0x74c7,	0x44a4,	0x5485,
+	0xa56a,	0xb54b,	0x8528,	0x9509,	0xe5ee,	0xf5cf,	0xc5ac,	0xd58d,
+	0x3653,	0x2672,	0x1611,	0x0630,	0x76d7,	0x66f6,	0x5695,	0x46b4,
+	0xb75b,	0xa77a,	0x9719,	0x8738,	0xf7df,	0xe7fe,	0xd79d,	0xc7bc,
+	0x48c4,	0x58e5,	0x6886,	0x78a7,	0x0840,	0x1861,	0x2802,	0x3823,
+	0xc9cc,	0xd9ed,	0xe98e,	0xf9af,	0x8948,	0x9969,	0xa90a,	0xb92b,
+	0x5af5,	0x4ad4,	0x7ab7,	0x6a96,	0x1a71,	0x0a50,	0x3a33,	0x2a12,
+	0xdbfd,	0xcbdc,	0xfbbf,	0xeb9e,	0x9b79,	0x8b58,	0xbb3b,	0xab1a,
+	0x6ca6,	0x7c87,	0x4ce4,	0x5cc5,	0x2c22,	0x3c03,	0x0c60,	0x1c41,
+	0xedae,	0xfd8f,	0xcdec,	0xddcd,	0xad2a,	0xbd0b,	0x8d68,	0x9d49,
+	0x7e97,	0x6eb6,	0x5ed5,	0x4ef4,	0x3e13,	0x2e32,	0x1e51,	0x0e70,
+	0xff9f,	0xefbe,	0xdfdd,	0xcffc,	0xbf1b,	0xaf3a,	0x9f59,	0x8f78,
+	0x9188,	0x81a9,	0xb1ca,	0xa1eb,	0xd10c,	0xc12d,	0xf14e,	0xe16f,
+	0x1080,	0x00a1,	0x30c2,	0x20e3,	0x5004,	0x4025,	0x7046,	0x6067,
+	0x83b9,	0x9398,	0xa3fb,	0xb3da,	0xc33d,	0xd31c,	0xe37f,	0xf35e,
+	0x02b1,	0x1290,	0x22f3,	0x32d2,	0x4235,	0x5214,	0x6277,	0x7256,
+	0xb5ea,	0xa5cb,	0x95a8,	0x8589,	0xf56e,	0xe54f,	0xd52c,	0xc50d,
+	0x34e2,	0x24c3,	0x14a0,	0x0481,	0x7466,	0x6447,	0x5424,	0x4405,
+	0xa7db,	0xb7fa,	0x8799,	0x97b8,	0xe75f,	0xf77e,	0xc71d,	0xd73c,
+	0x26d3,	0x36f2,	0x0691,	0x16b0,	0x6657,	0x7676,	0x4615,	0x5634,
+	0xd94c,	0xc96d,	0xf90e,	0xe92f,	0x99c8,	0x89e9,	0xb98a,	0xa9ab,
+	0x5844,	0x4865,	0x7806,	0x6827,	0x18c0,	0x08e1,	0x3882,	0x28a3,
+	0xcb7d,	0xdb5c,	0xeb3f,	0xfb1e,	0x8bf9,	0x9bd8,	0xabbb,	0xbb9a,
+	0x4a75,	0x5a54,	0x6a37,	0x7a16,	0x0af1,	0x1ad0,	0x2ab3,	0x3a92,
+	0xfd2e,	0xed0f,	0xdd6c,	0xcd4d,	0xbdaa,	0xad8b,	0x9de8,	0x8dc9,
+	0x7c26,	0x6c07,	0x5c64,	0x4c45,	0x3ca2,	0x2c83,	0x1ce0,	0x0cc1,
+	0xef1f,	0xff3e,	0xcf5d,	0xdf7c,	0xaf9b,	0xbfba,	0x8fd9,	0x9ff8,
+	0x6e17,	0x7e36,	0x4e55,	0x5e74,	0x2e93,	0x3eb2,	0x0ed1,	0x1ef0
+};
+
+void CRC_Init(unsigned short *crcvalue)
+{
+	*crcvalue = CRC_INIT_VALUE;
+}
+
+void CRC_ProcessByte(unsigned short *crcvalue, byte data)
+{
+	*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+	return crcvalue ^ CRC_XOR_VALUE;
+}
\ No newline at end of file
diff --git a/apps/plugins/sdl/progs/quake/crc.h b/apps/plugins/sdl/progs/quake/crc.h
new file mode 100644
index 0000000..cad9772
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/crc.h
@@ -0,0 +1,24 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/* crc.h */
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+unsigned short CRC_Value(unsigned short crcvalue);
diff --git a/apps/plugins/sdl/progs/quake/cvar.c b/apps/plugins/sdl/progs/quake/cvar.c
new file mode 100644
index 0000000..07a761a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cvar.c
@@ -0,0 +1,224 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cvar.c -- dynamic variable tracking
+
+#include "quakedef.h"
+
+cvar_t	*cvar_vars;
+char	*cvar_null_string = "";
+
+/*
+============
+Cvar_FindVar
+============
+*/
+cvar_t *Cvar_FindVar (char *var_name)
+{
+	cvar_t	*var;
+	
+	for (var=cvar_vars ; var ; var=var->next)
+		if (!Q_strcmp (var_name, var->name))
+			return var;
+
+	return NULL;
+}
+
+/*
+============
+Cvar_VariableValue
+============
+*/
+float	Cvar_VariableValue (char *var_name)
+{
+	cvar_t	*var;
+	
+	var = Cvar_FindVar (var_name);
+	if (!var)
+		return 0;
+	return Q_atof (var->string);
+}
+
+
+/*
+============
+Cvar_VariableString
+============
+*/
+char *Cvar_VariableString (char *var_name)
+{
+	cvar_t *var;
+	
+	var = Cvar_FindVar (var_name);
+	if (!var)
+		return cvar_null_string;
+	return var->string;
+}
+
+
+/*
+============
+Cvar_CompleteVariable
+============
+*/
+char *Cvar_CompleteVariable (char *partial)
+{
+	cvar_t		*cvar;
+	int			len;
+	
+	len = Q_strlen(partial);
+	
+	if (!len)
+		return NULL;
+		
+// check functions
+	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
+		if (!Q_strncmp (partial,cvar->name, len))
+			return cvar->name;
+
+	return NULL;
+}
+
+
+/*
+============
+Cvar_Set
+============
+*/
+void Cvar_Set (char *var_name, char *value)
+{
+	cvar_t	*var;
+	qboolean changed;
+	
+	var = Cvar_FindVar (var_name);
+	if (!var)
+	{	// there is an error in C code if this happens
+		Con_Printf ("Cvar_Set: variable %s not found\n", var_name);
+		return;
+	}
+
+	changed = Q_strcmp(var->string, value);
+	
+	Z_Free (var->string);	// free the old value string
+	
+	var->string = Z_Malloc (Q_strlen(value)+1);
+	Q_strcpy (var->string, value);
+	var->value = Q_atof (var->string);
+	if (var->server && changed)
+	{
+		if (sv.active)
+			SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);
+	}
+}
+
+/*
+============
+Cvar_SetValue
+============
+*/
+void Cvar_SetValue (char *var_name, float value)
+{
+	char	val[32];
+	
+	sprintf (val, "%f",value);
+	Cvar_Set (var_name, val);
+}
+
+
+/*
+============
+Cvar_RegisterVariable
+
+Adds a freestanding variable to the variable list.
+============
+*/
+void Cvar_RegisterVariable (cvar_t *variable)
+{
+	char	*oldstr;
+	
+// first check to see if it has allready been defined
+	if (Cvar_FindVar (variable->name))
+	{
+		Con_Printf ("Can't register variable %s, allready defined\n", variable->name);
+		return;
+	}
+	
+// check for overlap with a command
+	if (Cmd_Exists (variable->name))
+	{
+		Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
+		return;
+	}
+		
+// copy the value off, because future sets will Z_Free it
+	oldstr = variable->string;
+	variable->string = Z_Malloc (Q_strlen(variable->string)+1);	
+	Q_strcpy (variable->string, oldstr);
+	variable->value = Q_atof (variable->string);
+	
+// link the variable in
+	variable->next = cvar_vars;
+	cvar_vars = variable;
+}
+
+/*
+============
+Cvar_Command
+
+Handles variable inspection and changing from the console
+============
+*/
+qboolean	Cvar_Command (void)
+{
+	cvar_t			*v;
+
+// check variables
+	v = Cvar_FindVar (Cmd_Argv(0));
+	if (!v)
+		return false;
+		
+// perform a variable print or set
+	if (Cmd_Argc() == 1)
+	{
+		Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
+		return true;
+	}
+
+	Cvar_Set (v->name, Cmd_Argv(1));
+	return true;
+}
+
+
+/*
+============
+Cvar_WriteVariables
+
+Writes lines containing "set variable value" for all variables
+with the archive flag set to true.
+============
+*/
+void Cvar_WriteVariables (FILE *f)
+{
+	cvar_t	*var;
+	
+	for (var = cvar_vars ; var ; var = var->next)
+		if (var->archive)
+			fprintf (f, "%s \"%s\"\n", var->name, var->string);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/cvar.h b/apps/plugins/sdl/progs/quake/cvar.h
new file mode 100644
index 0000000..009b747
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/cvar.h
@@ -0,0 +1,97 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// cvar.h
+
+/*
+
+cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly
+in C code.
+
+it is sufficient to initialize a cvar_t with just the first two fields, or
+you can add a ,true flag for variables that you want saved to the configuration
+file when the game is quit:
+
+cvar_t	r_draworder = {"r_draworder","1"};
+cvar_t	scr_screensize = {"screensize","1",true};
+
+Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string.  Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed:
+Cvar_RegisterVariable (&host_framerate);
+
+
+C code usually just references a cvar in place:
+if ( r_draworder.value )
+
+It could optionally ask for the value to be looked up for a string name:
+if (Cvar_VariableValue ("r_draworder"))
+
+Interpreted prog code can access cvars with the cvar(name) or
+cvar_set (name, value) internal functions:
+teamplay = cvar("teamplay");
+cvar_set ("registered", "1");
+
+The user can access cvars from the console in two ways:
+r_draworder			prints the current value
+r_draworder 0		sets the current value to 0
+Cvars are restricted from having the same names as commands to keep this
+interface from being ambiguous.
+*/
+
+typedef struct cvar_s
+{
+	char	*name;
+	char	*string;
+	qboolean archive;		// set to true to cause it to be saved to vars.rc
+	qboolean server;		// notifies players when changed
+	float	value;
+	struct cvar_s *next;
+} cvar_t;
+
+void 	Cvar_RegisterVariable (cvar_t *variable);
+// registers a cvar that allready has the name, string, and optionally the
+// archive elements set.
+
+void 	Cvar_Set (char *var_name, char *value);
+// equivelant to "<name> <variable>" typed at the console
+
+void	Cvar_SetValue (char *var_name, float value);
+// expands value to a string and calls Cvar_Set
+
+float	Cvar_VariableValue (char *var_name);
+// returns 0 if not defined or non numeric
+
+char	*Cvar_VariableString (char *var_name);
+// returns an empty string if not defined
+
+char 	*Cvar_CompleteVariable (char *partial);
+// attempts to match a partial variable name for command line completion
+// returns NULL if nothing fits
+
+qboolean Cvar_Command (void);
+// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
+// command.  Returns true if the command was a variable reference that
+// was handled. (print or change)
+
+void 	Cvar_WriteVariables (FILE *f);
+// Writes lines containing "set variable value" for all variables
+// with the archive flag set to true.
+
+cvar_t *Cvar_FindVar (char *var_name);
+
+extern cvar_t	*cvar_vars;
diff --git a/apps/plugins/sdl/progs/quake/d_copy.S b/apps/plugins/sdl/progs/quake/d_copy.S
new file mode 100644
index 0000000..92e414a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_copy.S
@@ -0,0 +1,149 @@
+//
+// d_copy.s
+// x86 assembly-language screen copying code.
+//
+
+#include "asm_i386.h"
+#include "quakeasm.h"
+#include "asm_draw.h"
+
+	.data
+
+LCopyWidth:		.long	0
+LBlockSrcStep:	.long	0
+LBlockDestStep:	.long	0
+LSrcDelta:		.long	0
+LDestDelta:		.long	0
+
+#define bufptr	4+16
+
+// copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since
+// no Mode X mode is wider than 360, all the data should fit in the cache for
+// the passes for the next 3 planes
+
+	.text
+
+.globl C(VGA_UpdatePlanarScreen)
+C(VGA_UpdatePlanarScreen):
+	pushl	%ebp				// preserve caller's stack frame
+	pushl	%edi
+	pushl	%esi				// preserve register variables
+	pushl	%ebx
+
+	movl	C(VGA_bufferrowbytes),%eax
+	shll	$1,%eax
+	movl	%eax,LBlockSrcStep
+	movl	C(VGA_rowbytes),%eax
+	shll	$1,%eax
+	movl	%eax,LBlockDestStep
+
+	movl	$0x3C4,%edx
+	movb	$2,%al
+	outb	%al,%dx		// point the SC to the Map Mask
+	incl	%edx
+
+	movl	bufptr(%esp),%esi
+	movl	C(VGA_pagebase),%edi
+	movl	C(VGA_height),%ebp
+	shrl	$1,%ebp
+
+	movl	C(VGA_width),%ecx
+	movl	C(VGA_bufferrowbytes),%eax
+	subl	%ecx,%eax
+	movl	%eax,LSrcDelta
+	movl	C(VGA_rowbytes),%eax
+	shll	$2,%eax
+	subl	%ecx,%eax
+	movl	%eax,LDestDelta
+	shrl	$4,%ecx
+	movl	%ecx,LCopyWidth
+
+LRowLoop:
+	movb	$1,%al
+
+LPlaneLoop:
+	outb	%al,%dx
+	movb	$2,%ah
+
+	pushl	%esi
+	pushl	%edi
+LRowSetLoop:
+	movl	LCopyWidth,%ecx
+LColumnLoop:
+	movb	12(%esi),%bh
+	movb	8(%esi),%bl
+	shll	$16,%ebx
+	movb	4(%esi),%bh
+	movb	(%esi),%bl
+	movl	%ebx,(%edi)
+	addl	$16,%esi
+	addl	$4,%edi
+	decl	%ecx
+	jnz		LColumnLoop
+
+	addl	LDestDelta,%edi
+	addl	LSrcDelta,%esi
+	decb	%ah
+	jnz		LRowSetLoop
+
+	popl	%edi
+	popl	%esi
+	incl	%esi
+
+	shlb	$1,%al
+	cmpb	$16,%al
+	jnz		LPlaneLoop
+
+	subl	$4,%esi
+	addl	LBlockSrcStep,%esi
+	addl	LBlockDestStep,%edi
+	decl	%ebp
+	jnz		LRowLoop
+
+	popl	%ebx				// restore register variables
+	popl	%esi
+	popl	%edi
+	popl	%ebp				// restore the caller's stack frame
+
+	ret
+
+
+#define srcptr			4+16
+#define destptr			8+16
+#define width			12+16
+#define height			16+16
+#define srcrowbytes		20+16
+#define destrowbytes	24+16
+
+.globl C(VGA_UpdateLinearScreen)
+C(VGA_UpdateLinearScreen):
+	pushl	%ebp				// preserve caller's stack frame
+	pushl	%edi
+	pushl	%esi				// preserve register variables
+	pushl	%ebx
+
+	cld
+	movl	srcptr(%esp),%esi
+	movl	destptr(%esp),%edi
+	movl	width(%esp),%ebx
+	movl	srcrowbytes(%esp),%eax
+	subl	%ebx,%eax
+	movl	destrowbytes(%esp),%edx
+	subl	%ebx,%edx
+	shrl	$2,%ebx
+	movl	height(%esp),%ebp
+LLRowLoop:
+	movl	%ebx,%ecx
+	rep/movsl	(%esi),(%edi)
+	addl	%eax,%esi
+	addl	%edx,%edi
+	decl	%ebp
+	jnz		LLRowLoop
+
+	popl	%ebx				// restore register variables
+	popl	%esi
+	popl	%edi
+	popl	%ebp				// restore the caller's stack frame
+
+	ret
+
diff --git a/apps/plugins/sdl/progs/quake/d_edge.c b/apps/plugins/sdl/progs/quake/d_edge.c
new file mode 100644
index 0000000..52cc8eb
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_edge.c
@@ -0,0 +1,331 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_edge.c
+
+#include "quakedef.h"
+#include "d_local.h"
+
+static int	miplevel;
+
+float		scale_for_mip;
+int			screenwidth;
+int			ubasestep, errorterm, erroradjustup, erroradjustdown;
+int			vstartscan;
+
+// FIXME: should go away
+extern void			R_RotateBmodel (void);
+extern void			R_TransformFrustum (void);
+
+vec3_t		transformed_modelorg;
+
+/*
+==============
+D_DrawPoly
+
+==============
+*/
+void D_DrawPoly (void)
+{
+// this driver takes spans, not polygons
+}
+
+
+/*
+=============
+D_MipLevelForScale
+=============
+*/
+int D_MipLevelForScale (float scale)
+{
+	int		lmiplevel;
+
+	if (scale >= d_scalemip[0] )
+		lmiplevel = 0;
+	else if (scale >= d_scalemip[1] )
+		lmiplevel = 1;
+	else if (scale >= d_scalemip[2] )
+		lmiplevel = 2;
+	else
+		lmiplevel = 3;
+
+	if (lmiplevel < d_minmip)
+		lmiplevel = d_minmip;
+
+	return lmiplevel;
+}
+
+
+/*
+==============
+D_DrawSolidSurface
+==============
+*/
+
+// FIXME: clean this up
+
+void D_DrawSolidSurface (surf_t *surf, int color)
+{
+	espan_t	*span;
+	byte	*pdest;
+	int		u, u2, pix;
+	
+	pix = (color<<24) | (color<<16) | (color<<8) | color;
+	for (span=surf->spans ; span ; span=span->pnext)
+	{
+		pdest = (byte *)d_viewbuffer + screenwidth*span->v;
+		u = span->u;
+		u2 = span->u + span->count - 1;
+		((byte *)pdest)[u] = pix;
+
+		if (u2 - u < 8)
+		{
+			for (u++ ; u <= u2 ; u++)
+				((byte *)pdest)[u] = pix;
+		}
+		else
+		{
+			for (u++ ; u & 3 ; u++)
+				((byte *)pdest)[u] = pix;
+
+			u2 -= 4;
+			for ( ; u <= u2 ; u+=4)
+				*(int *)((byte *)pdest + u) = pix;
+			u2 += 4;
+			for ( ; u <= u2 ; u++)
+				((byte *)pdest)[u] = pix;
+		}
+	}
+}
+
+
+/*
+==============
+D_CalcGradients
+==============
+*/
+void D_CalcGradients (msurface_t *pface)
+{
+	mplane_t	*pplane;
+	float		mipscale;
+	vec3_t		p_temp1;
+	vec3_t		p_saxis, p_taxis;
+	float		t;
+
+	pplane = pface->plane;
+
+	mipscale = 1.0 / (float)(1 << miplevel);
+
+	TransformVector (pface->texinfo->vecs[0], p_saxis);
+	TransformVector (pface->texinfo->vecs[1], p_taxis);
+
+	t = xscaleinv * mipscale;
+	d_sdivzstepu = p_saxis[0] * t;
+	d_tdivzstepu = p_taxis[0] * t;
+
+	t = yscaleinv * mipscale;
+	d_sdivzstepv = -p_saxis[1] * t;
+	d_tdivzstepv = -p_taxis[1] * t;
+
+	d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu -
+			ycenter * d_sdivzstepv;
+	d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu -
+			ycenter * d_tdivzstepv;
+
+	VectorScale (transformed_modelorg, mipscale, p_temp1);
+
+	t = 0x10000*mipscale;
+	sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) -
+			((pface->texturemins[0] << 16) >> miplevel)
+			+ pface->texinfo->vecs[0][3]*t;
+	tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) -
+			((pface->texturemins[1] << 16) >> miplevel)
+			+ pface->texinfo->vecs[1][3]*t;
+
+//
+// -1 (-epsilon) so we never wander off the edge of the texture
+//
+	bbextents = ((pface->extents[0] << 16) >> miplevel) - 1;
+	bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1;
+}
+
+
+/*
+==============
+D_DrawSurfaces
+==============
+*/
+void D_DrawSurfaces (void)
+{
+	surf_t			*s;
+	msurface_t		*pface;
+	surfcache_t		*pcurrentcache;
+	vec3_t			world_transformed_modelorg;
+	vec3_t			local_modelorg;
+
+	currententity = &cl_entities[0];
+	TransformVector (modelorg, transformed_modelorg);
+	VectorCopy (transformed_modelorg, world_transformed_modelorg);
+
+// TODO: could preset a lot of this at mode set time
+	if (r_drawflat.value)
+	{
+		for (s = &surfaces[1] ; s<surface_p ; s++)
+		{
+			if (!s->spans)
+				continue;
+
+			d_zistepu = s->d_zistepu;
+			d_zistepv = s->d_zistepv;
+			d_ziorigin = s->d_ziorigin;
+
+			D_DrawSolidSurface (s, (int)s->data & 0xFF);
+			D_DrawZSpans (s->spans);
+		}
+	}
+	else
+	{
+		for (s = &surfaces[1] ; s<surface_p ; s++)
+		{
+			if (!s->spans)
+				continue;
+
+			r_drawnpolycount++;
+
+			d_zistepu = s->d_zistepu;
+			d_zistepv = s->d_zistepv;
+			d_ziorigin = s->d_ziorigin;
+
+			if (s->flags & SURF_DRAWSKY)
+			{
+				if (!r_skymade)
+				{
+					R_MakeSky ();
+				}
+
+				D_DrawSkyScans8 (s->spans);
+				D_DrawZSpans (s->spans);
+			}
+			else if (s->flags & SURF_DRAWBACKGROUND)
+			{
+			// set up a gradient for the background surface that places it
+			// effectively at infinity distance from the viewpoint
+				d_zistepu = 0;
+				d_zistepv = 0;
+				d_ziorigin = -0.9;
+
+				D_DrawSolidSurface (s, (int)r_clearcolor.value & 0xFF);
+				D_DrawZSpans (s->spans);
+			}
+			else if (s->flags & SURF_DRAWTURB)
+			{
+				pface = s->data;
+				miplevel = 0;
+				cacheblock = (pixel_t *)
+						((byte *)pface->texinfo->texture +
+						pface->texinfo->texture->offsets[0]);
+				cachewidth = 64;
+
+				if (s->insubmodel)
+				{
+				// FIXME: we don't want to do all this for every polygon!
+				// TODO: store once at start of frame
+					currententity = s->entity;	//FIXME: make this passed in to
+												// R_RotateBmodel ()
+					VectorSubtract (r_origin, currententity->origin,
+							local_modelorg);
+					TransformVector (local_modelorg, transformed_modelorg);
+
+					R_RotateBmodel ();	// FIXME: don't mess with the frustum,
+										// make entity passed in
+				}
+
+				D_CalcGradients (pface);
+				Turbulent8 (s->spans);
+				D_DrawZSpans (s->spans);
+
+				if (s->insubmodel)
+				{
+				//
+				// restore the old drawing state
+				// FIXME: we don't want to do this every time!
+				// TODO: speed up
+				//
+					currententity = &cl_entities[0];
+					VectorCopy (world_transformed_modelorg,
+								transformed_modelorg);
+					VectorCopy (base_vpn, vpn);
+					VectorCopy (base_vup, vup);
+					VectorCopy (base_vright, vright);
+					VectorCopy (base_modelorg, modelorg);
+					R_TransformFrustum ();
+				}
+			}
+			else
+			{
+				if (s->insubmodel)
+				{
+				// FIXME: we don't want to do all this for every polygon!
+				// TODO: store once at start of frame
+					currententity = s->entity;	//FIXME: make this passed in to
+												// R_RotateBmodel ()
+					VectorSubtract (r_origin, currententity->origin, local_modelorg);
+					TransformVector (local_modelorg, transformed_modelorg);
+
+					R_RotateBmodel ();	// FIXME: don't mess with the frustum,
+										// make entity passed in
+				}
+
+				pface = s->data;
+				miplevel = D_MipLevelForScale (s->nearzi * scale_for_mip
+				* pface->texinfo->mipadjust);
+
+			// FIXME: make this passed in to D_CacheSurface
+				pcurrentcache = D_CacheSurface (pface, miplevel);
+
+				cacheblock = (pixel_t *)pcurrentcache->data;
+				cachewidth = pcurrentcache->width;
+
+				D_CalcGradients (pface);
+
+				(*d_drawspans) (s->spans);
+
+				D_DrawZSpans (s->spans);
+
+				if (s->insubmodel)
+				{
+				//
+				// restore the old drawing state
+				// FIXME: we don't want to do this every time!
+				// TODO: speed up
+				//
+					currententity = &cl_entities[0];
+					VectorCopy (world_transformed_modelorg,
+								transformed_modelorg);
+					VectorCopy (base_vpn, vpn);
+					VectorCopy (base_vup, vup);
+					VectorCopy (base_vright, vright);
+					VectorCopy (base_modelorg, modelorg);
+					R_TransformFrustum ();
+				}
+			}
+		}
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_fill.c b/apps/plugins/sdl/progs/quake/d_fill.c
new file mode 100644
index 0000000..e6c1473
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_fill.c
@@ -0,0 +1,88 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_clear: clears a specified rectangle to the specified color
+
+#include "quakedef.h"
+
+
+/*
+================
+D_FillRect
+================
+*/
+void D_FillRect (vrect_t *rect, int color)
+{
+	int				rx, ry, rwidth, rheight;
+	unsigned char	*dest;
+	unsigned		*ldest;
+
+	rx = rect->x;
+	ry = rect->y;
+	rwidth = rect->width;
+	rheight = rect->height;
+
+	if (rx < 0)
+	{
+		rwidth += rx;
+		rx = 0;
+	}
+	if (ry < 0)
+	{
+		rheight += ry;
+		ry = 0;
+	}
+	if (rx+rwidth > vid.width)
+		rwidth = vid.width - rx;
+	if (ry+rheight > vid.height)
+		rheight = vid.height - rx;
+		
+	if (rwidth < 1 || rheight < 1)
+		return;
+
+	dest = ((byte *)vid.buffer + ry*vid.rowbytes + rx);
+
+	if (((rwidth & 0x03) == 0) && (((long)dest & 0x03) == 0))
+	{
+	// faster aligned dword clear
+		ldest = (unsigned *)dest;
+		color += color << 16;
+
+		rwidth >>= 2;
+		color += color << 8;
+
+		for (ry=0 ; ry<rheight ; ry++)
+		{
+			for (rx=0 ; rx<rwidth ; rx++)
+				ldest[rx] = color;
+			ldest = (unsigned *)((byte*)ldest + vid.rowbytes);
+		}
+	}
+	else
+	{
+	// slower byte-by-byte clear for unaligned cases
+		for (ry=0 ; ry<rheight ; ry++)
+		{
+			for (rx=0 ; rx<rwidth ; rx++)
+				dest[rx] = color;
+			dest += vid.rowbytes;
+		}
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_iface.h b/apps/plugins/sdl/progs/quake/d_iface.h
new file mode 100644
index 0000000..8dc5ce9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_iface.h
@@ -0,0 +1,231 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_iface.h: interface header file for rasterization driver modules
+
+#define WARP_WIDTH		320
+#define WARP_HEIGHT		200
+
+#define MAX_LBM_HEIGHT	480
+
+typedef struct
+{
+	float	u, v;
+	float	s, t;
+	float	zi;
+} emitpoint_t;
+
+typedef int ptype_t;
+
+enum {
+	pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2
+};
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct particle_s
+{
+// driver-usable fields
+	vec3_t		org;
+	float		color;
+// drivers never touch the following fields
+	struct particle_s	*next;
+	vec3_t		vel;
+	float		ramp;
+	float		die;
+	ptype_t		type;
+} particle_t;
+
+#define PARTICLE_Z_CLIP	8.0
+
+typedef struct polyvert_s {
+	float	u, v, zi, s, t;
+} polyvert_t;
+
+typedef struct polydesc_s {
+	int			numverts;
+	float		nearzi;
+	msurface_t	*pcurrentface;
+	polyvert_t	*pverts;
+} polydesc_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct finalvert_s {
+	int		v[6];		// u, v, s, t, l, 1/z
+	int		flags;
+	float	reserved;
+} finalvert_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct
+{
+	void				*pskin;
+	maliasskindesc_t	*pskindesc;
+	int					skinwidth;
+	int					skinheight;
+	mtriangle_t			*ptriangles;
+	finalvert_t			*pfinalverts;
+	int					numtriangles;
+	int					drawtype;
+	int					seamfixupX16;
+} affinetridesc_t;
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+typedef struct {
+	float	u, v, zi, color;
+} screenpart_t;
+
+typedef struct
+{
+	int			nump;
+	emitpoint_t	*pverts;	// there's room for an extra element at [nump], 
+							//  if the driver wants to duplicate element [0] at
+							//  element [nump] to avoid dealing with wrapping
+	mspriteframe_t	*pspriteframe;
+	vec3_t			vup, vright, vpn;	// in worldspace
+	float			nearzi;
+} spritedesc_t;
+
+typedef struct
+{
+	int		u, v;
+	float	zi;
+	int		color;
+} zpointdesc_t;
+
+extern cvar_t	r_drawflat;
+extern int		d_spanpixcount;
+extern int		r_framecount;		// sequence # of current frame since Quake
+									//  started
+extern qboolean	r_drawpolys;		// 1 if driver wants clipped polygons
+									//  rather than a span list
+extern qboolean	r_drawculledpolys;	// 1 if driver wants clipped polygons that
+									//  have been culled by the edge list
+extern qboolean	r_worldpolysbacktofront;	// 1 if driver wants polygons
+											//  delivered back to front rather
+											//  than front to back
+extern qboolean	r_recursiveaffinetriangles;	// true if a driver wants to use
+											//  recursive triangular subdivison
+											//  and vertex drawing via
+											//  D_PolysetDrawFinalVerts() past
+											//  a certain distance (normally 
+											//  only used by the software
+											//  driver)
+extern float	r_aliasuvscale;		// scale-up factor for screen u and v
+									//  on Alias vertices passed to driver
+extern int		r_pixbytes;
+extern qboolean	r_dowarp;
+
+extern affinetridesc_t	r_affinetridesc;
+extern spritedesc_t		r_spritedesc;
+extern zpointdesc_t		r_zpointdesc;
+extern polydesc_t		r_polydesc;
+
+extern int		d_con_indirect;	// if 0, Quake will draw console directly
+								//  to vid.buffer; if 1, Quake will
+								//  draw console via D_DrawRect. Must be
+								//  defined by driver
+
+extern vec3_t	r_pright, r_pup, r_ppn;
+
+
+void D_Aff8Patch (void *pcolormap);
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height);
+void D_DisableBackBufferAccess (void);
+void D_EndDirectRect (int x, int y, int width, int height);
+void D_PolysetDraw (void);
+void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts);
+void D_DrawParticle (particle_t *pparticle);
+void D_DrawPoly (void);
+void D_DrawSprite (void);
+void D_DrawSurfaces (void);
+void D_DrawZPoint (void);
+void D_EnableBackBufferAccess (void);
+void D_EndParticles (void);
+void D_Init (void);
+void D_ViewChanged (void);
+void D_SetupFrame (void);
+void D_StartParticles (void);
+void D_TurnZOn (void);
+void D_WarpScreen (void);
+
+void D_FillRect (vrect_t *vrect, int color);
+void D_DrawRect (void);
+void D_UpdateRects (vrect_t *prect);
+
+// currently for internal use only, and should be a do-nothing function in
+// hardware drivers
+// FIXME: this should go away
+void D_PolysetUpdateTables (void);
+
+// these are currently for internal use only, and should not be used by drivers
+extern int				r_skydirect;
+extern byte				*r_skysource;
+
+// transparency types for D_DrawRect ()
+#define DR_SOLID		0
+#define DR_TRANSPARENT	1
+
+// !!! must be kept the same as in quakeasm.h !!!
+#define TRANSPARENT_COLOR	0xFF
+
+extern void *acolormap;	// FIXME: should go away
+
+//=======================================================================//
+
+// callbacks to Quake
+
+typedef struct
+{
+	pixel_t		*surfdat;	// destination for generated surface
+	int			rowbytes;	// destination logical width in bytes
+	msurface_t	*surf;		// description for surface to generate
+	fixed8_t	lightadj[MAXLIGHTMAPS];
+							// adjust for lightmap levels for dynamic lighting
+	texture_t	*texture;	// corrected for animating textures
+	int			surfmip;	// mipmapped ratio of surface texels / world pixels
+	int			surfwidth;	// in mipmapped texels
+	int			surfheight;	// in mipmapped texels
+} drawsurf_t;
+
+extern drawsurf_t	r_drawsurf;
+
+void R_DrawSurface (void);
+void R_GenTile (msurface_t *psurf, void *pdest);
+
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define TURB_TEX_SIZE	64		// base turbulent texture size
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define	CYCLE			128		// turbulent cycle size
+
+#define TILE_SIZE		128		// size of textures generated by R_GenTiledSurf
+
+#define SKYSHIFT		7
+#define	SKYSIZE			(1 << SKYSHIFT)
+#define SKYMASK			(SKYSIZE - 1)
+
+extern float	skyspeed, skyspeed2;
+extern float	skytime;
+
+extern int		c_surf;
+extern vrect_t	scr_vrect;
+
+extern byte		*r_warpbuffer;
+
diff --git a/apps/plugins/sdl/progs/quake/d_ifacea.h b/apps/plugins/sdl/progs/quake/d_ifacea.h
new file mode 100644
index 0000000..241952a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_ifacea.h
@@ -0,0 +1,98 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// d_ifacea.h
+//
+// Include file for asm driver interface.
+//
+
+//
+// !!! note that this file must match the corresponding C structures in
+// d_iface.h at all times !!!
+//
+
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define ALIAS_ONSEAM				0x0020
+
+// !!! if this is changed, it must be changed in d_iface.h too !!!
+#define TURB_TEX_SIZE	64		// base turbulent texture size
+
+// !!! if this is changed, it must be changed in d_iface.h too !!!
+#define	CYCLE	128
+
+// !!! if this is changed, it must be changed in r_shared.h too !!!
+#define	MAXHEIGHT	1024
+
+// !!! if this is changed, it must be changed in quakedef.h too !!!
+#define CACHE_SIZE	32		// used to align key data structures
+
+// particle_t structure
+// !!! if this is changed, it must be changed in d_iface.h too !!!
+// driver-usable fields
+#define pt_org				0
+#define pt_color			12
+// drivers never touch the following fields
+#define pt_next				16
+#define pt_vel				20
+#define pt_ramp				32
+#define pt_die				36
+#define pt_type				40
+#define pt_size				44
+
+#define PARTICLE_Z_CLIP	8.0
+
+// finalvert_t structure
+// !!! if this is changed, it must be changed in d_iface.h too !!!
+#define fv_v				0	// !!! if this is moved, cases where the !!!
+								// !!! address of this field is pushed in !!!
+								// !!! d_polysa.s must be changed !!!
+#define fv_flags			24
+#define fv_reserved			28
+#define fv_size				32
+#define fv_shift			5
+
+
+// stvert_t structure
+// !!! if this is changed, it must be changed in modelgen.h too !!!
+#define stv_onseam	0
+#define stv_s		4
+#define stv_t		8
+#define stv_size	12
+
+
+// trivertx_t structure
+// !!! if this is changed, it must be changed in modelgen.h too !!!
+#define tv_v				0
+#define tv_lightnormalindex	3
+#define tv_size				4
+
+// affinetridesc_t structure
+// !!! if this is changed, it must be changed in d_iface.h too !!!
+#define atd_pskin			0
+#define atd_pskindesc		4
+#define atd_skinwidth		8
+#define atd_skinheight		12
+#define atd_ptriangles		16
+#define atd_pfinalverts		20
+#define atd_numtriangles	24
+#define atd_drawtype		28
+#define atd_seamfixupX16	32
+#define atd_size			36
+
diff --git a/apps/plugins/sdl/progs/quake/d_init.c b/apps/plugins/sdl/progs/quake/d_init.c
new file mode 100644
index 0000000..4c360fa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_init.c
@@ -0,0 +1,173 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_init.c: rasterization driver initialization
+
+#include "quakedef.h"
+#include "d_local.h"
+
+#define NUM_MIPS	4
+
+cvar_t	d_subdiv16 = {"d_subdiv16", "1"};
+cvar_t	d_mipcap = {"d_mipcap", "0"};
+cvar_t	d_mipscale = {"d_mipscale", "1"};
+
+surfcache_t		*d_initial_rover;
+qboolean		d_roverwrapped;
+int				d_minmip;
+float			d_scalemip[NUM_MIPS-1];
+
+static float	basemip[NUM_MIPS-1] = {1.0, 0.5*0.8, 0.25*0.8};
+
+extern int			d_aflatcolor;
+
+void (*d_drawspans) (espan_t *pspan);
+
+
+/*
+===============
+D_Init
+===============
+*/
+void D_Init (void)
+{
+
+	r_skydirect = 1;
+
+	Cvar_RegisterVariable (&d_subdiv16);
+	Cvar_RegisterVariable (&d_mipcap);
+	Cvar_RegisterVariable (&d_mipscale);
+
+	r_drawpolys = false;
+	r_worldpolysbacktofront = false;
+	r_recursiveaffinetriangles = true;
+	r_pixbytes = 1;
+	r_aliasuvscale = 1.0;
+}
+
+
+/*
+===============
+D_CopyRects
+===============
+*/
+void D_CopyRects (vrect_t *prects, int transparent)
+{
+
+// this function is only required if the CPU doesn't have direct access to the
+// back buffer, and there's some driver interface function that the driver
+// doesn't support and requires Quake to do in software (such as drawing the
+// console); Quake will then draw into wherever the driver points vid.buffer
+// and will call this function before swapping buffers
+
+	UNUSED(prects);
+	UNUSED(transparent);
+}
+
+
+/*
+===============
+D_EnableBackBufferAccess
+===============
+*/
+void D_EnableBackBufferAccess (void)
+{
+	VID_LockBuffer ();
+}
+
+
+/*
+===============
+D_TurnZOn
+===============
+*/
+void D_TurnZOn (void)
+{
+// not needed for software version
+}
+
+
+/*
+===============
+D_DisableBackBufferAccess
+===============
+*/
+void D_DisableBackBufferAccess (void)
+{
+	VID_UnlockBuffer ();
+}
+
+
+/*
+===============
+D_SetupFrame
+===============
+*/
+void D_SetupFrame (void)
+{
+	int		i;
+
+	if (r_dowarp)
+		d_viewbuffer = r_warpbuffer;
+	else
+		d_viewbuffer = (void *)(byte *)vid.buffer;
+
+	if (r_dowarp)
+		screenwidth = WARP_WIDTH;
+	else
+		screenwidth = vid.rowbytes;
+
+	d_roverwrapped = false;
+	d_initial_rover = sc_rover;
+
+	d_minmip = d_mipcap.value;
+	if (d_minmip > 3)
+		d_minmip = 3;
+	else if (d_minmip < 0)
+		d_minmip = 0;
+
+	for (i=0 ; i<(NUM_MIPS-1) ; i++)
+		d_scalemip[i] = basemip[i] * d_mipscale.value;
+
+#if	id386
+				if (d_subdiv16.value)
+					d_drawspans = D_DrawSpans16;
+				else
+					d_drawspans = D_DrawSpans8;
+#else
+				d_drawspans = D_DrawSpans8;
+#endif
+
+	d_aflatcolor = 0;
+}
+
+
+/*
+===============
+D_UpdateRects
+===============
+*/
+void D_UpdateRects (vrect_t *prect)
+{
+
+// the software driver draws these directly to the vid buffer
+
+	UNUSED(prect);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_local.h b/apps/plugins/sdl/progs/quake/d_local.h
new file mode 100644
index 0000000..af92489
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_local.h
@@ -0,0 +1,111 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_local.h:  private rasterization driver defs
+
+#include "r_shared.h"
+
+//
+// TODO: fine-tune this; it's based on providing some overage even if there
+// is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each
+//
+#define SCANBUFFERPAD		0x1000
+
+#define R_SKY_SMASK	0x007F0000
+#define R_SKY_TMASK	0x007F0000
+
+#define DS_SPAN_LIST_END	-128
+
+#define SURFCACHE_SIZE_AT_320X200	600*1024
+
+typedef struct surfcache_s
+{
+	struct surfcache_s	*next;
+	struct surfcache_s 	**owner;		// NULL is an empty chunk of memory
+	int					lightadj[MAXLIGHTMAPS]; // checked for strobe flush
+	int					dlight;
+	int					size;		// including header
+	unsigned			width;
+	unsigned			height;		// DEBUG only needed for debug
+	float				mipscale;
+	struct texture_s	*texture;	// checked for animating textures
+	byte				data[4];	// width*height elements
+} surfcache_t;
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct sspan_s
+{
+	int				u, v, count;
+} sspan_t;
+
+extern cvar_t	d_subdiv16;
+
+extern float	scale_for_mip;
+
+extern qboolean		d_roverwrapped;
+extern surfcache_t	*sc_rover;
+extern surfcache_t	*d_initial_rover;
+
+extern float	d_sdivzstepu, d_tdivzstepu, d_zistepu;
+extern float	d_sdivzstepv, d_tdivzstepv, d_zistepv;
+extern float	d_sdivzorigin, d_tdivzorigin, d_ziorigin;
+
+fixed16_t	sadjust, tadjust;
+fixed16_t	bbextents, bbextentt;
+
+
+void D_DrawSpans8 (espan_t *pspans);
+void D_DrawSpans16 (espan_t *pspans);
+void D_DrawZSpans (espan_t *pspans);
+void Turbulent8 (espan_t *pspan);
+void D_SpriteDrawSpans (sspan_t *pspan);
+
+void D_DrawSkyScans8 (espan_t *pspan);
+void D_DrawSkyScans16 (espan_t *pspan);
+
+void R_ShowSubDiv (void);
+void (*prealspandrawer)(void);
+surfcache_t	*D_CacheSurface (msurface_t *surface, int miplevel);
+
+extern int D_MipLevelForScale (float scale);
+
+#if id386
+extern void D_PolysetAff8Start (void);
+extern void D_PolysetAff8End (void);
+#endif
+
+extern short *d_pzbuffer;
+extern unsigned int d_zrowbytes, d_zwidth;
+
+extern int	*d_pscantable;
+extern int	d_scantable[MAXHEIGHT];
+
+extern int	d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle;
+
+extern int	d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift;
+
+extern pixel_t	*d_viewbuffer;
+
+extern short	*zspantable[MAXHEIGHT];
+
+extern int		d_minmip;
+extern float	d_scalemip[3];
+
+extern void (*d_drawspans) (espan_t *pspan);
+
diff --git a/apps/plugins/sdl/progs/quake/d_modech.c b/apps/plugins/sdl/progs/quake/d_modech.c
new file mode 100644
index 0000000..75c188e
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_modech.c
@@ -0,0 +1,107 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_modech.c: called when mode has just changed
+
+#include "quakedef.h"
+#include "d_local.h"
+
+int	d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle;
+
+int	d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift;
+
+int		d_scantable[MAXHEIGHT];
+short	*zspantable[MAXHEIGHT]; 
+
+/*
+================
+D_Patch
+================
+*/
+void D_Patch (void)
+{
+#if id386
+
+	static qboolean protectset8 = false;
+
+	if (!protectset8)
+	{
+		Sys_MakeCodeWriteable ((int)D_PolysetAff8Start,
+						     (int)D_PolysetAff8End - (int)D_PolysetAff8Start);
+		protectset8 = true;
+	}
+
+#endif	// id386
+}
+
+
+/*
+================
+D_ViewChanged
+================
+*/
+void D_ViewChanged (void)
+{
+	int rowbytes;
+
+	if (r_dowarp)
+		rowbytes = WARP_WIDTH;
+	else
+		rowbytes = vid.rowbytes;
+
+	scale_for_mip = xscale;
+	if (yscale > xscale)
+		scale_for_mip = yscale;
+
+	d_zrowbytes = vid.width * 2;
+	d_zwidth = vid.width;
+
+	d_pix_min = r_refdef.vrect.width / 320;
+	if (d_pix_min < 1)
+		d_pix_min = 1;
+
+	d_pix_max = (int)((float)r_refdef.vrect.width / (320.0 / 4.0) + 0.5);
+	d_pix_shift = 8 - (int)((float)r_refdef.vrect.width / 320.0 + 0.5);
+	if (d_pix_max < 1)
+		d_pix_max = 1;
+
+	if (pixelAspect > 1.4)
+		d_y_aspect_shift = 1;
+	else
+		d_y_aspect_shift = 0;
+
+	d_vrectx = r_refdef.vrect.x;
+	d_vrecty = r_refdef.vrect.y;
+	d_vrectright_particle = r_refdef.vrectright - d_pix_max;
+	d_vrectbottom_particle =
+			r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift);
+
+	{
+		int		i;
+
+		for (i=0 ; i<vid.height; i++)
+		{
+			d_scantable[i] = i*rowbytes;
+			zspantable[i] = d_pzbuffer + i*d_zwidth;
+		}
+	}
+
+	D_Patch ();
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_part.c b/apps/plugins/sdl/progs/quake/d_part.c
new file mode 100644
index 0000000..176b76b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_part.c
@@ -0,0 +1,207 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_part.c: software driver module for drawing particles
+
+#include "quakedef.h"
+#include "d_local.h"
+
+
+/*
+==============
+D_EndParticles
+==============
+*/
+void D_EndParticles (void)
+{
+// not used by software driver
+}
+
+
+/*
+==============
+D_StartParticles
+==============
+*/
+void D_StartParticles (void)
+{
+// not used by software driver
+}
+
+
+#if	!id386
+
+/*
+==============
+D_DrawParticle
+==============
+*/
+void D_DrawParticle (particle_t *pparticle)
+{
+	vec3_t	local, transformed;
+	float	zi;
+	byte	*pdest;
+	short	*pz;
+	int		i, izi, pix, count, u, v;
+
+// transform point
+	VectorSubtract (pparticle->org, r_origin, local);
+
+	transformed[0] = DotProduct(local, r_pright);
+	transformed[1] = DotProduct(local, r_pup);
+	transformed[2] = DotProduct(local, r_ppn);		
+
+	if (transformed[2] < PARTICLE_Z_CLIP)
+		return;
+
+// project the point
+// FIXME: preadjust xcenter and ycenter
+	zi = 1.0 / transformed[2];
+	u = (int)(xcenter + zi * transformed[0] + 0.5);
+	v = (int)(ycenter - zi * transformed[1] + 0.5);
+
+	if ((v > d_vrectbottom_particle) || 
+		(u > d_vrectright_particle) ||
+		(v < d_vrecty) ||
+		(u < d_vrectx))
+	{
+		return;
+	}
+
+	pz = d_pzbuffer + (d_zwidth * v) + u;
+	pdest = d_viewbuffer + d_scantable[v] + u;
+	izi = (int)(zi * 0x8000);
+
+	pix = izi >> d_pix_shift;
+
+	if (pix < d_pix_min)
+		pix = d_pix_min;
+	else if (pix > d_pix_max)
+		pix = d_pix_max;
+
+	switch (pix)
+	{
+	case 1:
+		count = 1 << d_y_aspect_shift;
+
+		for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth)
+		{
+			if (pz[0] <= izi)
+			{
+				pz[0] = izi;
+				pdest[0] = pparticle->color;
+			}
+		}
+		break;
+
+	case 2:
+		count = 2 << d_y_aspect_shift;
+
+		for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth)
+		{
+			if (pz[0] <= izi)
+			{
+				pz[0] = izi;
+				pdest[0] = pparticle->color;
+			}
+
+			if (pz[1] <= izi)
+			{
+				pz[1] = izi;
+				pdest[1] = pparticle->color;
+			}
+		}
+		break;
+
+	case 3:
+		count = 3 << d_y_aspect_shift;
+
+		for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth)
+		{
+			if (pz[0] <= izi)
+			{
+				pz[0] = izi;
+				pdest[0] = pparticle->color;
+			}
+
+			if (pz[1] <= izi)
+			{
+				pz[1] = izi;
+				pdest[1] = pparticle->color;
+			}
+
+			if (pz[2] <= izi)
+			{
+				pz[2] = izi;
+				pdest[2] = pparticle->color;
+			}
+		}
+		break;
+
+	case 4:
+		count = 4 << d_y_aspect_shift;
+
+		for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth)
+		{
+			if (pz[0] <= izi)
+			{
+				pz[0] = izi;
+				pdest[0] = pparticle->color;
+			}
+
+			if (pz[1] <= izi)
+			{
+				pz[1] = izi;
+				pdest[1] = pparticle->color;
+			}
+
+			if (pz[2] <= izi)
+			{
+				pz[2] = izi;
+				pdest[2] = pparticle->color;
+			}
+
+			if (pz[3] <= izi)
+			{
+				pz[3] = izi;
+				pdest[3] = pparticle->color;
+			}
+		}
+		break;
+
+	default:
+		count = pix << d_y_aspect_shift;
+
+		for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth)
+		{
+			for (i=0 ; i<pix ; i++)
+			{
+				if (pz[i] <= izi)
+				{
+					pz[i] = izi;
+					pdest[i] = pparticle->color;
+				}
+			}
+		}
+		break;
+	}
+}
+
+#endif	// !id386
+
diff --git a/apps/plugins/sdl/progs/quake/d_polyse.c b/apps/plugins/sdl/progs/quake/d_polyse.c
new file mode 100644
index 0000000..9acd34b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_polyse.c
@@ -0,0 +1,1111 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_polyset.c: routines for drawing sets of polygons sharing the same
+// texture (used for Alias models)
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+// TODO: put in span spilling to shrink list size
+// !!! if this is changed, it must be changed in d_polysa.s too !!!
+#define DPS_MAXSPANS			MAXHEIGHT+1	
+									// 1 extra for spanpackage that marks end
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct {
+	void			*pdest;
+	short			*pz;
+	int				count;
+	byte			*ptex;
+	int				sfrac, tfrac, light, zi;
+} spanpackage_t;
+
+typedef struct {
+	int		isflattop;
+	int		numleftedges;
+	int		*pleftedgevert0;
+	int		*pleftedgevert1;
+	int		*pleftedgevert2;
+	int		numrightedges;
+	int		*prightedgevert0;
+	int		*prightedgevert1;
+	int		*prightedgevert2;
+} edgetable;
+
+int	r_p0[6], r_p1[6], r_p2[6];
+
+byte		*d_pcolormap;
+
+int			d_aflatcolor;
+int			d_xdenom;
+
+edgetable	*pedgetable;
+
+edgetable	edgetables[12] = {
+	{0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2 },
+	{0, 2, r_p1, r_p0, r_p2,   1, r_p1, r_p2, NULL},
+	{1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL},
+	{0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0 },
+	{0, 2, r_p0, r_p2, r_p1,   1, r_p0, r_p1, NULL},
+	{0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL},
+	{0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1 },
+	{0, 2, r_p2, r_p1, r_p0,   1, r_p2, r_p0, NULL},
+	{0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL},
+	{1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL},
+	{1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL},
+	{0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL},
+};
+
+// FIXME: some of these can become statics
+int				a_sstepxfrac, a_tstepxfrac, r_lstepx, a_ststepxwhole;
+int				r_sstepx, r_tstepx, r_lstepy, r_sstepy, r_tstepy;
+int				r_zistepx, r_zistepy;
+int				d_aspancount, d_countextrastep;
+
+spanpackage_t			*a_spans;
+spanpackage_t			*d_pedgespanpackage;
+static int				ystart;
+byte					*d_pdest, *d_ptex;
+short					*d_pz;
+int						d_sfrac, d_tfrac, d_light, d_zi;
+int						d_ptexextrastep, d_sfracextrastep;
+int						d_tfracextrastep, d_lightextrastep, d_pdestextrastep;
+int						d_lightbasestep, d_pdestbasestep, d_ptexbasestep;
+int						d_sfracbasestep, d_tfracbasestep;
+int						d_ziextrastep, d_zibasestep;
+int						d_pzextrastep, d_pzbasestep;
+
+typedef struct {
+	int		quotient;
+	int		remainder;
+} adivtab_t;
+
+static adivtab_t	adivtab[32*32] = {
+#include "adivtab.h"
+};
+
+byte	*skintable[MAX_LBM_HEIGHT];
+int		skinwidth;
+byte	*skinstart;
+
+void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage);
+void D_PolysetCalcGradients (int skinwidth);
+void D_DrawSubdiv (void);
+void D_DrawNonSubdiv (void);
+void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3);
+void D_PolysetSetEdgeTable (void);
+void D_RasterizeAliasPolySmooth (void);
+void D_PolysetScanLeftEdge (int height);
+
+#if	!id386
+
+/*
+================
+D_PolysetDraw
+================
+*/
+void D_PolysetDraw (void)
+{
+	spanpackage_t	spans[DPS_MAXSPANS + 1 +
+			((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1];
+						// one extra because of cache line pretouching
+
+	a_spans = (spanpackage_t *)
+			(((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
+
+	if (r_affinetridesc.drawtype)
+	{
+		D_DrawSubdiv ();
+	}
+	else
+	{
+		D_DrawNonSubdiv ();
+	}
+}
+
+
+/*
+================
+D_PolysetDrawFinalVerts
+================
+*/
+void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts)
+{
+	int		i, z;
+	short	*zbuf;
+
+	for (i=0 ; i<numverts ; i++, fv++)
+	{
+	// valid triangle coordinates for filling can include the bottom and
+	// right clip edges, due to the fill rule; these shouldn't be drawn
+		if ((fv->v[0] < r_refdef.vrectright) &&
+			(fv->v[1] < r_refdef.vrectbottom))
+		{
+			z = fv->v[5]>>16;
+			zbuf = zspantable[fv->v[1]] + fv->v[0];
+			if (z >= *zbuf)
+			{
+				int		pix;
+				
+				*zbuf = z;
+				pix = skintable[fv->v[3]>>16][fv->v[2]>>16];
+				pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00) ];
+				d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix;
+			}
+		}
+	}
+}
+
+
+/*
+================
+D_DrawSubdiv
+================
+*/
+void D_DrawSubdiv (void)
+{
+	mtriangle_t		*ptri;
+	finalvert_t		*pfv, *index0, *index1, *index2;
+	int				i;
+	int				lnumtriangles;
+
+	pfv = r_affinetridesc.pfinalverts;
+	ptri = r_affinetridesc.ptriangles;
+	lnumtriangles = r_affinetridesc.numtriangles;
+
+	for (i=0 ; i<lnumtriangles ; i++)
+	{
+		index0 = pfv + ptri[i].vertindex[0];
+		index1 = pfv + ptri[i].vertindex[1];
+		index2 = pfv + ptri[i].vertindex[2];
+
+		if (((index0->v[1]-index1->v[1]) *
+			 (index0->v[0]-index2->v[0]) -
+			 (index0->v[0]-index1->v[0]) * 
+			 (index0->v[1]-index2->v[1])) >= 0)
+		{
+			continue;
+		}
+
+		d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00];
+
+		if (ptri[i].facesfront)
+		{
+			D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v);
+		}
+		else
+		{
+			int		s0, s1, s2;
+
+			s0 = index0->v[2];
+			s1 = index1->v[2];
+			s2 = index2->v[2];
+
+			if (index0->flags & ALIAS_ONSEAM)
+				index0->v[2] += r_affinetridesc.seamfixupX16;
+			if (index1->flags & ALIAS_ONSEAM)
+				index1->v[2] += r_affinetridesc.seamfixupX16;
+			if (index2->flags & ALIAS_ONSEAM)
+				index2->v[2] += r_affinetridesc.seamfixupX16;
+
+			D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v);
+
+			index0->v[2] = s0;
+			index1->v[2] = s1;
+			index2->v[2] = s2;
+		}
+	}
+}
+
+
+/*
+================
+D_DrawNonSubdiv
+================
+*/
+void D_DrawNonSubdiv (void)
+{
+	mtriangle_t		*ptri;
+	finalvert_t		*pfv, *index0, *index1, *index2;
+	int				i;
+	int				lnumtriangles;
+
+	pfv = r_affinetridesc.pfinalverts;
+	ptri = r_affinetridesc.ptriangles;
+	lnumtriangles = r_affinetridesc.numtriangles;
+
+	for (i=0 ; i<lnumtriangles ; i++, ptri++)
+	{
+		index0 = pfv + ptri->vertindex[0];
+		index1 = pfv + ptri->vertindex[1];
+		index2 = pfv + ptri->vertindex[2];
+
+		d_xdenom = (index0->v[1]-index1->v[1]) *
+				(index0->v[0]-index2->v[0]) -
+				(index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]);
+
+		if (d_xdenom >= 0)
+		{
+			continue;
+		}
+
+		r_p0[0] = index0->v[0];		// u
+		r_p0[1] = index0->v[1];		// v
+		r_p0[2] = index0->v[2];		// s
+		r_p0[3] = index0->v[3];		// t
+		r_p0[4] = index0->v[4];		// light
+		r_p0[5] = index0->v[5];		// iz
+
+		r_p1[0] = index1->v[0];
+		r_p1[1] = index1->v[1];
+		r_p1[2] = index1->v[2];
+		r_p1[3] = index1->v[3];
+		r_p1[4] = index1->v[4];
+		r_p1[5] = index1->v[5];
+
+		r_p2[0] = index2->v[0];
+		r_p2[1] = index2->v[1];
+		r_p2[2] = index2->v[2];
+		r_p2[3] = index2->v[3];
+		r_p2[4] = index2->v[4];
+		r_p2[5] = index2->v[5];
+
+		if (!ptri->facesfront)
+		{
+			if (index0->flags & ALIAS_ONSEAM)
+				r_p0[2] += r_affinetridesc.seamfixupX16;
+			if (index1->flags & ALIAS_ONSEAM)
+				r_p1[2] += r_affinetridesc.seamfixupX16;
+			if (index2->flags & ALIAS_ONSEAM)
+				r_p2[2] += r_affinetridesc.seamfixupX16;
+		}
+
+		D_PolysetSetEdgeTable ();
+		D_RasterizeAliasPolySmooth ();
+	}
+}
+
+
+/*
+================
+D_PolysetRecursiveTriangle
+================
+*/
+void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3)
+{
+	int		*temp;
+	int		d;
+	int		new[6];
+	int		z;
+	short	*zbuf;
+
+	d = lp2[0] - lp1[0];
+	if (d < -1 || d > 1)
+		goto split;
+	d = lp2[1] - lp1[1];
+	if (d < -1 || d > 1)
+		goto split;
+
+	d = lp3[0] - lp2[0];
+	if (d < -1 || d > 1)
+		goto split2;
+	d = lp3[1] - lp2[1];
+	if (d < -1 || d > 1)
+		goto split2;
+
+	d = lp1[0] - lp3[0];
+	if (d < -1 || d > 1)
+		goto split3;
+	d = lp1[1] - lp3[1];
+	if (d < -1 || d > 1)
+	{
+split3:
+		temp = lp1;
+		lp1 = lp3;
+		lp3 = lp2;
+		lp2 = temp;
+
+		goto split;
+	}
+
+	return;			// entire tri is filled
+
+split2:
+	temp = lp1;
+	lp1 = lp2;
+	lp2 = lp3;
+	lp3 = temp;
+
+split:
+// split this edge
+	new[0] = (lp1[0] + lp2[0]) >> 1;
+	new[1] = (lp1[1] + lp2[1]) >> 1;
+	new[2] = (lp1[2] + lp2[2]) >> 1;
+	new[3] = (lp1[3] + lp2[3]) >> 1;
+	new[5] = (lp1[5] + lp2[5]) >> 1;
+
+// draw the point if splitting a leading edge
+	if (lp2[1] > lp1[1])
+		goto nodraw;
+	if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))
+		goto nodraw;
+
+
+	z = new[5]>>16;
+	zbuf = zspantable[new[1]] + new[0];
+	if (z >= *zbuf)
+	{
+		int		pix;
+		
+		*zbuf = z;
+		pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]];
+		d_viewbuffer[d_scantable[new[1]] + new[0]] = pix;
+	}
+
+nodraw:
+// recursively continue
+	D_PolysetRecursiveTriangle (lp3, lp1, new);
+	D_PolysetRecursiveTriangle (lp3, new, lp2);
+}
+
+#endif	// !id386
+
+
+/*
+================
+D_PolysetUpdateTables
+================
+*/
+void D_PolysetUpdateTables (void)
+{
+	int		i;
+	byte	*s;
+	
+	if (r_affinetridesc.skinwidth != skinwidth ||
+		r_affinetridesc.pskin != skinstart)
+	{
+		skinwidth = r_affinetridesc.skinwidth;
+		skinstart = r_affinetridesc.pskin;
+		s = skinstart;
+		for (i=0 ; i<MAX_LBM_HEIGHT ; i++, s+=skinwidth)
+			skintable[i] = s;
+	}
+}
+
+
+#if	!id386
+
+/*
+===================
+D_PolysetScanLeftEdge
+====================
+*/
+void D_PolysetScanLeftEdge (int height)
+{
+
+	do
+	{
+		d_pedgespanpackage->pdest = d_pdest;
+		d_pedgespanpackage->pz = d_pz;
+		d_pedgespanpackage->count = d_aspancount;
+		d_pedgespanpackage->ptex = d_ptex;
+
+		d_pedgespanpackage->sfrac = d_sfrac;
+		d_pedgespanpackage->tfrac = d_tfrac;
+
+	// FIXME: need to clamp l, s, t, at both ends?
+		d_pedgespanpackage->light = d_light;
+		d_pedgespanpackage->zi = d_zi;
+
+		d_pedgespanpackage++;
+
+		errorterm += erroradjustup;
+		if (errorterm >= 0)
+		{
+			d_pdest += d_pdestextrastep;
+			d_pz += d_pzextrastep;
+			d_aspancount += d_countextrastep;
+			d_ptex += d_ptexextrastep;
+			d_sfrac += d_sfracextrastep;
+			d_ptex += d_sfrac >> 16;
+
+			d_sfrac &= 0xFFFF;
+			d_tfrac += d_tfracextrastep;
+			if (d_tfrac & 0x10000)
+			{
+				d_ptex += r_affinetridesc.skinwidth;
+				d_tfrac &= 0xFFFF;
+			}
+			d_light += d_lightextrastep;
+			d_zi += d_ziextrastep;
+			errorterm -= erroradjustdown;
+		}
+		else
+		{
+			d_pdest += d_pdestbasestep;
+			d_pz += d_pzbasestep;
+			d_aspancount += ubasestep;
+			d_ptex += d_ptexbasestep;
+			d_sfrac += d_sfracbasestep;
+			d_ptex += d_sfrac >> 16;
+			d_sfrac &= 0xFFFF;
+			d_tfrac += d_tfracbasestep;
+			if (d_tfrac & 0x10000)
+			{
+				d_ptex += r_affinetridesc.skinwidth;
+				d_tfrac &= 0xFFFF;
+			}
+			d_light += d_lightbasestep;
+			d_zi += d_zibasestep;
+		}
+	} while (--height);
+}
+
+#endif	// !id386
+
+
+/*
+===================
+D_PolysetSetUpForLineScan
+====================
+*/
+void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv,
+		fixed8_t endvertu, fixed8_t endvertv)
+{
+	double		dm, dn;
+	int			tm, tn;
+	adivtab_t	*ptemp;
+
+// TODO: implement x86 version
+
+	errorterm = -1;
+
+	tm = endvertu - startvertu;
+	tn = endvertv - startvertv;
+
+	if (((tm <= 16) && (tm >= -15)) &&
+		((tn <= 16) && (tn >= -15)))
+	{
+		ptemp = &adivtab[((tm+15) << 5) + (tn+15)];
+		ubasestep = ptemp->quotient;
+		erroradjustup = ptemp->remainder;
+		erroradjustdown = tn;
+	}
+	else
+	{
+		dm = (double)tm;
+		dn = (double)tn;
+
+		FloorDivMod (dm, dn, &ubasestep, &erroradjustup);
+
+		erroradjustdown = dn;
+	}
+}
+
+
+#if	!id386
+
+/*
+================
+D_PolysetCalcGradients
+================
+*/
+void D_PolysetCalcGradients (int skinwidth)
+{
+	float	xstepdenominv, ystepdenominv, t0, t1;
+	float	p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20;
+
+	p00_minus_p20 = r_p0[0] - r_p2[0];
+	p01_minus_p21 = r_p0[1] - r_p2[1];
+	p10_minus_p20 = r_p1[0] - r_p2[0];
+	p11_minus_p21 = r_p1[1] - r_p2[1];
+
+	xstepdenominv = 1.0 / (float)d_xdenom;
+
+	ystepdenominv = -xstepdenominv;
+
+// ceil () for light so positive steps are exaggerated, negative steps
+// diminished,  pushing us away from underflow toward overflow. Underflow is
+// very visible, overflow is very unlikely, because of ambient lighting
+	t0 = r_p0[4] - r_p2[4];
+	t1 = r_p1[4] - r_p2[4];
+	r_lstepx = (int)
+			ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv);
+	r_lstepy = (int)
+			ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv);
+
+	t0 = r_p0[2] - r_p2[2];
+	t1 = r_p1[2] - r_p2[2];
+	r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) *
+			xstepdenominv);
+	r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) *
+			ystepdenominv);
+
+	t0 = r_p0[3] - r_p2[3];
+	t1 = r_p1[3] - r_p2[3];
+	r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) *
+			xstepdenominv);
+	r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) *
+			ystepdenominv);
+
+	t0 = r_p0[5] - r_p2[5];
+	t1 = r_p1[5] - r_p2[5];
+	r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) *
+			xstepdenominv);
+	r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) *
+			ystepdenominv);
+
+#if	id386
+	a_sstepxfrac = r_sstepx << 16;
+	a_tstepxfrac = r_tstepx << 16;
+#else
+	a_sstepxfrac = r_sstepx & 0xFFFF;
+	a_tstepxfrac = r_tstepx & 0xFFFF;
+#endif
+
+	a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16);
+}
+
+#endif	// !id386
+
+
+#if 0
+byte gelmap[256];
+void InitGel (byte *palette)
+{
+	int		i;
+	int		r;
+
+	for (i=0 ; i<256 ; i++)
+	{
+//		r = (palette[i*3]>>4);
+		r = (palette[i*3] + palette[i*3+1] + palette[i*3+2])/(16*3);
+		gelmap[i] = /* 64 */ 0 + r;
+	}
+}
+#endif
+
+
+#if	!id386
+
+/*
+================
+D_PolysetDrawSpans8
+================
+*/
+void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage)
+{
+	int		lcount;
+	byte	*lpdest;
+	byte	*lptex;
+	int		lsfrac, ltfrac;
+	int		llight;
+	int		lzi;
+	short	*lpz;
+
+	do
+	{
+		lcount = d_aspancount - pspanpackage->count;
+
+		errorterm += erroradjustup;
+		if (errorterm >= 0)
+		{
+			d_aspancount += d_countextrastep;
+			errorterm -= erroradjustdown;
+		}
+		else
+		{
+			d_aspancount += ubasestep;
+		}
+
+		if (lcount)
+		{
+			lpdest = pspanpackage->pdest;
+			lptex = pspanpackage->ptex;
+			lpz = pspanpackage->pz;
+			lsfrac = pspanpackage->sfrac;
+			ltfrac = pspanpackage->tfrac;
+			llight = pspanpackage->light;
+			lzi = pspanpackage->zi;
+
+			do
+			{
+				if ((lzi >> 16) >= *lpz)
+				{
+					*lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)];
+// gel mapping					*lpdest = gelmap[*lpdest];
+					*lpz = lzi >> 16;
+				}
+				lpdest++;
+				lzi += r_zistepx;
+				lpz++;
+				llight += r_lstepx;
+				lptex += a_ststepxwhole;
+				lsfrac += a_sstepxfrac;
+				lptex += lsfrac >> 16;
+				lsfrac &= 0xFFFF;
+				ltfrac += a_tstepxfrac;
+				if (ltfrac & 0x10000)
+				{
+					lptex += r_affinetridesc.skinwidth;
+					ltfrac &= 0xFFFF;
+				}
+			} while (--lcount);
+		}
+
+		pspanpackage++;
+	} while (pspanpackage->count != -999999);
+}
+#endif	// !id386
+
+
+/*
+================
+D_PolysetFillSpans8
+================
+*/
+void D_PolysetFillSpans8 (spanpackage_t *pspanpackage)
+{
+	int				color;
+
+// FIXME: do z buffering
+
+	color = d_aflatcolor++;
+
+	while (1)
+	{
+		int		lcount;
+		byte	*lpdest;
+
+		lcount = pspanpackage->count;
+
+		if (lcount == -1)
+			return;
+
+		if (lcount)
+		{
+			lpdest = pspanpackage->pdest;
+
+			do
+			{
+				*lpdest++ = color;
+			} while (--lcount);
+		}
+
+		pspanpackage++;
+	}
+}
+
+/*
+================
+D_RasterizeAliasPolySmooth
+================
+*/
+void D_RasterizeAliasPolySmooth (void)
+{
+	int				initialleftheight, initialrightheight;
+	int				*plefttop, *prighttop, *pleftbottom, *prightbottom;
+	int				working_lstepx, originalcount;
+
+	plefttop = pedgetable->pleftedgevert0;
+	prighttop = pedgetable->prightedgevert0;
+
+	pleftbottom = pedgetable->pleftedgevert1;
+	prightbottom = pedgetable->prightedgevert1;
+
+	initialleftheight = pleftbottom[1] - plefttop[1];
+	initialrightheight = prightbottom[1] - prighttop[1];
+
+//
+// set the s, t, and light gradients, which are consistent across the triangle
+// because being a triangle, things are affine
+//
+	D_PolysetCalcGradients (r_affinetridesc.skinwidth);
+
+//
+// rasterize the polygon
+//
+
+//
+// scan out the top (and possibly only) part of the left edge
+//
+	d_pedgespanpackage = a_spans;
+
+	ystart = plefttop[1];
+	d_aspancount = plefttop[0] - prighttop[0];
+
+	d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) +
+			(plefttop[3] >> 16) * r_affinetridesc.skinwidth;
+#if	id386
+	d_sfrac = (plefttop[2] & 0xFFFF) << 16;
+	d_tfrac = (plefttop[3] & 0xFFFF) << 16;
+#else
+	d_sfrac = plefttop[2] & 0xFFFF;
+	d_tfrac = plefttop[3] & 0xFFFF;
+#endif
+	d_light = plefttop[4];
+	d_zi = plefttop[5];
+
+	d_pdest = (byte *)d_viewbuffer +
+			ystart * screenwidth + plefttop[0];
+	d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0];
+
+	if (initialleftheight == 1)
+	{
+		d_pedgespanpackage->pdest = d_pdest;
+		d_pedgespanpackage->pz = d_pz;
+		d_pedgespanpackage->count = d_aspancount;
+		d_pedgespanpackage->ptex = d_ptex;
+
+		d_pedgespanpackage->sfrac = d_sfrac;
+		d_pedgespanpackage->tfrac = d_tfrac;
+
+	// FIXME: need to clamp l, s, t, at both ends?
+		d_pedgespanpackage->light = d_light;
+		d_pedgespanpackage->zi = d_zi;
+
+		d_pedgespanpackage++;
+	}
+	else
+	{
+		D_PolysetSetUpForLineScan(plefttop[0], plefttop[1],
+							  pleftbottom[0], pleftbottom[1]);
+
+	#if	id386
+		d_pzbasestep = (d_zwidth + ubasestep) << 1;
+		d_pzextrastep = d_pzbasestep + 2;
+	#else
+		d_pzbasestep = d_zwidth + ubasestep;
+		d_pzextrastep = d_pzbasestep + 1;
+	#endif
+
+		d_pdestbasestep = screenwidth + ubasestep;
+		d_pdestextrastep = d_pdestbasestep + 1;
+
+	// TODO: can reuse partial expressions here
+
+	// for negative steps in x along left edge, bias toward overflow rather than
+	// underflow (sort of turning the floor () we did in the gradient calcs into
+	// ceil (), but plus a little bit)
+		if (ubasestep < 0)
+			working_lstepx = r_lstepx - 1;
+		else
+			working_lstepx = r_lstepx;
+
+		d_countextrastep = ubasestep + 1;
+		d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) +
+				((r_tstepy + r_tstepx * ubasestep) >> 16) *
+				r_affinetridesc.skinwidth;
+	#if	id386
+		d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16;
+		d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16;
+	#else
+		d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF;
+		d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF;
+	#endif
+		d_lightbasestep = r_lstepy + working_lstepx * ubasestep;
+		d_zibasestep = r_zistepy + r_zistepx * ubasestep;
+
+		d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) +
+				((r_tstepy + r_tstepx * d_countextrastep) >> 16) *
+				r_affinetridesc.skinwidth;
+	#if	id386
+		d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) << 16;
+		d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) << 16;
+	#else
+		d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF;
+		d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF;
+	#endif
+		d_lightextrastep = d_lightbasestep + working_lstepx;
+		d_ziextrastep = d_zibasestep + r_zistepx;
+
+		D_PolysetScanLeftEdge (initialleftheight);
+	}
+
+//
+// scan out the bottom part of the left edge, if it exists
+//
+	if (pedgetable->numleftedges == 2)
+	{
+		int		height;
+
+		plefttop = pleftbottom;
+		pleftbottom = pedgetable->pleftedgevert2;
+
+		height = pleftbottom[1] - plefttop[1];
+
+// TODO: make this a function; modularize this function in general
+
+		ystart = plefttop[1];
+		d_aspancount = plefttop[0] - prighttop[0];
+		d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) +
+				(plefttop[3] >> 16) * r_affinetridesc.skinwidth;
+		d_sfrac = 0;
+		d_tfrac = 0;
+		d_light = plefttop[4];
+		d_zi = plefttop[5];
+
+		d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0];
+		d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0];
+
+		if (height == 1)
+		{
+			d_pedgespanpackage->pdest = d_pdest;
+			d_pedgespanpackage->pz = d_pz;
+			d_pedgespanpackage->count = d_aspancount;
+			d_pedgespanpackage->ptex = d_ptex;
+
+			d_pedgespanpackage->sfrac = d_sfrac;
+			d_pedgespanpackage->tfrac = d_tfrac;
+
+		// FIXME: need to clamp l, s, t, at both ends?
+			d_pedgespanpackage->light = d_light;
+			d_pedgespanpackage->zi = d_zi;
+
+			d_pedgespanpackage++;
+		}
+		else
+		{
+			D_PolysetSetUpForLineScan(plefttop[0], plefttop[1],
+								  pleftbottom[0], pleftbottom[1]);
+
+			d_pdestbasestep = screenwidth + ubasestep;
+			d_pdestextrastep = d_pdestbasestep + 1;
+
+	#if	id386
+			d_pzbasestep = (d_zwidth + ubasestep) << 1;
+			d_pzextrastep = d_pzbasestep + 2;
+	#else
+			d_pzbasestep = d_zwidth + ubasestep;
+			d_pzextrastep = d_pzbasestep + 1;
+	#endif
+
+			if (ubasestep < 0)
+				working_lstepx = r_lstepx - 1;
+			else
+				working_lstepx = r_lstepx;
+
+			d_countextrastep = ubasestep + 1;
+			d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) +
+					((r_tstepy + r_tstepx * ubasestep) >> 16) *
+					r_affinetridesc.skinwidth;
+	#if	id386
+			d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16;
+			d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16;
+	#else
+			d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF;
+			d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF;
+	#endif
+			d_lightbasestep = r_lstepy + working_lstepx * ubasestep;
+			d_zibasestep = r_zistepy + r_zistepx * ubasestep;
+
+			d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) +
+					((r_tstepy + r_tstepx * d_countextrastep) >> 16) *
+					r_affinetridesc.skinwidth;
+	#if	id386
+			d_sfracextrastep = ((r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF)<<16;
+			d_tfracextrastep = ((r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF)<<16;
+	#else
+			d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF;
+			d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF;
+	#endif
+			d_lightextrastep = d_lightbasestep + working_lstepx;
+			d_ziextrastep = d_zibasestep + r_zistepx;
+
+			D_PolysetScanLeftEdge (height);
+		}
+	}
+
+// scan out the top (and possibly only) part of the right edge, updating the
+// count field
+	d_pedgespanpackage = a_spans;
+
+	D_PolysetSetUpForLineScan(prighttop[0], prighttop[1],
+						  prightbottom[0], prightbottom[1]);
+	d_aspancount = 0;
+	d_countextrastep = ubasestep + 1;
+	originalcount = a_spans[initialrightheight].count;
+	a_spans[initialrightheight].count = -999999; // mark end of the spanpackages
+	D_PolysetDrawSpans8 (a_spans);
+
+// scan out the bottom part of the right edge, if it exists
+	if (pedgetable->numrightedges == 2)
+	{
+		int				height;
+		spanpackage_t	*pstart;
+
+		pstart = a_spans + initialrightheight;
+		pstart->count = originalcount;
+
+		d_aspancount = prightbottom[0] - prighttop[0];
+
+		prighttop = prightbottom;
+		prightbottom = pedgetable->prightedgevert2;
+
+		height = prightbottom[1] - prighttop[1];
+
+		D_PolysetSetUpForLineScan(prighttop[0], prighttop[1],
+							  prightbottom[0], prightbottom[1]);
+
+		d_countextrastep = ubasestep + 1;
+		a_spans[initialrightheight + height].count = -999999;
+											// mark end of the spanpackages
+		D_PolysetDrawSpans8 (pstart);
+	}
+}
+
+
+/*
+================
+D_PolysetSetEdgeTable
+================
+*/
+void D_PolysetSetEdgeTable (void)
+{
+	int			edgetableindex;
+
+	edgetableindex = 0;	// assume the vertices are already in
+						//  top to bottom order
+
+//
+// determine which edges are right & left, and the order in which
+// to rasterize them
+//
+	if (r_p0[1] >= r_p1[1])
+	{
+		if (r_p0[1] == r_p1[1])
+		{
+			if (r_p0[1] < r_p2[1])
+				pedgetable = &edgetables[2];
+			else
+				pedgetable = &edgetables[5];
+
+			return;
+		}
+		else
+		{
+			edgetableindex = 1;
+		}
+	}
+
+	if (r_p0[1] == r_p2[1])
+	{
+		if (edgetableindex)
+			pedgetable = &edgetables[8];
+		else
+			pedgetable = &edgetables[9];
+
+		return;
+	}
+	else if (r_p1[1] == r_p2[1])
+	{
+		if (edgetableindex)
+			pedgetable = &edgetables[10];
+		else
+			pedgetable = &edgetables[11];
+
+		return;
+	}
+
+	if (r_p0[1] > r_p2[1])
+		edgetableindex += 2;
+
+	if (r_p1[1] > r_p2[1])
+		edgetableindex += 4;
+
+	pedgetable = &edgetables[edgetableindex];
+}
+
+
+#if 0
+
+void D_PolysetRecursiveDrawLine (int *lp1, int *lp2)
+{
+	int		d;
+	int		new[6];
+	int 	ofs;
+	
+	d = lp2[0] - lp1[0];
+	if (d < -1 || d > 1)
+		goto split;
+	d = lp2[1] - lp1[1];
+	if (d < -1 || d > 1)
+		goto split;
+
+	return;	// line is completed
+
+split:
+// split this edge
+	new[0] = (lp1[0] + lp2[0]) >> 1;
+	new[1] = (lp1[1] + lp2[1]) >> 1;
+	new[5] = (lp1[5] + lp2[5]) >> 1;
+	new[2] = (lp1[2] + lp2[2]) >> 1;
+	new[3] = (lp1[3] + lp2[3]) >> 1;
+	new[4] = (lp1[4] + lp2[4]) >> 1;
+
+// draw the point
+	ofs = d_scantable[new[1]] + new[0];
+	if (new[5] > d_pzbuffer[ofs])
+	{
+		int		pix;
+		
+		d_pzbuffer[ofs] = new[5];
+		pix = skintable[new[3]>>16][new[2]>>16];
+//		pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)];
+		d_viewbuffer[ofs] = pix;
+	}
+
+// recursively continue
+	D_PolysetRecursiveDrawLine (lp1, new);
+	D_PolysetRecursiveDrawLine (new, lp2);
+}
+
+void D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3)
+{
+	int		d;
+	int		new[4];
+	
+	d = lp2[0] - lp1[0];
+	if (d < -1 || d > 1)
+		goto split;
+	d = lp2[1] - lp1[1];
+	if (d < -1 || d > 1)
+		goto split;
+	return;
+
+split:
+// split this edge
+	new[0] = (lp1[0] + lp2[0]) >> 1;
+	new[1] = (lp1[1] + lp2[1]) >> 1;
+	new[5] = (lp1[5] + lp2[5]) >> 1;
+	new[2] = (lp1[2] + lp2[2]) >> 1;
+	new[3] = (lp1[3] + lp2[3]) >> 1;
+	new[4] = (lp1[4] + lp2[4]) >> 1;
+
+	D_PolysetRecursiveDrawLine (new, lp3);
+
+// recursively continue
+	D_PolysetRecursiveTriangle (lp1, new, lp3);
+	D_PolysetRecursiveTriangle (new, lp2, lp3);
+}
+
+#endif
+
diff --git a/apps/plugins/sdl/progs/quake/d_scan.c b/apps/plugins/sdl/progs/quake/d_scan.c
new file mode 100644
index 0000000..008c783
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_scan.c
@@ -0,0 +1,449 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_scan.c
+//
+// Portable C scan-level rasterization code, all pixel depths.
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+unsigned char	*r_turb_pbase, *r_turb_pdest;
+fixed16_t		r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep;
+int				*r_turb_turb;
+int				r_turb_spancount;
+
+void D_DrawTurbulent8Span (void);
+
+
+/*
+=============
+D_WarpScreen
+
+// this performs a slight compression of the screen at the same time as
+// the sine warp, to keep the edges from wrapping
+=============
+*/
+void D_WarpScreen (void)
+{
+	int		w, h;
+	int		u,v;
+	byte	*dest;
+	int		*turb;
+	int		*col;
+	byte	**row;
+	byte	*rowptr[MAXHEIGHT+(AMP2*2)];
+	int		column[MAXWIDTH+(AMP2*2)];
+	float	wratio, hratio;
+
+	w = r_refdef.vrect.width;
+	h = r_refdef.vrect.height;
+
+	wratio = w / (float)scr_vrect.width;
+	hratio = h / (float)scr_vrect.height;
+
+	for (v=0 ; v<scr_vrect.height+AMP2*2 ; v++)
+	{
+		rowptr[v] = d_viewbuffer + (r_refdef.vrect.y * screenwidth) +
+				 (screenwidth * (int)((float)v * hratio * h / (h + AMP2 * 2)));
+	}
+
+	for (u=0 ; u<scr_vrect.width+AMP2*2 ; u++)
+	{
+		column[u] = r_refdef.vrect.x +
+				(int)((float)u * wratio * w / (w + AMP2 * 2));
+	}
+
+	turb = intsintable + ((int)(cl.time*SPEED)&(CYCLE-1));
+	dest = vid.buffer + scr_vrect.y * vid.rowbytes + scr_vrect.x;
+
+	for (v=0 ; v<scr_vrect.height ; v++, dest += vid.rowbytes)
+	{
+		col = &column[turb[v]];
+		row = &rowptr[v];
+
+		for (u=0 ; u<scr_vrect.width ; u+=4)
+		{
+			dest[u+0] = row[turb[u+0]][col[u+0]];
+			dest[u+1] = row[turb[u+1]][col[u+1]];
+			dest[u+2] = row[turb[u+2]][col[u+2]];
+			dest[u+3] = row[turb[u+3]][col[u+3]];
+		}
+	}
+}
+
+
+#if	!id386
+
+/*
+=============
+D_DrawTurbulent8Span
+=============
+*/
+void D_DrawTurbulent8Span (void)
+{
+	int		sturb, tturb;
+
+	do
+	{
+		sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63;
+		tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63;
+		*r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb);
+		r_turb_s += r_turb_sstep;
+		r_turb_t += r_turb_tstep;
+	} while (--r_turb_spancount > 0);
+}
+
+#endif	// !id386
+
+
+/*
+=============
+Turbulent8
+=============
+*/
+void Turbulent8 (espan_t *pspan)
+{
+	int				count;
+	fixed16_t		snext, tnext;
+	float			sdivz, tdivz, zi, z, du, dv, spancountminus1;
+	float			sdivz16stepu, tdivz16stepu, zi16stepu;
+	
+	r_turb_turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1));
+
+	r_turb_sstep = 0;	// keep compiler happy
+	r_turb_tstep = 0;	// ditto
+
+	r_turb_pbase = (unsigned char *)cacheblock;
+
+	sdivz16stepu = d_sdivzstepu * 16;
+	tdivz16stepu = d_tdivzstepu * 16;
+	zi16stepu = d_zistepu * 16;
+
+	do
+	{
+		r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer +
+				(screenwidth * pspan->v) + pspan->u);
+
+		count = pspan->count;
+
+	// calculate the initial s/z, t/z, 1/z, s, and t and clamp
+		du = (float)pspan->u;
+		dv = (float)pspan->v;
+
+		sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu;
+		tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu;
+		zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
+		z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+
+		r_turb_s = (int)(sdivz * z) + sadjust;
+		if (r_turb_s > bbextents)
+			r_turb_s = bbextents;
+		else if (r_turb_s < 0)
+			r_turb_s = 0;
+
+		r_turb_t = (int)(tdivz * z) + tadjust;
+		if (r_turb_t > bbextentt)
+			r_turb_t = bbextentt;
+		else if (r_turb_t < 0)
+			r_turb_t = 0;
+
+		do
+		{
+		// calculate s and t at the far end of the span
+			if (count >= 16)
+				r_turb_spancount = 16;
+			else
+				r_turb_spancount = count;
+
+			count -= r_turb_spancount;
+
+			if (count)
+			{
+			// calculate s/z, t/z, zi->fixed s and t at far end of span,
+			// calculate s and t steps across span by shifting
+				sdivz += sdivz16stepu;
+				tdivz += tdivz16stepu;
+				zi += zi16stepu;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 16)
+					snext = 16;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 16)
+					tnext = 16;	// guard against round-off error on <0 steps
+
+				r_turb_sstep = (snext - r_turb_s) >> 4;
+				r_turb_tstep = (tnext - r_turb_t) >> 4;
+			}
+			else
+			{
+			// calculate s/z, t/z, zi->fixed s and t at last pixel in span (so
+			// can't step off polygon), clamp, calculate s and t steps across
+			// span by division, biasing steps low so we don't run off the
+			// texture
+				spancountminus1 = (float)(r_turb_spancount - 1);
+				sdivz += d_sdivzstepu * spancountminus1;
+				tdivz += d_tdivzstepu * spancountminus1;
+				zi += d_zistepu * spancountminus1;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 16)
+					snext = 16;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 16)
+					tnext = 16;	// guard against round-off error on <0 steps
+
+				if (r_turb_spancount > 1)
+				{
+					r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1);
+					r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1);
+				}
+			}
+
+			r_turb_s = r_turb_s & ((CYCLE<<16)-1);
+			r_turb_t = r_turb_t & ((CYCLE<<16)-1);
+
+			D_DrawTurbulent8Span ();
+
+			r_turb_s = snext;
+			r_turb_t = tnext;
+
+		} while (count > 0);
+
+	} while ((pspan = pspan->pnext) != NULL);
+}
+
+
+#if	!id386
+
+/*
+=============
+D_DrawSpans8
+=============
+*/
+void D_DrawSpans8 (espan_t *pspan)
+{
+	int				count, spancount;
+	unsigned char	*pbase, *pdest;
+	fixed16_t		s, t, snext, tnext, sstep, tstep;
+	float			sdivz, tdivz, zi, z, du, dv, spancountminus1;
+	float			sdivz8stepu, tdivz8stepu, zi8stepu;
+
+	sstep = 0;	// keep compiler happy
+	tstep = 0;	// ditto
+
+	pbase = (unsigned char *)cacheblock;
+
+	sdivz8stepu = d_sdivzstepu * 8;
+	tdivz8stepu = d_tdivzstepu * 8;
+	zi8stepu = d_zistepu * 8;
+
+	do
+	{
+		pdest = (unsigned char *)((byte *)d_viewbuffer +
+				(screenwidth * pspan->v) + pspan->u);
+
+		count = pspan->count;
+
+	// calculate the initial s/z, t/z, 1/z, s, and t and clamp
+		du = (float)pspan->u;
+		dv = (float)pspan->v;
+
+		sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu;
+		tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu;
+		zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
+		z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+
+		s = (int)(sdivz * z) + sadjust;
+		if (s > bbextents)
+			s = bbextents;
+		else if (s < 0)
+			s = 0;
+
+		t = (int)(tdivz * z) + tadjust;
+		if (t > bbextentt)
+			t = bbextentt;
+		else if (t < 0)
+			t = 0;
+
+		do
+		{
+		// calculate s and t at the far end of the span
+			if (count >= 8)
+				spancount = 8;
+			else
+				spancount = count;
+
+			count -= spancount;
+
+			if (count)
+			{
+			// calculate s/z, t/z, zi->fixed s and t at far end of span,
+			// calculate s and t steps across span by shifting
+				sdivz += sdivz8stepu;
+				tdivz += tdivz8stepu;
+				zi += zi8stepu;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 8)
+					snext = 8;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 8)
+					tnext = 8;	// guard against round-off error on <0 steps
+
+				sstep = (snext - s) >> 3;
+				tstep = (tnext - t) >> 3;
+			}
+			else
+			{
+			// calculate s/z, t/z, zi->fixed s and t at last pixel in span (so
+			// can't step off polygon), clamp, calculate s and t steps across
+			// span by division, biasing steps low so we don't run off the
+			// texture
+				spancountminus1 = (float)(spancount - 1);
+				sdivz += d_sdivzstepu * spancountminus1;
+				tdivz += d_tdivzstepu * spancountminus1;
+				zi += d_zistepu * spancountminus1;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 8)
+					snext = 8;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 8)
+					tnext = 8;	// guard against round-off error on <0 steps
+
+				if (spancount > 1)
+				{
+					sstep = (snext - s) / (spancount - 1);
+					tstep = (tnext - t) / (spancount - 1);
+				}
+			}
+
+			do
+			{
+				*pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth);
+				s += sstep;
+				t += tstep;
+			} while (--spancount > 0);
+
+			s = snext;
+			t = tnext;
+
+		} while (count > 0);
+
+	} while ((pspan = pspan->pnext) != NULL);
+}
+
+#endif
+
+
+#if	!id386
+
+/*
+=============
+D_DrawZSpans
+=============
+*/
+void D_DrawZSpans (espan_t *pspan)
+{
+	int				count, doublecount, izistep;
+	int				izi;
+	short			*pdest;
+	unsigned		ltemp;
+	double			zi;
+	float			du, dv;
+
+// FIXME: check for clamping/range problems
+// we count on FP exceptions being turned off to avoid range problems
+	izistep = (int)(d_zistepu * 0x8000 * 0x10000);
+
+	do
+	{
+		pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u;
+
+		count = pspan->count;
+
+	// calculate the initial 1/z
+		du = (float)pspan->u;
+		dv = (float)pspan->v;
+
+		zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
+	// we count on FP exceptions being turned off to avoid range problems
+		izi = (int)(zi * 0x8000 * 0x10000);
+
+		if ((long)pdest & 0x02)
+		{
+			*pdest++ = (short)(izi >> 16);
+			izi += izistep;
+			count--;
+		}
+
+		if ((doublecount = count >> 1) > 0)
+		{
+			do
+			{
+				ltemp = izi >> 16;
+				izi += izistep;
+				ltemp |= izi & 0xFFFF0000;
+				izi += izistep;
+				*(int *)pdest = ltemp;
+				pdest += 2;
+			} while (--doublecount > 0);
+		}
+
+		if (count & 1)
+			*pdest = (short)(izi >> 16);
+
+	} while ((pspan = pspan->pnext) != NULL);
+}
+
+#endif
+
diff --git a/apps/plugins/sdl/progs/quake/d_sky.c b/apps/plugins/sdl/progs/quake/d_sky.c
new file mode 100644
index 0000000..9e28a8e
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_sky.c
@@ -0,0 +1,138 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_sky.c
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+#define SKY_SPAN_SHIFT	5
+#define SKY_SPAN_MAX	(1 << SKY_SPAN_SHIFT)
+
+
+/*
+=================
+D_Sky_uv_To_st
+=================
+*/
+void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t)
+{
+	float	wu, wv, temp;
+	vec3_t	end;
+
+	if (r_refdef.vrect.width >= r_refdef.vrect.height)
+		temp = (float)r_refdef.vrect.width;
+	else
+		temp = (float)r_refdef.vrect.height;
+
+	wu = 8192.0 * (float)(u-((int)vid.width>>1)) / temp;
+	wv = 8192.0 * (float)(((int)vid.height>>1)-v) / temp;
+
+	end[0] = 4096*vpn[0] + wu*vright[0] + wv*vup[0];
+	end[1] = 4096*vpn[1] + wu*vright[1] + wv*vup[1];
+	end[2] = 4096*vpn[2] + wu*vright[2] + wv*vup[2];
+	end[2] *= 3;
+	VectorNormalizeNoRet (end);
+
+	temp = skytime*skyspeed;	// TODO: add D_SetupFrame & set this there
+	*s = (int)((temp + 6*(SKYSIZE/2-1)*end[0]) * 0x10000);
+	*t = (int)((temp + 6*(SKYSIZE/2-1)*end[1]) * 0x10000);
+}
+
+
+/*
+=================
+D_DrawSkyScans8
+=================
+*/
+void D_DrawSkyScans8 (espan_t *pspan)
+{
+	int				count, spancount, u, v;
+	unsigned char	*pdest;
+	fixed16_t		s, t, snext, tnext, sstep, tstep;
+	int				spancountminus1;
+
+	sstep = 0;	// keep compiler happy
+	tstep = 0;	// ditto
+
+	do
+	{
+		pdest = (unsigned char *)((byte *)d_viewbuffer +
+				(screenwidth * pspan->v) + pspan->u);
+
+		count = pspan->count;
+
+	// calculate the initial s & t
+		u = pspan->u;
+		v = pspan->v;
+		D_Sky_uv_To_st (u, v, &s, &t);
+
+		do
+		{
+			if (count >= SKY_SPAN_MAX)
+				spancount = SKY_SPAN_MAX;
+			else
+				spancount = count;
+
+			count -= spancount;
+
+			if (count)
+			{
+				u += spancount;
+
+			// calculate s and t at far end of span,
+			// calculate s and t steps across span by shifting
+				D_Sky_uv_To_st (u, v, &snext, &tnext);
+
+				sstep = (snext - s) >> SKY_SPAN_SHIFT;
+				tstep = (tnext - t) >> SKY_SPAN_SHIFT;
+			}
+			else
+			{
+			// calculate s and t at last pixel in span,
+			// calculate s and t steps across span by division
+				spancountminus1 = (float)(spancount - 1);
+
+				if (spancountminus1 > 0)
+				{
+					u += spancountminus1;
+					D_Sky_uv_To_st (u, v, &snext, &tnext);
+
+					sstep = (snext - s) / spancountminus1;
+					tstep = (tnext - t) / spancountminus1;
+				}
+			}
+
+			do
+			{
+				*pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) +
+						((s & R_SKY_SMASK) >> 16)];
+				s += sstep;
+				t += tstep;
+			} while (--spancount > 0);
+
+			s = snext;
+			t = tnext;
+
+		} while (count > 0);
+
+	} while ((pspan = pspan->pnext) != NULL);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_sprite.c b/apps/plugins/sdl/progs/quake/d_sprite.c
new file mode 100644
index 0000000..2f02ad2
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_sprite.c
@@ -0,0 +1,442 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_sprite.c: software top-level rasterization driver module for drawing
+// sprites
+
+#include "quakedef.h"
+#include "d_local.h"
+
+static int		sprite_height;
+static int		minindex, maxindex;
+static sspan_t	*sprite_spans;
+
+#if	!id386
+
+/*
+=====================
+D_SpriteDrawSpans
+=====================
+*/
+void D_SpriteDrawSpans (sspan_t *pspan)
+{
+	int			count, spancount, izistep;
+	int			izi;
+	byte		*pbase, *pdest;
+	fixed16_t	s, t, snext, tnext, sstep, tstep;
+	float		sdivz, tdivz, zi, z, du, dv, spancountminus1;
+	float		sdivz8stepu, tdivz8stepu, zi8stepu;
+	byte		btemp;
+	short		*pz;
+
+	sstep = 0;	// keep compiler happy
+	tstep = 0;	// ditto
+
+	pbase = cacheblock;
+
+	sdivz8stepu = d_sdivzstepu * 8;
+	tdivz8stepu = d_tdivzstepu * 8;
+	zi8stepu = d_zistepu * 8;
+
+// we count on FP exceptions being turned off to avoid range problems
+	izistep = (int)(d_zistepu * 0x8000 * 0x10000);
+
+	do
+	{
+		pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u;
+		pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u;
+
+		count = pspan->count;
+
+		if (count <= 0)
+			goto NextSpan;
+
+	// calculate the initial s/z, t/z, 1/z, s, and t and clamp
+		du = (float)pspan->u;
+		dv = (float)pspan->v;
+
+		sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu;
+		tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu;
+		zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
+		z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+	// we count on FP exceptions being turned off to avoid range problems
+		izi = (int)(zi * 0x8000 * 0x10000);
+
+		s = (int)(sdivz * z) + sadjust;
+		if (s > bbextents)
+			s = bbextents;
+		else if (s < 0)
+			s = 0;
+
+		t = (int)(tdivz * z) + tadjust;
+		if (t > bbextentt)
+			t = bbextentt;
+		else if (t < 0)
+			t = 0;
+
+		do
+		{
+		// calculate s and t at the far end of the span
+			if (count >= 8)
+				spancount = 8;
+			else
+				spancount = count;
+
+			count -= spancount;
+
+			if (count)
+			{
+			// calculate s/z, t/z, zi->fixed s and t at far end of span,
+			// calculate s and t steps across span by shifting
+				sdivz += sdivz8stepu;
+				tdivz += tdivz8stepu;
+				zi += zi8stepu;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 8)
+					snext = 8;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 8)
+					tnext = 8;	// guard against round-off error on <0 steps
+
+				sstep = (snext - s) >> 3;
+				tstep = (tnext - t) >> 3;
+			}
+			else
+			{
+			// calculate s/z, t/z, zi->fixed s and t at last pixel in span (so
+			// can't step off polygon), clamp, calculate s and t steps across
+			// span by division, biasing steps low so we don't run off the
+			// texture
+				spancountminus1 = (float)(spancount - 1);
+				sdivz += d_sdivzstepu * spancountminus1;
+				tdivz += d_tdivzstepu * spancountminus1;
+				zi += d_zistepu * spancountminus1;
+				z = (float)0x10000 / zi;	// prescale to 16.16 fixed-point
+				snext = (int)(sdivz * z) + sadjust;
+				if (snext > bbextents)
+					snext = bbextents;
+				else if (snext < 8)
+					snext = 8;	// prevent round-off error on <0 steps from
+								//  from causing overstepping & running off the
+								//  edge of the texture
+
+				tnext = (int)(tdivz * z) + tadjust;
+				if (tnext > bbextentt)
+					tnext = bbextentt;
+				else if (tnext < 8)
+					tnext = 8;	// guard against round-off error on <0 steps
+
+				if (spancount > 1)
+				{
+					sstep = (snext - s) / (spancount - 1);
+					tstep = (tnext - t) / (spancount - 1);
+				}
+			}
+
+			do
+			{
+				btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth);
+				if (btemp != 255)
+				{
+					if (*pz <= (izi >> 16))
+					{
+						*pz = izi >> 16;
+						*pdest = btemp;
+					}
+				}
+
+				izi += izistep;
+				pdest++;
+				pz++;
+				s += sstep;
+				t += tstep;
+			} while (--spancount > 0);
+
+			s = snext;
+			t = tnext;
+
+		} while (count > 0);
+
+NextSpan:
+		pspan++;
+
+	} while (pspan->count != DS_SPAN_LIST_END);
+}
+
+#endif
+
+
+/*
+=====================
+D_SpriteScanLeftEdge
+=====================
+*/
+void D_SpriteScanLeftEdge (void)
+{
+	int			i, v, itop, ibottom, lmaxindex;
+	emitpoint_t	*pvert, *pnext;
+	sspan_t		*pspan;
+	float		du, dv, vtop, vbottom, slope;
+	fixed16_t	u, u_step;
+
+	pspan = sprite_spans;
+	i = minindex;
+	if (i == 0)
+		i = r_spritedesc.nump;
+
+	lmaxindex = maxindex;
+	if (lmaxindex == 0)
+		lmaxindex = r_spritedesc.nump;
+
+	vtop = ceil (r_spritedesc.pverts[i].v);
+
+	do
+	{
+		pvert = &r_spritedesc.pverts[i];
+		pnext = pvert - 1;
+
+		vbottom = ceil (pnext->v);
+
+		if (vtop < vbottom)
+		{
+			du = pnext->u - pvert->u;
+			dv = pnext->v - pvert->v;
+			slope = du / dv;
+			u_step = (int)(slope * 0x10000);
+		// adjust u to ceil the integer portion
+			u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) +
+					(0x10000 - 1);
+			itop = (int)vtop;
+			ibottom = (int)vbottom;
+
+			for (v=itop ; v<ibottom ; v++)
+			{
+				pspan->u = u >> 16;
+				pspan->v = v;
+				u += u_step;
+				pspan++;
+			}
+		}
+
+		vtop = vbottom;
+
+		i--;
+		if (i == 0)
+			i = r_spritedesc.nump;
+
+	} while (i != lmaxindex);
+}
+
+
+/*
+=====================
+D_SpriteScanRightEdge
+=====================
+*/
+void D_SpriteScanRightEdge (void)
+{
+	int			i, v, itop, ibottom;
+	emitpoint_t	*pvert, *pnext;
+	sspan_t		*pspan;
+	float		du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext;
+	fixed16_t	u, u_step;
+
+	pspan = sprite_spans;
+	i = minindex;
+
+	vvert = r_spritedesc.pverts[i].v;
+	if (vvert < r_refdef.fvrecty_adj)
+		vvert = r_refdef.fvrecty_adj;
+	if (vvert > r_refdef.fvrectbottom_adj)
+		vvert = r_refdef.fvrectbottom_adj;
+
+	vtop = ceil (vvert);
+
+	do
+	{
+		pvert = &r_spritedesc.pverts[i];
+		pnext = pvert + 1;
+
+		vnext = pnext->v;
+		if (vnext < r_refdef.fvrecty_adj)
+			vnext = r_refdef.fvrecty_adj;
+		if (vnext > r_refdef.fvrectbottom_adj)
+			vnext = r_refdef.fvrectbottom_adj;
+
+		vbottom = ceil (vnext);
+
+		if (vtop < vbottom)
+		{
+			uvert = pvert->u;
+			if (uvert < r_refdef.fvrectx_adj)
+				uvert = r_refdef.fvrectx_adj;
+			if (uvert > r_refdef.fvrectright_adj)
+				uvert = r_refdef.fvrectright_adj;
+
+			unext = pnext->u;
+			if (unext < r_refdef.fvrectx_adj)
+				unext = r_refdef.fvrectx_adj;
+			if (unext > r_refdef.fvrectright_adj)
+				unext = r_refdef.fvrectright_adj;
+
+			du = unext - uvert;
+			dv = vnext - vvert;
+			slope = du / dv;
+			u_step = (int)(slope * 0x10000);
+		// adjust u to ceil the integer portion
+			u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) +
+					(0x10000 - 1);
+			itop = (int)vtop;
+			ibottom = (int)vbottom;
+
+			for (v=itop ; v<ibottom ; v++)
+			{
+				pspan->count = (u >> 16) - pspan->u;
+				u += u_step;
+				pspan++;
+			}
+		}
+
+		vtop = vbottom;
+		vvert = vnext;
+
+		i++;
+		if (i == r_spritedesc.nump)
+			i = 0;
+
+	} while (i != maxindex);
+
+	pspan->count = DS_SPAN_LIST_END;	// mark the end of the span list 
+}
+
+
+/*
+=====================
+D_SpriteCalculateGradients
+=====================
+*/
+void D_SpriteCalculateGradients (void)
+{
+	vec3_t		p_normal, p_saxis, p_taxis, p_temp1;
+	float		distinv;
+
+	TransformVector (r_spritedesc.vpn, p_normal);
+	TransformVector (r_spritedesc.vright, p_saxis);
+	TransformVector (r_spritedesc.vup, p_taxis);
+	VectorInverse (p_taxis);
+
+	distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn));
+
+	d_sdivzstepu = p_saxis[0] * xscaleinv;
+	d_tdivzstepu = p_taxis[0] * xscaleinv;
+
+	d_sdivzstepv = -p_saxis[1] * yscaleinv;
+	d_tdivzstepv = -p_taxis[1] * yscaleinv;
+
+	d_zistepu = p_normal[0] * xscaleinv * distinv;
+	d_zistepv = -p_normal[1] * yscaleinv * distinv;
+
+	d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu -
+			ycenter * d_sdivzstepv;
+	d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu -
+			ycenter * d_tdivzstepv;
+	d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu -
+			ycenter * d_zistepv;
+
+	TransformVector (modelorg, p_temp1);
+
+	sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) -
+			(-(cachewidth >> 1) << 16);
+	tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) -
+			(-(sprite_height >> 1) << 16);
+
+// -1 (-epsilon) so we never wander off the edge of the texture
+	bbextents = (cachewidth << 16) - 1;
+	bbextentt = (sprite_height << 16) - 1;
+}
+
+
+/*
+=====================
+D_DrawSprite
+=====================
+*/
+void D_DrawSprite (void)
+{
+	int			i, nump;
+	float		ymin, ymax;
+	emitpoint_t	*pverts;
+	sspan_t		spans[MAXHEIGHT+1];
+
+	sprite_spans = spans;
+
+// find the top and bottom vertices, and make sure there's at least one scan to
+// draw
+	ymin = 999999.9;
+	ymax = -999999.9;
+	pverts = r_spritedesc.pverts;
+
+	for (i=0 ; i<r_spritedesc.nump ; i++)
+	{
+		if (pverts->v < ymin)
+		{
+			ymin = pverts->v;
+			minindex = i;
+		}
+
+		if (pverts->v > ymax)
+		{
+			ymax = pverts->v;
+			maxindex = i;
+		}
+
+		pverts++;
+	}
+
+	ymin = ceil (ymin);
+	ymax = ceil (ymax);
+
+	if (ymin >= ymax)
+		return;		// doesn't cross any scans at all
+
+	cachewidth = r_spritedesc.pspriteframe->width;
+	sprite_height = r_spritedesc.pspriteframe->height;
+	cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0];
+
+// copy the first vertex to the last vertex, so we don't have to deal with
+// wrapping
+	nump = r_spritedesc.nump;
+	pverts = r_spritedesc.pverts;
+	pverts[nump] = pverts[0];
+
+	D_SpriteCalculateGradients ();
+	D_SpriteScanLeftEdge ();
+	D_SpriteScanRightEdge ();
+	D_SpriteDrawSpans (sprite_spans);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/d_surf.c b/apps/plugins/sdl/progs/quake/d_surf.c
new file mode 100644
index 0000000..b09c5f8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_surf.c
@@ -0,0 +1,335 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_surf.c: rasterization driver surface heap manager
+
+#include "quakedef.h"
+#include "d_local.h"
+#include "r_local.h"
+
+float           surfscale;
+qboolean        r_cache_thrash;         // set if surface cache is thrashing
+
+int                                     sc_size;
+surfcache_t                     *sc_rover, *sc_base;
+
+#define GUARDSIZE       4
+
+
+int     D_SurfaceCacheForRes (int width, int height)
+{
+	int             size, pix;
+
+	if (COM_CheckParm ("-surfcachesize"))
+	{
+		size = Q_atoi(com_argv[COM_CheckParm("-surfcachesize")+1]) * 1024;
+		return size;
+	}
+	
+	size = SURFCACHE_SIZE_AT_320X200;
+
+	pix = width*height;
+	if (pix > 64000)
+		size += (pix-64000)*3;
+		
+
+	return size;
+}
+
+void D_CheckCacheGuard (void)
+{
+	byte    *s;
+	int             i;
+
+	s = (byte *)sc_base + sc_size;
+	for (i=0 ; i<GUARDSIZE ; i++)
+		if (s[i] != (byte)i)
+			Sys_Error ("D_CheckCacheGuard: failed");
+}
+
+void D_ClearCacheGuard (void)
+{
+	byte    *s;
+	int             i;
+	
+	s = (byte *)sc_base + sc_size;
+	for (i=0 ; i<GUARDSIZE ; i++)
+		s[i] = (byte)i;
+}
+
+
+/*
+================
+D_InitCaches
+
+================
+*/
+void D_InitCaches (void *buffer, int size)
+{
+
+	if (!msg_suppress_1)
+		Con_Printf ("%ik surface cache\n", size/1024);
+
+	sc_size = size - GUARDSIZE;
+	sc_base = (surfcache_t *)buffer;
+	sc_rover = sc_base;
+	
+	sc_base->next = NULL;
+	sc_base->owner = NULL;
+	sc_base->size = sc_size;
+	
+	D_ClearCacheGuard ();
+}
+
+
+/*
+==================
+D_FlushCaches
+==================
+*/
+void D_FlushCaches (void)
+{
+	surfcache_t     *c;
+	
+	if (!sc_base)
+		return;
+
+	for (c = sc_base ; c ; c = c->next)
+	{
+		if (c->owner)
+			*c->owner = NULL;
+	}
+	
+	sc_rover = sc_base;
+	sc_base->next = NULL;
+	sc_base->owner = NULL;
+	sc_base->size = sc_size;
+}
+
+/*
+=================
+D_SCAlloc
+=================
+*/
+surfcache_t     *D_SCAlloc (int width, int size)
+{
+	surfcache_t             *new;
+	qboolean                wrapped_this_time;
+
+	if ((width < 0) || (width > 256))
+		Sys_Error ("D_SCAlloc: bad cache width %d\n", width);
+
+	if ((size <= 0) || (size > 0x10000))
+		Sys_Error ("D_SCAlloc: bad cache size %d\n", size);
+	
+	size = (int)&((surfcache_t *)0)->data[size];
+	size = (size + 3) & ~3;
+	if (size > sc_size)
+		Sys_Error ("D_SCAlloc: %i > cache size",size);
+
+// if there is not size bytes after the rover, reset to the start
+	wrapped_this_time = false;
+
+	if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size)
+	{
+		if (sc_rover)
+		{
+			wrapped_this_time = true;
+		}
+		sc_rover = sc_base;
+	}
+		
+// colect and free surfcache_t blocks until the rover block is large enough
+	new = sc_rover;
+	if (sc_rover->owner)
+		*sc_rover->owner = NULL;
+	
+	while (new->size < size)
+	{
+	// free another
+		sc_rover = sc_rover->next;
+		if (!sc_rover)
+			Sys_Error ("D_SCAlloc: hit the end of memory");
+		if (sc_rover->owner)
+			*sc_rover->owner = NULL;
+			
+		new->size += sc_rover->size;
+		new->next = sc_rover->next;
+	}
+
+// create a fragment out of any leftovers
+	if (new->size - size > 256)
+	{
+		sc_rover = (surfcache_t *)( (byte *)new + size);
+		sc_rover->size = new->size - size;
+		sc_rover->next = new->next;
+		sc_rover->width = 0;
+		sc_rover->owner = NULL;
+		new->next = sc_rover;
+		new->size = size;
+	}
+	else
+		sc_rover = new->next;
+	
+	new->width = width;
+// DEBUG
+	if (width > 0)
+		new->height = (size - sizeof(*new) + sizeof(new->data)) / width;
+
+	new->owner = NULL;              // should be set properly after return
+
+	if (d_roverwrapped)
+	{
+		if (wrapped_this_time || (sc_rover >= d_initial_rover))
+			r_cache_thrash = true;
+	}
+	else if (wrapped_this_time)
+	{       
+		d_roverwrapped = true;
+	}
+
+D_CheckCacheGuard ();   // DEBUG
+	return new;
+}
+
+
+/*
+=================
+D_SCDump
+=================
+*/
+void D_SCDump (void)
+{
+	surfcache_t             *test;
+
+	for (test = sc_base ; test ; test = test->next)
+	{
+		if (test == sc_rover)
+			Sys_Printf ("ROVER:\n");
+		printf ("%p : %i bytes     %i width\n",test, test->size, test->width);
+	}
+}
+
+//=============================================================================
+
+// if the num is not a power of 2, assume it will not repeat
+
+int     MaskForNum (int num)
+{
+	if (num==128)
+		return 127;
+	if (num==64)
+		return 63;
+	if (num==32)
+		return 31;
+	if (num==16)
+		return 15;
+	return 255;
+}
+
+int D_log2 (int num)
+{
+	int     c;
+	
+	c = 0;
+	
+	while (num>>=1)
+		c++;
+	return c;
+}
+
+//=============================================================================
+
+/*
+================
+D_CacheSurface
+================
+*/
+surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel)
+{
+	surfcache_t     *cache;
+
+//
+// if the surface is animating or flashing, flush the cache
+//
+	r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture);
+	r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]];
+	r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]];
+	r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]];
+	r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]];
+	
+//
+// see if the cache holds apropriate data
+//
+	cache = surface->cachespots[miplevel];
+
+	if (cache && !cache->dlight && surface->dlightframe != r_framecount
+			&& cache->texture == r_drawsurf.texture
+			&& cache->lightadj[0] == r_drawsurf.lightadj[0]
+			&& cache->lightadj[1] == r_drawsurf.lightadj[1]
+			&& cache->lightadj[2] == r_drawsurf.lightadj[2]
+			&& cache->lightadj[3] == r_drawsurf.lightadj[3] )
+		return cache;
+
+//
+// determine shape of surface
+//
+	surfscale = 1.0 / (1<<miplevel);
+	r_drawsurf.surfmip = miplevel;
+	r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
+	r_drawsurf.rowbytes = r_drawsurf.surfwidth;
+	r_drawsurf.surfheight = surface->extents[1] >> miplevel;
+	
+//
+// allocate memory if needed
+//
+	if (!cache)     // if a texture just animated, don't reallocate it
+	{
+		cache = D_SCAlloc (r_drawsurf.surfwidth,
+						   r_drawsurf.surfwidth * r_drawsurf.surfheight);
+		surface->cachespots[miplevel] = cache;
+		cache->owner = &surface->cachespots[miplevel];
+		cache->mipscale = surfscale;
+	}
+	
+	if (surface->dlightframe == r_framecount)
+		cache->dlight = 1;
+	else
+		cache->dlight = 0;
+
+	r_drawsurf.surfdat = (pixel_t *)cache->data;
+	
+	cache->texture = r_drawsurf.texture;
+	cache->lightadj[0] = r_drawsurf.lightadj[0];
+	cache->lightadj[1] = r_drawsurf.lightadj[1];
+	cache->lightadj[2] = r_drawsurf.lightadj[2];
+	cache->lightadj[3] = r_drawsurf.lightadj[3];
+
+//
+// draw and light the surface texture
+//
+	r_drawsurf.surf = surface;
+
+	c_surf++;
+	R_DrawSurface ();
+
+	return surface->cachespots[miplevel];
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/d_vars.c b/apps/plugins/sdl/progs/quake/d_vars.c
new file mode 100644
index 0000000..88cc841
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_vars.c
@@ -0,0 +1,50 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_vars.c: global refresh variables
+
+#include	"quakedef.h"
+
+#if	!id386
+
+// all global and static refresh variables are collected in a contiguous block
+// to avoid cache conflicts.
+
+//-------------------------------------------------------
+// global refresh variables
+//-------------------------------------------------------
+
+// FIXME: make into one big structure, like cl or sv
+// FIXME: do separately for refresh engine and driver
+
+float	d_sdivzstepu, d_tdivzstepu, d_zistepu;
+float	d_sdivzstepv, d_tdivzstepv, d_zistepv;
+float	d_sdivzorigin, d_tdivzorigin, d_ziorigin;
+
+fixed16_t	sadjust, tadjust, bbextents, bbextentt;
+
+pixel_t			*cacheblock;
+int				cachewidth;
+pixel_t			*d_viewbuffer;
+short			*d_pzbuffer;
+unsigned int	d_zrowbytes;
+unsigned int	d_zwidth;
+
+#endif	// !id386
+
diff --git a/apps/plugins/sdl/progs/quake/d_zpoint.c b/apps/plugins/sdl/progs/quake/d_zpoint.c
new file mode 100644
index 0000000..909f250
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/d_zpoint.c
@@ -0,0 +1,47 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// d_zpoint.c: software driver module for drawing z-buffered points
+
+#include "quakedef.h"
+#include "d_local.h"
+
+
+/*
+=====================
+D_DrawZPoint
+=====================
+*/
+void D_DrawZPoint (void)
+{
+	byte	*pdest;
+	short	*pz;
+	int		izi;
+	
+	pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u;
+	pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u;
+	izi = (int)(r_zpointdesc.zi * 0x8000);
+
+	if (*pz <= izi)
+	{
+		*pz = izi;
+		*pdest = r_zpointdesc.color;
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/dosasm.S b/apps/plugins/sdl/progs/quake/dosasm.S
new file mode 100644
index 0000000..2633be9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/dosasm.S
@@ -0,0 +1,77 @@
+#include "asm_i386.h"
+
+.data
+fpenv:	.long	0, 0, 0, 0, 0, 0, 0, 0
+
+.text
+.globl	C(StartMSRInterval)
+C(StartMSRInterval):
+	movl	$0x11,%ecx	// read the CESR
+	.byte	0x0F
+	.byte	0x32		// RDMSR
+
+	andl	$0xFE3FFE3F,%eax	// stop both counters
+	.byte	0x0F
+	.byte	0x30		// WRMSR
+
+	movl	4(%esp),%eax	// point counter 0 to desired event, with counters
+	andl	$0x3F,%eax		//  still stopped
+	movl	$0x11,%ecx
+	.byte	0x0F
+	.byte	0x30		// WRMSR
+
+	movl	$0x12,%ecx	// set counter 0 to the value 0
+	subl	%eax,%eax
+	subl	%edx,%edx
+	.byte	0x0F
+	.byte	0x30		// WRMSR
+	
+	movl	4(%esp),%eax // restart counter 0 with selected event
+	andl	$0x3F,%eax
+	subl	%edx,%edx
+	orl		$0xC0,%eax
+	movl	$0x11,%ecx	// control and event select
+	.byte	0x0F
+	.byte	0x30		// WRMSR
+
+	ret
+
+.globl	C(EndMSRInterval)
+C(EndMSRInterval):
+	movl	$0x12,%ecx	// counter 0
+	.byte	0x0F
+	.byte	0x32		// RDMSR
+
+	ret					// lower 32 bits of count in %eax
+
+#if 0
+	.data
+Lxxx:	.long	0
+
+	.text
+
+.globl C(setstackcheck)
+C(setstackcheck):
+
+	movl	%esp,%eax
+	subl	$0x38000,%eax
+	movl	$0x5A5A5A5A,(%eax)
+	movl	%eax,Lxxx
+
+	ret
+
+
+.globl C(dostackcheck)
+C(dostackcheck):
+
+	movl	Lxxx,%edx
+	movl	$0,%eax
+
+	cmpl	$0x5A5A5A5A,(%edx)
+	jz		qqq
+	incl	%eax
+qqq:
+
+	ret
+#endif
+
diff --git a/apps/plugins/sdl/progs/quake/dosisms.h b/apps/plugins/sdl/progs/quake/dosisms.h
new file mode 100644
index 0000000..7f12268
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/dosisms.h
@@ -0,0 +1,100 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// dosisms.h: I'd call it dos.h, but the name's taken
+//
+
+#ifndef _DOSISMS_H_
+#define _DOSISMS_H_
+
+int dos_lockmem(void *addr, int size);
+int dos_unlockmem(void *addr, int size);
+
+typedef union {
+	struct {
+		unsigned long edi;
+		unsigned long esi;
+		unsigned long ebp;
+		unsigned long res;
+		unsigned long ebx;
+		unsigned long edx;
+		unsigned long ecx;
+		unsigned long eax;
+	} d;
+	struct {
+		unsigned short di, di_hi;
+		unsigned short si, si_hi;
+		unsigned short bp, bp_hi;
+		unsigned short res, res_hi;
+		unsigned short bx, bx_hi;
+		unsigned short dx, dx_hi;
+		unsigned short cx, cx_hi;
+		unsigned short ax, ax_hi;
+		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;
+	} x;
+	struct {
+		unsigned char edi[4];
+		unsigned char esi[4];
+		unsigned char ebp[4];
+		unsigned char res[4];
+		unsigned char bl, bh, ebx_b2, ebx_b3;
+		unsigned char dl, dh, edx_b2, edx_b3;
+		unsigned char cl, ch, ecx_b2, ecx_b3;
+		unsigned char al, ah, eax_b2, eax_b3;
+	} h;
+} regs_t;
+
+unsigned int ptr2real(void *ptr);
+void *real2ptr(unsigned int real);
+void *far2ptr(unsigned int farptr);
+unsigned int ptr2far(void *ptr);
+
+int	dos_inportb(int port);
+int	dos_inportw(int port);
+void dos_outportb(int port, int val);
+void dos_outportw(int port, int val);
+
+void dos_irqenable(void);
+void dos_irqdisable(void);
+void dos_registerintr(int intr, void (*handler)(void));
+void dos_restoreintr(int intr);
+
+int	dos_int86(int vec);
+
+void *dos_getmemory(int size);
+void dos_freememory(void *ptr);
+
+void	dos_usleep(int usecs);
+
+int dos_getheapsize(void);
+
+extern regs_t regs;
+
+#endif	// _DOSISMS_H_
+
diff --git a/apps/plugins/sdl/progs/quake/draw.c b/apps/plugins/sdl/progs/quake/draw.c
new file mode 100644
index 0000000..2a2f65b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/draw.c
@@ -0,0 +1,890 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// draw.c -- this is the only file outside the refresh that touches the
+// vid buffer
+
+#include "quakedef.h"
+
+typedef struct {
+	vrect_t	rect;
+	int		width;
+	int		height;
+	byte	*ptexbytes;
+	int		rowbytes;
+} rectdesc_t;
+
+static rectdesc_t	r_rectdesc;
+
+byte		*draw_chars;				// 8*8 graphic characters
+qpic_t		*draw_disc;
+qpic_t		*draw_backtile;
+
+//=============================================================================
+/* Support Routines */
+
+typedef struct cachepic_s
+{
+	char		name[MAX_QPATH];
+	cache_user_t	cache;
+} cachepic_t;
+
+#define	MAX_CACHED_PICS		128
+cachepic_t	menu_cachepics[MAX_CACHED_PICS];
+int			menu_numcachepics;
+
+
+qpic_t	*Draw_PicFromWad (char *name)
+{
+	return W_GetLumpName (name);
+}
+
+/*
+================
+Draw_CachePic
+================
+*/
+qpic_t	*Draw_CachePic (char *path)
+{
+	cachepic_t	*pic;
+	int			i;
+	qpic_t		*dat;
+	
+	for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
+		if (!strcmp (path, pic->name))
+			break;
+
+	if (i == menu_numcachepics)
+	{
+		if (menu_numcachepics == MAX_CACHED_PICS)
+			Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
+		menu_numcachepics++;
+		strcpy (pic->name, path);
+	}
+
+	dat = Cache_Check (&pic->cache);
+
+	if (dat)
+		return dat;
+
+//
+// load the pic from disk
+//
+	COM_LoadCacheFile (path, &pic->cache);
+	
+	dat = (qpic_t *)pic->cache.data;
+	if (!dat)
+	{
+		Sys_Error ("Draw_CachePic: failed to load %s", path);
+	}
+
+	SwapPic (dat);
+
+	return dat;
+}
+
+
+
+/*
+===============
+Draw_Init
+===============
+*/
+void Draw_Init (void)
+{
+	int		i;
+
+	draw_chars = W_GetLumpName ("conchars");
+	draw_disc = W_GetLumpName ("disc");
+	draw_backtile = W_GetLumpName ("backtile");
+
+	r_rectdesc.width = draw_backtile->width;
+	r_rectdesc.height = draw_backtile->height;
+	r_rectdesc.ptexbytes = draw_backtile->data;
+	r_rectdesc.rowbytes = draw_backtile->width;
+}
+
+
+
+/*
+================
+Draw_Character
+
+Draws one 8*8 graphics character with 0 being transparent.
+It can be clipped to the top of the screen to allow the console to be
+smoothly scrolled off.
+================
+*/
+void Draw_Character (int x, int y, int num)
+{
+	byte			*dest;
+	byte			*source;
+	unsigned short	*pusdest;
+	int				drawline;	
+	int				row, col;
+
+	num &= 255;
+	
+	if (y <= -8)
+		return;			// totally off screen
+
+#ifdef PARANOID
+	if (y > vid.height - 8 || x < 0 || x > vid.width - 8)
+		Sys_Error ("Con_DrawCharacter: (%i, %i)", x, y);
+	if (num < 0 || num > 255)
+		Sys_Error ("Con_DrawCharacter: char %i", num);
+#endif
+
+	row = num>>4;
+	col = num&15;
+	source = draw_chars + (row<<10) + (col<<3);
+
+	if (y < 0)
+	{	// clipped
+		drawline = 8 + y;
+		source -= 128*y;
+		y = 0;
+	}
+	else
+		drawline = 8;
+
+
+	if (r_pixbytes == 1)
+	{
+		dest = vid.conbuffer + y*vid.conrowbytes + x;
+	
+		while (drawline--)
+		{
+			if (source[0])
+				dest[0] = source[0];
+			if (source[1])
+				dest[1] = source[1];
+			if (source[2])
+				dest[2] = source[2];
+			if (source[3])
+				dest[3] = source[3];
+			if (source[4])
+				dest[4] = source[4];
+			if (source[5])
+				dest[5] = source[5];
+			if (source[6])
+				dest[6] = source[6];
+			if (source[7])
+				dest[7] = source[7];
+			source += 128;
+			dest += vid.conrowbytes;
+		}
+	}
+	else
+	{
+	// FIXME: pre-expand to native format?
+		pusdest = (unsigned short *)
+				((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1));
+
+		while (drawline--)
+		{
+			if (source[0])
+				pusdest[0] = d_8to16table[source[0]];
+			if (source[1])
+				pusdest[1] = d_8to16table[source[1]];
+			if (source[2])
+				pusdest[2] = d_8to16table[source[2]];
+			if (source[3])
+				pusdest[3] = d_8to16table[source[3]];
+			if (source[4])
+				pusdest[4] = d_8to16table[source[4]];
+			if (source[5])
+				pusdest[5] = d_8to16table[source[5]];
+			if (source[6])
+				pusdest[6] = d_8to16table[source[6]];
+			if (source[7])
+				pusdest[7] = d_8to16table[source[7]];
+
+			source += 128;
+			pusdest += (vid.conrowbytes >> 1);
+		}
+	}
+}
+
+/*
+================
+Draw_String
+================
+*/
+void Draw_String (int x, int y, char *str)
+{
+	while (*str)
+	{
+		Draw_Character (x, y, *str);
+		str++;
+		x += 8;
+	}
+}
+
+/*
+================
+Draw_DebugChar
+
+Draws a single character directly to the upper right corner of the screen.
+This is for debugging lockups by drawing different chars in different parts
+of the code.
+================
+*/
+void Draw_DebugChar (char num)
+{
+	byte			*dest;
+	byte			*source;
+	int				drawline;	
+	extern byte		*draw_chars;
+	int				row, col;
+
+	if (!vid.direct)
+		return;		// don't have direct FB access, so no debugchars...
+
+	drawline = 8;
+
+	row = num>>4;
+	col = num&15;
+	source = draw_chars + (row<<10) + (col<<3);
+
+	dest = vid.direct + 312;
+
+	while (drawline--)
+	{
+		dest[0] = source[0];
+		dest[1] = source[1];
+		dest[2] = source[2];
+		dest[3] = source[3];
+		dest[4] = source[4];
+		dest[5] = source[5];
+		dest[6] = source[6];
+		dest[7] = source[7];
+		source += 128;
+		dest += 320;
+	}
+}
+
+/*
+=============
+Draw_Pic
+=============
+*/
+void Draw_Pic (int x, int y, qpic_t *pic)
+{
+	byte			*dest, *source;
+	unsigned short	*pusdest;
+	int				v, u;
+
+	if ((x < 0) ||
+		(x + pic->width > vid.width) ||
+		(y < 0) ||
+		(y + pic->height > vid.height))
+	{
+		Sys_Error ("Draw_Pic: bad coordinates");
+	}
+
+	source = pic->data;
+
+	if (r_pixbytes == 1)
+	{
+		dest = vid.buffer + y * vid.rowbytes + x;
+
+		for (v=0 ; v<pic->height ; v++)
+		{
+			Q_memcpy (dest, source, pic->width);
+			dest += vid.rowbytes;
+			source += pic->width;
+		}
+	}
+	else
+	{
+	// FIXME: pretranslate at load time?
+		pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x;
+
+		for (v=0 ; v<pic->height ; v++)
+		{
+			for (u=0 ; u<pic->width ; u++)
+			{
+				pusdest[u] = d_8to16table[source[u]];
+			}
+
+			pusdest += vid.rowbytes >> 1;
+			source += pic->width;
+		}
+	}
+}
+
+
+/*
+=============
+Draw_TransPic
+=============
+*/
+void Draw_TransPic (int x, int y, qpic_t *pic)
+{
+	byte	*dest, *source, tbyte;
+	unsigned short	*pusdest;
+	int				v, u;
+
+	if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 ||
+		 (unsigned)(y + pic->height) > vid.height)
+	{
+		Sys_Error ("Draw_TransPic: bad coordinates");
+	}
+		
+	source = pic->data;
+
+	if (r_pixbytes == 1)
+	{
+		dest = vid.buffer + y * vid.rowbytes + x;
+
+		if (pic->width & 7)
+		{	// general
+			for (v=0 ; v<pic->height ; v++)
+			{
+				for (u=0 ; u<pic->width ; u++)
+					if ( (tbyte=source[u]) != TRANSPARENT_COLOR)
+						dest[u] = tbyte;
+	
+				dest += vid.rowbytes;
+				source += pic->width;
+			}
+		}
+		else
+		{	// unwound
+			for (v=0 ; v<pic->height ; v++)
+			{
+				for (u=0 ; u<pic->width ; u+=8)
+				{
+					if ( (tbyte=source[u]) != TRANSPARENT_COLOR)
+						dest[u] = tbyte;
+					if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR)
+						dest[u+1] = tbyte;
+					if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR)
+						dest[u+2] = tbyte;
+					if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR)
+						dest[u+3] = tbyte;
+					if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR)
+						dest[u+4] = tbyte;
+					if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR)
+						dest[u+5] = tbyte;
+					if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR)
+						dest[u+6] = tbyte;
+					if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR)
+						dest[u+7] = tbyte;
+				}
+				dest += vid.rowbytes;
+				source += pic->width;
+			}
+		}
+	}
+	else
+	{
+	// FIXME: pretranslate at load time?
+		pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x;
+
+		for (v=0 ; v<pic->height ; v++)
+		{
+			for (u=0 ; u<pic->width ; u++)
+			{
+				tbyte = source[u];
+
+				if (tbyte != TRANSPARENT_COLOR)
+				{
+					pusdest[u] = d_8to16table[tbyte];
+				}
+			}
+
+			pusdest += vid.rowbytes >> 1;
+			source += pic->width;
+		}
+	}
+}
+
+
+/*
+=============
+Draw_TransPicTranslate
+=============
+*/
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
+{
+	byte	*dest, *source, tbyte;
+	unsigned short	*pusdest;
+	int				v, u;
+
+	if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 ||
+		 (unsigned)(y + pic->height) > vid.height)
+	{
+		Sys_Error ("Draw_TransPic: bad coordinates");
+	}
+		
+	source = pic->data;
+
+	if (r_pixbytes == 1)
+	{
+		dest = vid.buffer + y * vid.rowbytes + x;
+
+		if (pic->width & 7)
+		{	// general
+			for (v=0 ; v<pic->height ; v++)
+			{
+				for (u=0 ; u<pic->width ; u++)
+					if ( (tbyte=source[u]) != TRANSPARENT_COLOR)
+						dest[u] = translation[tbyte];
+
+				dest += vid.rowbytes;
+				source += pic->width;
+			}
+		}
+		else
+		{	// unwound
+			for (v=0 ; v<pic->height ; v++)
+			{
+				for (u=0 ; u<pic->width ; u+=8)
+				{
+					if ( (tbyte=source[u]) != TRANSPARENT_COLOR)
+						dest[u] = translation[tbyte];
+					if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR)
+						dest[u+1] = translation[tbyte];
+					if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR)
+						dest[u+2] = translation[tbyte];
+					if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR)
+						dest[u+3] = translation[tbyte];
+					if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR)
+						dest[u+4] = translation[tbyte];
+					if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR)
+						dest[u+5] = translation[tbyte];
+					if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR)
+						dest[u+6] = translation[tbyte];
+					if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR)
+						dest[u+7] = translation[tbyte];
+				}
+				dest += vid.rowbytes;
+				source += pic->width;
+			}
+		}
+	}
+	else
+	{
+	// FIXME: pretranslate at load time?
+		pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x;
+
+		for (v=0 ; v<pic->height ; v++)
+		{
+			for (u=0 ; u<pic->width ; u++)
+			{
+				tbyte = source[u];
+
+				if (tbyte != TRANSPARENT_COLOR)
+				{
+					pusdest[u] = d_8to16table[tbyte];
+				}
+			}
+
+			pusdest += vid.rowbytes >> 1;
+			source += pic->width;
+		}
+	}
+}
+
+
+void Draw_CharToConback (int num, byte *dest)
+{
+	int		row, col;
+	byte	*source;
+	int		drawline;
+	int		x;
+
+	row = num>>4;
+	col = num&15;
+	source = draw_chars + (row<<10) + (col<<3);
+
+	drawline = 8;
+
+	while (drawline--)
+	{
+		for (x=0 ; x<8 ; x++)
+			if (source[x])
+				dest[x] = 0x60 + source[x];
+		source += 128;
+		dest += 320;
+	}
+
+}
+
+/*
+================
+Draw_ConsoleBackground
+
+================
+*/
+void Draw_ConsoleBackground (int lines)
+{
+	int				x, y, v;
+	byte			*src, *dest;
+	unsigned short	*pusdest;
+	int				f, fstep;
+	qpic_t			*conback;
+	char			ver[100];
+
+	conback = Draw_CachePic ("gfx/conback.lmp");
+
+	dest = conback->data + 320 - 43 + 320*186;
+	sprintf (ver, "%.2f", (float)VERSION);
+
+	for (x=0 ; x<strlen(ver) ; x++)
+		Draw_CharToConback (ver[x], dest+(x<<3));
+	
+// draw the pic
+	if (r_pixbytes == 1)
+	{
+		dest = vid.conbuffer;
+
+		for (y=0 ; y<lines ; y++, dest += vid.conrowbytes)
+		{
+			v = (vid.conheight - lines + y)*200/vid.conheight;
+			src = conback->data + v*320;
+			if (vid.conwidth == 320)
+				memcpy (dest, src, vid.conwidth);
+			else
+			{
+				f = 0;
+				fstep = 320*0x10000/vid.conwidth;
+				for (x=0 ; x<vid.conwidth ; x+=4)
+				{
+					dest[x] = src[f>>16];
+					f += fstep;
+					dest[x+1] = src[f>>16];
+					f += fstep;
+					dest[x+2] = src[f>>16];
+					f += fstep;
+					dest[x+3] = src[f>>16];
+					f += fstep;
+				}
+			}
+		}
+	}
+	else
+	{
+		pusdest = (unsigned short *)vid.conbuffer;
+
+		for (y=0 ; y<lines ; y++, pusdest += (vid.conrowbytes >> 1))
+		{
+		// FIXME: pre-expand to native format?
+		// FIXME: does the endian switching go away in production?
+			v = (vid.conheight - lines + y)*200/vid.conheight;
+			src = conback->data + v*320;
+			f = 0;
+			fstep = 320*0x10000/vid.conwidth;
+			for (x=0 ; x<vid.conwidth ; x+=4)
+			{
+				pusdest[x] = d_8to16table[src[f>>16]];
+				f += fstep;
+				pusdest[x+1] = d_8to16table[src[f>>16]];
+				f += fstep;
+				pusdest[x+2] = d_8to16table[src[f>>16]];
+				f += fstep;
+				pusdest[x+3] = d_8to16table[src[f>>16]];
+				f += fstep;
+			}
+		}
+	}
+}
+
+
+/*
+==============
+R_DrawRect8
+==============
+*/
+void R_DrawRect8 (vrect_t *prect, int rowbytes, byte *psrc,
+	int transparent)
+{
+	byte	t;
+	int		i, j, srcdelta, destdelta;
+	byte	*pdest;
+
+	pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x;
+
+	srcdelta = rowbytes - prect->width;
+	destdelta = vid.rowbytes - prect->width;
+
+	if (transparent)
+	{
+		for (i=0 ; i<prect->height ; i++)
+		{
+			for (j=0 ; j<prect->width ; j++)
+			{
+				t = *psrc;
+				if (t != TRANSPARENT_COLOR)
+				{
+					*pdest = t;
+				}
+
+				psrc++;
+				pdest++;
+			}
+
+			psrc += srcdelta;
+			pdest += destdelta;
+		}
+	}
+	else
+	{
+		for (i=0 ; i<prect->height ; i++)
+		{
+			memcpy (pdest, psrc, prect->width);
+			psrc += rowbytes;
+			pdest += vid.rowbytes;
+		}
+	}
+}
+
+
+/*
+==============
+R_DrawRect16
+==============
+*/
+void R_DrawRect16 (vrect_t *prect, int rowbytes, byte *psrc,
+	int transparent)
+{
+	byte			t;
+	int				i, j, srcdelta, destdelta;
+	unsigned short	*pdest;
+
+// FIXME: would it be better to pre-expand native-format versions?
+
+	pdest = (unsigned short *)vid.buffer +
+			(prect->y * (vid.rowbytes >> 1)) + prect->x;
+
+	srcdelta = rowbytes - prect->width;
+	destdelta = (vid.rowbytes >> 1) - prect->width;
+
+	if (transparent)
+	{
+		for (i=0 ; i<prect->height ; i++)
+		{
+			for (j=0 ; j<prect->width ; j++)
+			{
+				t = *psrc;
+				if (t != TRANSPARENT_COLOR)
+				{
+					*pdest = d_8to16table[t];
+				}
+
+				psrc++;
+				pdest++;
+			}
+
+			psrc += srcdelta;
+			pdest += destdelta;
+		}
+	}
+	else
+	{
+		for (i=0 ; i<prect->height ; i++)
+		{
+			for (j=0 ; j<prect->width ; j++)
+			{
+				*pdest = d_8to16table[*psrc];
+				psrc++;
+				pdest++;
+			}
+
+			psrc += srcdelta;
+			pdest += destdelta;
+		}
+	}
+}
+
+
+/*
+=============
+Draw_TileClear
+
+This repeats a 64*64 tile graphic to fill the screen around a sized down
+refresh window.
+=============
+*/
+void Draw_TileClear (int x, int y, int w, int h)
+{
+	int				width, height, tileoffsetx, tileoffsety;
+	byte			*psrc;
+	vrect_t			vr;
+
+	r_rectdesc.rect.x = x;
+	r_rectdesc.rect.y = y;
+	r_rectdesc.rect.width = w;
+	r_rectdesc.rect.height = h;
+
+	vr.y = r_rectdesc.rect.y;
+	height = r_rectdesc.rect.height;
+
+	tileoffsety = vr.y % r_rectdesc.height;
+
+	while (height > 0)
+	{
+		vr.x = r_rectdesc.rect.x;
+		width = r_rectdesc.rect.width;
+
+		if (tileoffsety != 0)
+			vr.height = r_rectdesc.height - tileoffsety;
+		else
+			vr.height = r_rectdesc.height;
+
+		if (vr.height > height)
+			vr.height = height;
+
+		tileoffsetx = vr.x % r_rectdesc.width;
+
+		while (width > 0)
+		{
+			if (tileoffsetx != 0)
+				vr.width = r_rectdesc.width - tileoffsetx;
+			else
+				vr.width = r_rectdesc.width;
+
+			if (vr.width > width)
+				vr.width = width;
+
+			psrc = r_rectdesc.ptexbytes +
+					(tileoffsety * r_rectdesc.rowbytes) + tileoffsetx;
+
+			if (r_pixbytes == 1)
+			{
+				R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0);
+			}
+			else
+			{
+				R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0);
+			}
+
+			vr.x += vr.width;
+			width -= vr.width;
+			tileoffsetx = 0;	// only the left tile can be left-clipped
+		}
+
+		vr.y += vr.height;
+		height -= vr.height;
+		tileoffsety = 0;		// only the top tile can be top-clipped
+	}
+}
+
+
+/*
+=============
+Draw_Fill
+
+Fills a box of pixels with a single color
+=============
+*/
+void Draw_Fill (int x, int y, int w, int h, int c)
+{
+	byte			*dest;
+	unsigned short	*pusdest;
+	unsigned		uc;
+	int				u, v;
+
+	if (r_pixbytes == 1)
+	{
+		dest = vid.buffer + y*vid.rowbytes + x;
+		for (v=0 ; v<h ; v++, dest += vid.rowbytes)
+			for (u=0 ; u<w ; u++)
+				dest[u] = c;
+	}
+	else
+	{
+		uc = d_8to16table[c];
+
+		pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x;
+		for (v=0 ; v<h ; v++, pusdest += (vid.rowbytes >> 1))
+			for (u=0 ; u<w ; u++)
+				pusdest[u] = uc;
+	}
+}
+//=============================================================================
+
+/*
+================
+Draw_FadeScreen
+
+================
+*/
+void Draw_FadeScreen (void)
+{
+	int			x,y;
+	byte		*pbuf;
+
+	VID_UnlockBuffer ();
+	S_ExtraUpdate ();
+	VID_LockBuffer ();
+
+	for (y=0 ; y<vid.height ; y++)
+	{
+		int	t;
+
+		pbuf = (byte *)(vid.buffer + vid.rowbytes*y);
+		t = (y & 1) << 1;
+
+		for (x=0 ; x<vid.width ; x++)
+		{
+			if ((x & 3) != t)
+				pbuf[x] = 0;
+		}
+	}
+
+	VID_UnlockBuffer ();
+	S_ExtraUpdate ();
+	VID_LockBuffer ();
+}
+
+//=============================================================================
+
+/*
+================
+Draw_BeginDisc
+
+Draws the little blue disc in the corner of the screen.
+Call before beginning any disc IO.
+================
+*/
+void Draw_BeginDisc (void)
+{
+
+	D_BeginDirectRect (vid.width - 24, 0, draw_disc->data, 24, 24);
+}
+
+
+/*
+================
+Draw_EndDisc
+
+Erases the disc icon.
+Call after completing any disc IO
+================
+*/
+void Draw_EndDisc (void)
+{
+
+	D_EndDirectRect (vid.width - 24, 0, 24, 24);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/draw.h b/apps/plugins/sdl/progs/quake/draw.h
new file mode 100644
index 0000000..5055b65
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/draw.h
@@ -0,0 +1,40 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// draw.h -- these are the only functions outside the refresh allowed
+// to touch the vid buffer
+
+extern	qpic_t		*draw_disc;	// also used on sbar
+
+void Draw_Init (void);
+void Draw_Character (int x, int y, int num);
+void Draw_DebugChar (char num);
+void Draw_Pic (int x, int y, qpic_t *pic);
+void Draw_TransPic (int x, int y, qpic_t *pic);
+void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation);
+void Draw_ConsoleBackground (int lines);
+void Draw_BeginDisc (void);
+void Draw_EndDisc (void);
+void Draw_TileClear (int x, int y, int w, int h);
+void Draw_Fill (int x, int y, int w, int h, int c);
+void Draw_FadeScreen (void);
+void Draw_String (int x, int y, char *str);
+qpic_t *Draw_PicFromWad (char *name);
+qpic_t *Draw_CachePic (char *path);
diff --git a/apps/plugins/sdl/progs/quake/gl_vidlinux.c b/apps/plugins/sdl/progs/quake/gl_vidlinux.c
new file mode 100644
index 0000000..c29f042
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/gl_vidlinux.c
@@ -0,0 +1,866 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 <termios.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/vt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <asm/io.h>
+#include <dlfcn.h>
+
+/*#include "vga.h" */
+#include "vgakeyboard.h"
+#include "vgamouse.h"
+
+#include "quakedef.h"
+#include "GL/fxmesa.h"
+
+#define WARP_WIDTH              320
+#define WARP_HEIGHT             200
+
+static fxMesaContext fc = NULL;
+#define stringify(m) { #m, m }
+
+unsigned short	d_8to16table[256];
+unsigned	d_8to24table[256];
+unsigned char d_15to8table[65536];
+
+int num_shades=32;
+
+struct
+{
+	char *name;
+	int num;
+} mice[] =
+{
+	stringify(MOUSE_MICROSOFT),
+	stringify(MOUSE_MOUSESYSTEMS),
+	stringify(MOUSE_MMSERIES),
+	stringify(MOUSE_LOGITECH),
+	stringify(MOUSE_BUSMOUSE),
+	stringify(MOUSE_PS2),
+};
+
+static unsigned char scantokey[128];
+
+int num_mice = sizeof (mice) / sizeof(mice[0]);
+
+int	d_con_indirect = 0;
+
+int		svgalib_inited=0;
+int		UseMouse = 1;
+int		UseKeyboard = 1;
+
+int		mouserate = MOUSE_DEFAULTSAMPLERATE;
+
+cvar_t		vid_mode = {"vid_mode","5",false};
+cvar_t		vid_redrawfull = {"vid_redrawfull","0",false};
+cvar_t		vid_waitforrefresh = {"vid_waitforrefresh","0",true};
+ 
+char	*framebuffer_ptr;
+
+cvar_t  mouse_button_commands[3] =
+{
+    {"mouse1","+attack"},
+    {"mouse2","+strafe"},
+    {"mouse3","+forward"},
+};
+
+int     mouse_buttons;
+int     mouse_buttonstate;
+int     mouse_oldbuttonstate;
+float   mouse_x, mouse_y;
+float	old_mouse_x, old_mouse_y;
+int		mx, my;
+
+cvar_t	m_filter = {"m_filter","1"};
+
+int scr_width, scr_height;
+
+/*-----------------------------------------------------------------------*/
+
+//int		texture_mode = GL_NEAREST;
+//int		texture_mode = GL_NEAREST_MIPMAP_NEAREST;
+//int		texture_mode = GL_NEAREST_MIPMAP_LINEAR;
+int		texture_mode = GL_LINEAR;
+//int		texture_mode = GL_LINEAR_MIPMAP_NEAREST;
+//int		texture_mode = GL_LINEAR_MIPMAP_LINEAR;
+
+int		texture_extension_number = 1;
+
+float		gldepthmin, gldepthmax;
+
+cvar_t	gl_ztrick = {"gl_ztrick","1"};
+
+const char *gl_vendor;
+const char *gl_renderer;
+const char *gl_version;
+const char *gl_extensions;
+
+void (*qgl3DfxSetPaletteEXT) (GLuint *);
+void (*qglColorTableEXT) (int, int, int, int, int, const void *);
+
+static float vid_gamma = 1.0;
+
+qboolean is8bit = false;
+qboolean isPermedia = false;
+qboolean gl_mtexable = false;
+
+/*-----------------------------------------------------------------------*/
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
+{
+}
+
+void D_EndDirectRect (int x, int y, int width, int height)
+{
+}
+
+int matchmouse(int mouse, char *name)
+{
+	int i;
+	for (i=0 ; i<num_mice ; i++)
+		if (!strcmp(mice[i].name, name))
+			return i;
+	return mouse;
+}
+
+#if 0
+
+void vtswitch(int newconsole)
+{
+
+	int fd;
+	struct vt_stat x;
+
+// switch consoles and wait until reactivated
+	fd = open("/dev/console", O_RDONLY);
+	ioctl(fd, VT_GETSTATE, &x);
+	ioctl(fd, VT_ACTIVATE, newconsole);
+	ioctl(fd, VT_WAITACTIVE, x.v_active);
+	close(fd);
+
+}
+
+#endif
+
+void keyhandler(int scancode, int state)
+{
+	
+	int sc;
+
+	sc = scancode & 0x7f;
+
+	Key_Event(scantokey[sc], state == KEY_EVENTPRESS);
+
+}
+
+void VID_Shutdown(void)
+{
+	if (!fc)
+		return;
+
+	fxMesaDestroyContext(fc);
+
+	if (UseKeyboard)
+		keyboard_close();
+}
+
+void signal_handler(int sig)
+{
+	printf("Received signal %d, exiting...\n", sig);
+	Sys_Quit();
+	exit(0);
+}
+
+void InitSig(void)
+{
+	signal(SIGHUP, signal_handler);
+	signal(SIGINT, signal_handler);
+	signal(SIGQUIT, signal_handler);
+	signal(SIGILL, signal_handler);
+	signal(SIGTRAP, signal_handler);
+	signal(SIGIOT, signal_handler);
+	signal(SIGBUS, signal_handler);
+	signal(SIGFPE, signal_handler);
+	signal(SIGSEGV, signal_handler);
+	signal(SIGTERM, signal_handler);
+}
+
+void VID_ShiftPalette(unsigned char *p)
+{
+//	VID_SetPalette(p);
+}
+
+void	VID_SetPalette (unsigned char *palette)
+{
+	byte	*pal;
+	unsigned r,g,b;
+	unsigned v;
+	int     r1,g1,b1;
+	int		j,k,l,m;
+	unsigned short i;
+	unsigned	*table;
+	FILE *f;
+	char s[255];
+	int dist, bestdist;
+	static qboolean palflag = false;
+
+//
+// 8 8 8 encoding
+//
+	pal = palette;
+	table = d_8to24table;
+	for (i=0 ; i<256 ; i++)
+	{
+		r = pal[0];
+		g = pal[1];
+		b = pal[2];
+		pal += 3;
+		
+		v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
+		*table++ = v;
+	}
+	d_8to24table[255] &= 0xffffff;	// 255 is transparent
+
+	// JACK: 3D distance calcs - k is last closest, l is the distance.
+	for (i=0; i < (1<<15); i++) {
+		/* Maps
+		000000000000000
+		000000000011111 = Red  = 0x1F
+		000001111100000 = Blue = 0x03E0
+		111110000000000 = Grn  = 0x7C00
+		*/
+		r = ((i & 0x1F) << 3)+4;
+		g = ((i & 0x03E0) >> 2)+4;
+		b = ((i & 0x7C00) >> 7)+4;
+		pal = (unsigned char *)d_8to24table;
+		for (v=0,k=0,bestdist=10000*10000; v<256; v++,pal+=4) {
+			r1 = (int)r - (int)pal[0];
+			g1 = (int)g - (int)pal[1];
+			b1 = (int)b - (int)pal[2];
+			dist = (r1*r1)+(g1*g1)+(b1*b1);
+			if (dist < bestdist) {
+				k=v;
+				bestdist = dist;
+			}
+		}
+		d_15to8table[i]=k;
+	}
+}
+
+void CheckMultiTextureExtensions(void) 
+{
+	void *prjobj;
+
+	if (strstr(gl_extensions, "GL_SGIS_multitexture ") && !COM_CheckParm("-nomtex")) {
+		Con_Printf("Found GL_SGIS_multitexture...\n");
+
+		if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL) {
+			Con_Printf("Unable to open symbol list for main program.\n");
+			return;
+		}
+
+		qglMTexCoord2fSGIS = (void *) dlsym(prjobj, "glMTexCoord2fSGIS");
+		qglSelectTextureSGIS = (void *) dlsym(prjobj, "glSelectTextureSGIS");
+
+		if (qglMTexCoord2fSGIS && qglSelectTextureSGIS) {
+			Con_Printf("Multitexture extensions found.\n");
+			gl_mtexable = true;
+		} else
+			Con_Printf("Symbol not found, disabled.\n");
+
+		dlclose(prjobj);
+	}
+}
+
+/*
+===============
+GL_Init
+===============
+*/
+void GL_Init (void)
+{
+	gl_vendor = glGetString (GL_VENDOR);
+	Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
+	gl_renderer = glGetString (GL_RENDERER);
+	Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
+
+	gl_version = glGetString (GL_VERSION);
+	Con_Printf ("GL_VERSION: %s\n", gl_version);
+	gl_extensions = glGetString (GL_EXTENSIONS);
+	Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
+
+//	Con_Printf ("%s %s\n", gl_renderer, gl_version);
+
+	CheckMultiTextureExtensions ();
+
+	glClearColor (1,0,0,0);
+	glCullFace(GL_FRONT);
+	glEnable(GL_TEXTURE_2D);
+
+	glEnable(GL_ALPHA_TEST);
+	glAlphaFunc(GL_GREATER, 0.666);
+
+	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	glShadeModel (GL_FLAT);
+
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+//	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+}
+
+/*
+=================
+GL_BeginRendering
+
+=================
+*/
+void GL_BeginRendering (int *x, int *y, int *width, int *height)
+{
+	extern cvar_t gl_clear;
+
+	*x = *y = 0;
+	*width = scr_width;
+	*height = scr_height;
+
+//    if (!wglMakeCurrent( maindc, baseRC ))
+//		Sys_Error ("wglMakeCurrent failed");
+
+//	glViewport (*x, *y, *width, *height);
+}
+
+
+void GL_EndRendering (void)
+{
+	glFlush();
+	fxMesaSwapBuffers();
+}
+
+void Init_KBD(void)
+{
+	int i;
+
+	if (COM_CheckParm("-nokbd")) UseKeyboard = 0;
+
+	if (UseKeyboard)
+	{
+		for (i=0 ; i<128 ; i++)
+			scantokey[i] = ' ';
+
+		scantokey[42] = K_SHIFT;
+		scantokey[54] = K_SHIFT;
+		scantokey[72] = K_UPARROW;
+		scantokey[103] = K_UPARROW;
+		scantokey[80] = K_DOWNARROW;
+		scantokey[108] = K_DOWNARROW;
+		scantokey[75] = K_LEFTARROW;
+		scantokey[105] = K_LEFTARROW;
+		scantokey[77] = K_RIGHTARROW;
+		scantokey[106] = K_RIGHTARROW;
+		scantokey[29] = K_CTRL;
+		scantokey[97] = K_CTRL;
+		scantokey[56] = K_ALT;
+		scantokey[100] = K_ALT;
+//		scantokey[58] = JK_CAPS;
+//		scantokey[69] = JK_NUM_LOCK;
+		scantokey[71] = K_HOME;
+		scantokey[73] = K_PGUP;
+		scantokey[79] = K_END;
+		scantokey[81] = K_PGDN;
+		scantokey[82] = K_INS;
+		scantokey[83] = K_DEL;
+		scantokey[1 ] = K_ESCAPE;
+		scantokey[28] = K_ENTER;
+		scantokey[15] = K_TAB;
+		scantokey[14] = K_BACKSPACE;
+		scantokey[119] = K_PAUSE;
+		scantokey[57] = ' ';
+
+		scantokey[102] = K_HOME;
+		scantokey[104] = K_PGUP;
+		scantokey[107] = K_END;
+		scantokey[109] = K_PGDN;
+		scantokey[110] = K_INS;
+		scantokey[111] = K_DEL;
+
+		scantokey[2] = '1';
+		scantokey[3] = '2';
+		scantokey[4] = '3';
+		scantokey[5] = '4';
+		scantokey[6] = '5';
+		scantokey[7] = '6';
+		scantokey[8] = '7';
+		scantokey[9] = '8';
+		scantokey[10] = '9';
+		scantokey[11] = '0';
+		scantokey[12] = '-';
+		scantokey[13] = '=';
+		scantokey[41] = '`';
+		scantokey[26] = '[';
+		scantokey[27] = ']';
+		scantokey[39] = ';';
+		scantokey[40] = '\'';
+		scantokey[51] = ',';
+		scantokey[52] = '.';
+		scantokey[53] = '/';
+		scantokey[43] = '\\';
+
+		scantokey[59] = K_F1;
+		scantokey[60] = K_F2;
+		scantokey[61] = K_F3;
+		scantokey[62] = K_F4;
+		scantokey[63] = K_F5;
+		scantokey[64] = K_F6;
+		scantokey[65] = K_F7;
+		scantokey[66] = K_F8;
+		scantokey[67] = K_F9;
+		scantokey[68] = K_F10;
+		scantokey[87] = K_F11;
+		scantokey[88] = K_F12;
+		scantokey[30] = 'a';
+		scantokey[48] = 'b';
+		scantokey[46] = 'c';
+		scantokey[32] = 'd';       
+		scantokey[18] = 'e';       
+		scantokey[33] = 'f';       
+		scantokey[34] = 'g';       
+		scantokey[35] = 'h';       
+		scantokey[23] = 'i';       
+		scantokey[36] = 'j';       
+		scantokey[37] = 'k';       
+		scantokey[38] = 'l';       
+		scantokey[50] = 'm';       
+		scantokey[49] = 'n';       
+		scantokey[24] = 'o';       
+		scantokey[25] = 'p';       
+		scantokey[16] = 'q';       
+		scantokey[19] = 'r';       
+		scantokey[31] = 's';       
+		scantokey[20] = 't';       
+		scantokey[22] = 'u';       
+		scantokey[47] = 'v';       
+		scantokey[17] = 'w';       
+		scantokey[45] = 'x';       
+		scantokey[21] = 'y';       
+		scantokey[44] = 'z';       
+
+		scantokey[78] = '+';
+		scantokey[74] = '-';
+
+		if (keyboard_init())
+			Sys_Error("keyboard_init() failed");
+		keyboard_seteventhandler(keyhandler);
+	}
+}
+
+#define NUM_RESOLUTIONS 16
+
+static int resolutions[NUM_RESOLUTIONS][3]={ 
+	320,200,  GR_RESOLUTION_320x200,
+	320,240,  GR_RESOLUTION_320x240,
+	400,256,  GR_RESOLUTION_400x256,
+	400,300,  GR_RESOLUTION_400x300,
+	512,384,  GR_RESOLUTION_512x384,
+	640,200,  GR_RESOLUTION_640x200,
+	640,350,  GR_RESOLUTION_640x350,
+	640,400,  GR_RESOLUTION_640x400,
+	640,480,  GR_RESOLUTION_640x480,
+	800,600,  GR_RESOLUTION_800x600,
+	960,720,  GR_RESOLUTION_960x720,
+	856,480,  GR_RESOLUTION_856x480,
+	512,256,  GR_RESOLUTION_512x256,
+	1024,768, GR_RESOLUTION_1024x768,
+	1280,1024,GR_RESOLUTION_1280x1024,
+	1600,1200,GR_RESOLUTION_1600x1200
+};
+
+int findres(int *width, int *height)
+{
+	int i;
+
+	for(i=0;i<NUM_RESOLUTIONS;i++)
+		if((*width<=resolutions[i][0]) && (*height<=resolutions[i][1])) {
+			*width = resolutions[i][0];
+			*height = resolutions[i][1];
+			return resolutions[i][2];
+		}
+        
+	*width = 640;
+	*height = 480;
+	return GR_RESOLUTION_640x480;
+}
+
+qboolean VID_Is8bit(void)
+{
+	return is8bit;
+}
+
+void VID_Init8bitPalette(void) 
+{
+	// Check for 8bit Extensions and initialize them.
+	int i;
+	void *prjobj;
+
+	if (COM_CheckParm("-no8bit"))
+		return;
+
+	if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL) {
+		Con_Printf("Unable to open symbol list for main program.\n");
+		return;
+	}
+
+	if (strstr(gl_extensions, "3DFX_set_global_palette") &&
+		(qgl3DfxSetPaletteEXT = dlsym(prjobj, "gl3DfxSetPaletteEXT")) != NULL) {
+		GLubyte table[256][4];
+		char *oldpal;
+
+		Con_SafePrintf("... Using 3DFX_set_global_palette\n");
+		glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
+		oldpal = (char *) d_8to24table; //d_8to24table3dfx;
+		for (i=0;i<256;i++) {
+			table[i][2] = *oldpal++;
+			table[i][1] = *oldpal++;
+			table[i][0] = *oldpal++;
+			table[i][3] = 255;
+			oldpal++;
+		}
+		qgl3DfxSetPaletteEXT((GLuint *)table);
+		is8bit = true;
+
+	} else if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") &&
+		(qglColorTableEXT = dlsym(prjobj, "glColorTableEXT")) != NULL) {
+		char thePalette[256*3];
+		char *oldPalette, *newPalette;
+
+		Con_SafePrintf("... Using GL_EXT_shared_texture_palette\n");
+		glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
+		oldPalette = (char *) d_8to24table; //d_8to24table3dfx;
+		newPalette = thePalette;
+		for (i=0;i<256;i++) {
+			*newPalette++ = *oldPalette++;
+			*newPalette++ = *oldPalette++;
+			*newPalette++ = *oldPalette++;
+			oldPalette++;
+		}
+		qglColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette);
+		is8bit = true;
+	
+	}
+
+	dlclose(prjobj);
+}
+
+static void Check_Gamma (unsigned char *pal)
+{
+	float	f, inf;
+	unsigned char	palette[768];
+	int		i;
+
+	if ((i = COM_CheckParm("-gamma")) == 0) {
+		if ((gl_renderer && strstr(gl_renderer, "Voodoo")) ||
+			(gl_vendor && strstr(gl_vendor, "3Dfx")))
+			vid_gamma = 1;
+		else
+			vid_gamma = 0.7; // default to 0.7 on non-3dfx hardware
+	} else
+		vid_gamma = Q_atof(com_argv[i+1]);
+
+	for (i=0 ; i<768 ; i++)
+	{
+		f = pow ( (pal[i]+1)/256.0 , vid_gamma );
+		inf = f*255 + 0.5;
+		if (inf < 0)
+			inf = 0;
+		if (inf > 255)
+			inf = 255;
+		palette[i] = inf;
+	}
+
+	memcpy (pal, palette, sizeof(palette));
+}
+
+void VID_Init(unsigned char *palette)
+{
+	int i;
+	GLint attribs[32];
+	char	gldir[MAX_OSPATH];
+	int width = 640, height = 480;
+
+	Init_KBD();
+
+	Cvar_RegisterVariable (&vid_mode);
+	Cvar_RegisterVariable (&vid_redrawfull);
+	Cvar_RegisterVariable (&vid_waitforrefresh);
+	Cvar_RegisterVariable (&gl_ztrick);
+	
+	vid.maxwarpwidth = WARP_WIDTH;
+	vid.maxwarpheight = WARP_HEIGHT;
+	vid.colormap = host_colormap;
+	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+
+// interpret command-line params
+
+// set vid parameters
+	attribs[0] = FXMESA_DOUBLEBUFFER;
+	attribs[1] = FXMESA_ALPHA_SIZE;
+	attribs[2] = 1;
+	attribs[3] = FXMESA_DEPTH_SIZE;
+	attribs[4] = 1;
+	attribs[5] = FXMESA_NONE;
+
+	if ((i = COM_CheckParm("-width")) != 0)
+		width = atoi(com_argv[i+1]);
+	if ((i = COM_CheckParm("-height")) != 0)
+		height = atoi(com_argv[i+1]);
+
+	if ((i = COM_CheckParm("-conwidth")) != 0)
+		vid.conwidth = Q_atoi(com_argv[i+1]);
+	else
+		vid.conwidth = 640;
+
+	vid.conwidth &= 0xfff8; // make it a multiple of eight
+
+	if (vid.conwidth < 320)
+		vid.conwidth = 320;
+
+	// pick a conheight that matches with correct aspect
+	vid.conheight = vid.conwidth*3 / 4;
+
+	if ((i = COM_CheckParm("-conheight")) != 0)
+		vid.conheight = Q_atoi(com_argv[i+1]);
+	if (vid.conheight < 200)
+		vid.conheight = 200;
+
+	fc = fxMesaCreateContext(0, findres(&width, &height), GR_REFRESH_75Hz, 
+		attribs);
+	if (!fc)
+		Sys_Error("Unable to create 3DFX context.\n");
+
+	InitSig(); // trap evil signals
+
+	scr_width = width;
+	scr_height = height;
+
+	fxMesaMakeCurrent(fc);
+
+	if (vid.conheight > height)
+		vid.conheight = height;
+	if (vid.conwidth > width)
+		vid.conwidth = width;
+	vid.width = vid.conwidth;
+	vid.height = vid.conheight;
+
+	vid.aspect = ((float)vid.height / (float)vid.width) *
+				(320.0 / 240.0);
+	vid.numpages = 2;
+
+	GL_Init();
+
+	sprintf (gldir, "%s/glquake", com_gamedir);
+	Sys_mkdir (gldir);
+
+	Check_Gamma(palette);
+	VID_SetPalette(palette);
+
+	// Check for 3DFX Extensions and initialize them.
+	VID_Init8bitPalette();
+
+	Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
+
+	vid.recalc_refdef = 1;				// force a surface cache flush
+}
+
+void Sys_SendKeyEvents(void)
+{
+	if (UseKeyboard)
+		while (keyboard_update());
+}
+
+void Force_CenterView_f (void)
+{
+	cl.viewangles[PITCH] = 0;
+}
+
+
+void mousehandler(int buttonstate, int dx, int dy)
+{
+	mouse_buttonstate = buttonstate;
+	mx += dx;
+	my += dy;
+}
+
+void IN_Init(void)
+{
+
+	int mtype;
+	char *mousedev;
+	int mouserate;
+
+	if (UseMouse)
+	{
+
+		Cvar_RegisterVariable (&mouse_button_commands[0]);
+		Cvar_RegisterVariable (&mouse_button_commands[1]);
+		Cvar_RegisterVariable (&mouse_button_commands[2]);
+		Cmd_AddCommand ("force_centerview", Force_CenterView_f);
+
+		mouse_buttons = 3;
+
+		mtype = vga_getmousetype();
+
+		mousedev = "/dev/mouse";
+		if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV");
+		if (COM_CheckParm("-mdev"))
+			mousedev = com_argv[COM_CheckParm("-mdev")+1];
+
+		mouserate = 1200;
+		if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE"));
+		if (COM_CheckParm("-mrate"))
+			mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]);
+
+		if (mouse_init(mousedev, mtype, mouserate))
+		{
+			Con_Printf("No mouse found\n");
+			UseMouse = 0;
+		}
+		else
+			mouse_seteventhandler(mousehandler);
+
+	}
+
+}
+
+void IN_Shutdown(void)
+{
+	if (UseMouse)
+		mouse_close();
+}
+
+/*
+===========
+IN_Commands
+===========
+*/
+void IN_Commands (void)
+{
+	if (UseMouse && cls.state != ca_dedicated)
+	{
+		// poll mouse values
+		while (mouse_update())
+			;
+
+		// perform button actions
+		if ((mouse_buttonstate & MOUSE_LEFTBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_LEFTBUTTON))
+			Key_Event (K_MOUSE1, true);
+		else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_LEFTBUTTON))
+			Key_Event (K_MOUSE1, false);
+
+		if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON))
+			Key_Event (K_MOUSE2, true);
+		else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON))
+			Key_Event (K_MOUSE2, false);
+
+		if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON))
+			Key_Event (K_MOUSE3, true);
+		else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON))
+			Key_Event (K_MOUSE3, false);
+
+		mouse_oldbuttonstate = mouse_buttonstate;
+	}
+}
+
+/*
+===========
+IN_Move
+===========
+*/
+void IN_MouseMove (usercmd_t *cmd)
+{
+	if (!UseMouse)
+		return;
+
+	// poll mouse values
+	while (mouse_update())
+		;
+
+	if (m_filter.value)
+	{
+		mouse_x = (mx + old_mouse_x) * 0.5;
+		mouse_y = (my + old_mouse_y) * 0.5;
+	}
+	else
+	{
+		mouse_x = mx;
+		mouse_y = my;
+	}
+	old_mouse_x = mx;
+	old_mouse_y = my;
+	mx = my = 0; // clear for next update
+
+	mouse_x *= sensitivity.value;
+	mouse_y *= sensitivity.value;
+
+// add mouse X/Y movement to cmd
+	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+		cmd->sidemove += m_side.value * mouse_x;
+	else
+		cl.viewangles[YAW] -= m_yaw.value * mouse_x;
+	
+	if (in_mlook.state & 1)
+		V_StopPitchDrift ();
+		
+	if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
+	{
+		cl.viewangles[PITCH] += m_pitch.value * mouse_y;
+		if (cl.viewangles[PITCH] > 80)
+			cl.viewangles[PITCH] = 80;
+		if (cl.viewangles[PITCH] < -70)
+			cl.viewangles[PITCH] = -70;
+	}
+	else
+	{
+		if ((in_strafe.state & 1) && noclip_anglehack)
+			cmd->upmove -= m_forward.value * mouse_y;
+		else
+			cmd->forwardmove -= m_forward.value * mouse_y;
+	}
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+	IN_MouseMove(cmd);
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/gl_vidlinuxglx.c b/apps/plugins/sdl/progs/quake/gl_vidlinuxglx.c
new file mode 100644
index 0000000..400adb4
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/gl_vidlinuxglx.c
@@ -0,0 +1,997 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 <termios.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/vt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <dlfcn.h>
+
+#include "quakedef.h"
+
+#include <GL/glx.h>
+
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+
+#include <X11/extensions/xf86dga.h>
+#include <X11/extensions/xf86vmode.h>
+
+#define WARP_WIDTH              320
+#define WARP_HEIGHT             200
+
+static Display *dpy = NULL;
+static int scrnum;
+static Window win;
+static GLXContext ctx = NULL;
+
+#define KEY_MASK (KeyPressMask | KeyReleaseMask)
+#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
+		    PointerMotionMask | ButtonMotionMask )
+#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask )
+
+
+unsigned short	d_8to16table[256];
+unsigned		d_8to24table[256];
+unsigned char	d_15to8table[65536];
+
+cvar_t	vid_mode = {"vid_mode","0",false};
+ 
+static qboolean        mouse_avail;
+static qboolean        mouse_active;
+static int   mx, my;
+static int	old_mouse_x, old_mouse_y;
+
+static cvar_t in_mouse = {"in_mouse", "1", false};
+static cvar_t in_dgamouse = {"in_dgamouse", "1", false};
+static cvar_t m_filter = {"m_filter", "0"};
+
+qboolean dgamouse = false;
+qboolean vidmode_ext = false;
+
+static int win_x, win_y;
+
+static int scr_width, scr_height;
+
+static XF86VidModeModeInfo **vidmodes;
+static int default_dotclock_vidmode;
+static int num_vidmodes;
+static qboolean vidmode_active = false;
+
+/*-----------------------------------------------------------------------*/
+
+//int		texture_mode = GL_NEAREST;
+//int		texture_mode = GL_NEAREST_MIPMAP_NEAREST;
+//int		texture_mode = GL_NEAREST_MIPMAP_LINEAR;
+int		texture_mode = GL_LINEAR;
+//int		texture_mode = GL_LINEAR_MIPMAP_NEAREST;
+//int		texture_mode = GL_LINEAR_MIPMAP_LINEAR;
+
+int		texture_extension_number = 1;
+
+float		gldepthmin, gldepthmax;
+
+cvar_t	gl_ztrick = {"gl_ztrick","1"};
+
+const char *gl_vendor;
+const char *gl_renderer;
+const char *gl_version;
+const char *gl_extensions;
+
+void (*qglColorTableEXT) (int, int, int, int, int, const void*);
+void (*qgl3DfxSetPaletteEXT) (GLuint *);
+
+static float vid_gamma = 1.0;
+
+qboolean is8bit = false;
+qboolean isPermedia = false;
+qboolean gl_mtexable = false;
+
+/*-----------------------------------------------------------------------*/
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
+{
+}
+
+void D_EndDirectRect (int x, int y, int width, int height)
+{
+}
+
+static int XLateKey(XKeyEvent *ev)
+{
+
+	int key;
+	char buf[64];
+	KeySym keysym;
+
+	key = 0;
+
+	XLookupString(ev, buf, sizeof buf, &keysym, 0);
+
+	switch(keysym)
+	{
+		case XK_KP_Page_Up:	 
+		case XK_Page_Up:	 key = K_PGUP; break;
+
+		case XK_KP_Page_Down: 
+		case XK_Page_Down:	 key = K_PGDN; break;
+
+		case XK_KP_Home: 
+		case XK_Home:	 key = K_HOME; break;
+
+		case XK_KP_End:  
+		case XK_End:	 key = K_END; break;
+
+		case XK_KP_Left: 
+		case XK_Left:	 key = K_LEFTARROW; break;
+
+		case XK_KP_Right: 
+		case XK_Right:	key = K_RIGHTARROW;		break;
+
+		case XK_KP_Down: 
+		case XK_Down:	 key = K_DOWNARROW; break;
+
+		case XK_KP_Up:   
+		case XK_Up:		 key = K_UPARROW;	 break;
+
+		case XK_Escape: key = K_ESCAPE;		break;
+
+		case XK_KP_Enter: 
+		case XK_Return: key = K_ENTER;		 break;
+
+		case XK_Tab:		key = K_TAB;			 break;
+
+		case XK_F1:		 key = K_F1;				break;
+
+		case XK_F2:		 key = K_F2;				break;
+
+		case XK_F3:		 key = K_F3;				break;
+
+		case XK_F4:		 key = K_F4;				break;
+
+		case XK_F5:		 key = K_F5;				break;
+
+		case XK_F6:		 key = K_F6;				break;
+
+		case XK_F7:		 key = K_F7;				break;
+
+		case XK_F8:		 key = K_F8;				break;
+
+		case XK_F9:		 key = K_F9;				break;
+
+		case XK_F10:		key = K_F10;			 break;
+
+		case XK_F11:		key = K_F11;			 break;
+
+		case XK_F12:		key = K_F12;			 break;
+
+		case XK_BackSpace: key = K_BACKSPACE; break;
+
+		case XK_KP_Delete: 
+		case XK_Delete: key = K_DEL; break;
+
+		case XK_Pause:	key = K_PAUSE;		 break;
+
+		case XK_Shift_L:
+		case XK_Shift_R:	key = K_SHIFT;		break;
+
+		case XK_Execute: 
+		case XK_Control_L: 
+		case XK_Control_R:	key = K_CTRL;		 break;
+
+		case XK_Alt_L:	
+		case XK_Meta_L: 
+		case XK_Alt_R:	
+		case XK_Meta_R: key = K_ALT;			break;
+
+		case XK_KP_Begin: key = '5';	break;
+
+		case XK_KP_Insert: 
+		case XK_Insert:key = K_INS; break;
+
+		case XK_KP_Multiply: key = '*'; break;
+		case XK_KP_Add:  key = '+'; break;
+		case XK_KP_Subtract: key = '-'; break;
+		case XK_KP_Divide: key = '/'; break;
+
+#if 0
+		case 0x021: key = '1';break;/* [!] */
+		case 0x040: key = '2';break;/* [@] */
+		case 0x023: key = '3';break;/* [#] */
+		case 0x024: key = '4';break;/* [$] */
+		case 0x025: key = '5';break;/* [%] */
+		case 0x05e: key = '6';break;/* [^] */
+		case 0x026: key = '7';break;/* [&] */
+		case 0x02a: key = '8';break;/* [*] */
+		case 0x028: key = '9';;break;/* [(] */
+		case 0x029: key = '0';break;/* [)] */
+		case 0x05f: key = '-';break;/* [_] */
+		case 0x02b: key = '=';break;/* [+] */
+		case 0x07c: key = '\'';break;/* [|] */
+		case 0x07d: key = '[';break;/* [}] */
+		case 0x07b: key = ']';break;/* [{] */
+		case 0x022: key = '\'';break;/* ["] */
+		case 0x03a: key = ';';break;/* [:] */
+		case 0x03f: key = '/';break;/* [?] */
+		case 0x03e: key = '.';break;/* [>] */
+		case 0x03c: key = ',';break;/* [<] */
+#endif
+
+		default:
+			key = *(unsigned char*)buf;
+			if (key >= 'A' && key <= 'Z')
+				key = key - 'A' + 'a';
+			break;
+	} 
+
+	return key;
+}
+
+static Cursor CreateNullCursor(Display *display, Window root)
+{
+    Pixmap cursormask; 
+    XGCValues xgc;
+    GC gc;
+    XColor dummycolour;
+    Cursor cursor;
+
+    cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
+    xgc.function = GXclear;
+    gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
+    XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
+    dummycolour.pixel = 0;
+    dummycolour.red = 0;
+    dummycolour.flags = 04;
+    cursor = XCreatePixmapCursor(display, cursormask, cursormask,
+          &dummycolour,&dummycolour, 0,0);
+    XFreePixmap(display,cursormask);
+    XFreeGC(display,gc);
+    return cursor;
+}
+
+static void install_grabs(void)
+{
+
+// inviso cursor
+	XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
+
+	XGrabPointer(dpy, win,
+				 True,
+				 0,
+				 GrabModeAsync, GrabModeAsync,
+				 win,
+				 None,
+				 CurrentTime);
+
+	if (in_dgamouse.value) {
+		int MajorVersion, MinorVersion;
+
+		if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) { 
+			// unable to query, probalby not supported
+			Con_Printf( "Failed to detect XF86DGA Mouse\n" );
+			in_dgamouse.value = 0;
+		} else {
+			dgamouse = true;
+			XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
+			XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
+		}
+	} else {
+		XWarpPointer(dpy, None, win,
+					 0, 0, 0, 0,
+					 vid.width / 2, vid.height / 2);
+	}
+
+	XGrabKeyboard(dpy, win,
+				  False,
+				  GrabModeAsync, GrabModeAsync,
+				  CurrentTime);
+
+	mouse_active = true;
+
+//	XSync(dpy, True);
+}
+
+static void uninstall_grabs(void)
+{
+	if (!dpy || !win)
+		return;
+
+	if (dgamouse) {
+		dgamouse = false;
+		XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
+	}
+
+	XUngrabPointer(dpy, CurrentTime);
+	XUngrabKeyboard(dpy, CurrentTime);
+
+// inviso cursor
+	XUndefineCursor(dpy, win);
+
+	mouse_active = false;
+}
+
+static void HandleEvents(void)
+{
+	XEvent event;
+	KeySym ks;
+	int b;
+	qboolean dowarp = false;
+	int mwx = vid.width/2;
+	int mwy = vid.height/2;
+
+	if (!dpy)
+		return;
+
+	while (XPending(dpy)) {
+		XNextEvent(dpy, &event);
+
+		switch (event.type) {
+		case KeyPress:
+		case KeyRelease:
+			Key_Event(XLateKey(&event.xkey), event.type == KeyPress);
+			break;
+
+		case MotionNotify:
+			if (mouse_active) {
+				if (dgamouse) {
+					mx += (event.xmotion.x + win_x) * 2;
+					my += (event.xmotion.y + win_y) * 2;
+				} 
+				else 
+				{
+					mx += ((int)event.xmotion.x - mwx) * 2;
+					my += ((int)event.xmotion.y - mwy) * 2;
+					mwx = event.xmotion.x;
+					mwy = event.xmotion.y;
+
+					if (mx || my)
+						dowarp = true;
+				}
+			}
+			break;
+
+			break;
+
+		case ButtonPress:
+			b=-1;
+			if (event.xbutton.button == 1)
+				b = 0;
+			else if (event.xbutton.button == 2)
+				b = 2;
+			else if (event.xbutton.button == 3)
+				b = 1;
+			if (b>=0)
+				Key_Event(K_MOUSE1 + b, true);
+			break;
+
+		case ButtonRelease:
+			b=-1;
+			if (event.xbutton.button == 1)
+				b = 0;
+			else if (event.xbutton.button == 2)
+				b = 2;
+			else if (event.xbutton.button == 3)
+				b = 1;
+			if (b>=0)
+				Key_Event(K_MOUSE1 + b, false);
+			break;
+
+		case CreateNotify :
+			win_x = event.xcreatewindow.x;
+			win_y = event.xcreatewindow.y;
+			break;
+
+		case ConfigureNotify :
+			win_x = event.xconfigure.x;
+			win_y = event.xconfigure.y;
+			break;
+		}
+	}
+
+	if (dowarp) {
+		/* move the mouse to the window center again */
+		XWarpPointer(dpy, None, win, 0, 0, 0, 0, vid.width / 2, vid.height / 2);
+	}
+
+}
+
+static void IN_DeactivateMouse( void ) 
+{
+	if (!mouse_avail || !dpy || !win)
+		return;
+
+	if (mouse_active) {
+		uninstall_grabs();
+		mouse_active = false;
+	}
+}
+
+static void IN_ActivateMouse( void ) 
+{
+	if (!mouse_avail || !dpy || !win)
+		return;
+
+	if (!mouse_active) {
+		mx = my = 0; // don't spazz
+		install_grabs();
+		mouse_active = true;
+	}
+}
+
+
+void VID_Shutdown(void)
+{
+	if (!ctx || !dpy)
+		return;
+	IN_DeactivateMouse();
+	if (dpy) {
+		if (ctx)
+			glXDestroyContext(dpy, ctx);
+		if (win)
+			XDestroyWindow(dpy, win);
+		if (vidmode_active)
+			XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
+		XCloseDisplay(dpy);
+	}
+	vidmode_active = false;
+	dpy = NULL;
+	win = 0;
+	ctx = NULL;
+}
+
+void signal_handler(int sig)
+{
+	printf("Received signal %d, exiting...\n", sig);
+	Sys_Quit();
+	exit(0);
+}
+
+void InitSig(void)
+{
+	signal(SIGHUP, signal_handler);
+	signal(SIGINT, signal_handler);
+	signal(SIGQUIT, signal_handler);
+	signal(SIGILL, signal_handler);
+	signal(SIGTRAP, signal_handler);
+	signal(SIGIOT, signal_handler);
+	signal(SIGBUS, signal_handler);
+	signal(SIGFPE, signal_handler);
+	signal(SIGSEGV, signal_handler);
+	signal(SIGTERM, signal_handler);
+}
+
+void VID_ShiftPalette(unsigned char *p)
+{
+//	VID_SetPalette(p);
+}
+
+void	VID_SetPalette (unsigned char *palette)
+{
+	byte	*pal;
+	unsigned r,g,b;
+	unsigned v;
+	int     r1,g1,b1;
+	int		j,k,l,m;
+	unsigned short i;
+	unsigned	*table;
+	FILE *f;
+	char s[255];
+	int dist, bestdist;
+
+//
+// 8 8 8 encoding
+//
+	pal = palette;
+	table = d_8to24table;
+	for (i=0 ; i<256 ; i++)
+	{
+		r = pal[0];
+		g = pal[1];
+		b = pal[2];
+		pal += 3;
+		
+		v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
+		*table++ = v;
+	}
+	d_8to24table[255] &= 0xffffff;	// 255 is transparent
+
+	for (i=0; i < (1<<15); i++) {
+		/* Maps
+		000000000000000
+		000000000011111 = Red  = 0x1F
+		000001111100000 = Blue = 0x03E0
+		111110000000000 = Grn  = 0x7C00
+		*/
+		r = ((i & 0x1F) << 3)+4;
+		g = ((i & 0x03E0) >> 2)+4;
+		b = ((i & 0x7C00) >> 7)+4;
+		pal = (unsigned char *)d_8to24table;
+		for (v=0,k=0,bestdist=10000*10000; v<256; v++,pal+=4) {
+			r1 = (int)r - (int)pal[0];
+			g1 = (int)g - (int)pal[1];
+			b1 = (int)b - (int)pal[2];
+			dist = (r1*r1)+(g1*g1)+(b1*b1);
+			if (dist < bestdist) {
+				k=v;
+				bestdist = dist;
+			}
+		}
+		d_15to8table[i]=k;
+	}
+}
+
+void CheckMultiTextureExtensions(void) 
+{
+	void *prjobj;
+
+	if (strstr(gl_extensions, "GL_SGIS_multitexture ") && !COM_CheckParm("-nomtex")) {
+		Con_Printf("Found GL_SGIS_multitexture...\n");
+
+		if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL) {
+			Con_Printf("Unable to open symbol list for main program.\n");
+			return;
+		}
+
+		qglMTexCoord2fSGIS = (void *) dlsym(prjobj, "glMTexCoord2fSGIS");
+		qglSelectTextureSGIS = (void *) dlsym(prjobj, "glSelectTextureSGIS");
+
+		if (qglMTexCoord2fSGIS && qglSelectTextureSGIS) {
+			Con_Printf("Multitexture extensions found.\n");
+			gl_mtexable = true;
+		} else
+			Con_Printf("Symbol not found, disabled.\n");
+
+		dlclose(prjobj);
+	}
+}
+
+/*
+===============
+GL_Init
+===============
+*/
+void GL_Init (void)
+{
+	gl_vendor = glGetString (GL_VENDOR);
+	Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
+	gl_renderer = glGetString (GL_RENDERER);
+	Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
+
+	gl_version = glGetString (GL_VERSION);
+	Con_Printf ("GL_VERSION: %s\n", gl_version);
+	gl_extensions = glGetString (GL_EXTENSIONS);
+	Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
+
+//	Con_Printf ("%s %s\n", gl_renderer, gl_version);
+
+	CheckMultiTextureExtensions ();
+
+	glClearColor (1,0,0,0);
+	glCullFace(GL_FRONT);
+	glEnable(GL_TEXTURE_2D);
+
+	glEnable(GL_ALPHA_TEST);
+	glAlphaFunc(GL_GREATER, 0.666);
+
+	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+	glShadeModel (GL_FLAT);
+
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+//	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+}
+
+/*
+=================
+GL_BeginRendering
+
+=================
+*/
+void GL_BeginRendering (int *x, int *y, int *width, int *height)
+{
+	extern cvar_t gl_clear;
+
+	*x = *y = 0;
+	*width = scr_width;
+	*height = scr_height;
+
+//    if (!wglMakeCurrent( maindc, baseRC ))
+//		Sys_Error ("wglMakeCurrent failed");
+
+//	glViewport (*x, *y, *width, *height);
+}
+
+
+void GL_EndRendering (void)
+{
+	glFlush();
+	glXSwapBuffers(dpy, win);
+}
+
+qboolean VID_Is8bit(void)
+{
+	return is8bit;
+}
+
+void VID_Init8bitPalette(void) 
+{
+	// Check for 8bit Extensions and initialize them.
+	int i;
+	void *prjobj;
+
+	if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL) {
+		Con_Printf("Unable to open symbol list for main program.\n");
+		return;
+	}
+
+	if (strstr(gl_extensions, "3DFX_set_global_palette") &&
+		(qgl3DfxSetPaletteEXT = dlsym(prjobj, "gl3DfxSetPaletteEXT")) != NULL) {
+		GLubyte table[256][4];
+		char *oldpal;
+
+		Con_SafePrintf("8-bit GL extensions enabled.\n");
+		glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
+		oldpal = (char *) d_8to24table; //d_8to24table3dfx;
+		for (i=0;i<256;i++) {
+			table[i][2] = *oldpal++;
+			table[i][1] = *oldpal++;
+			table[i][0] = *oldpal++;
+			table[i][3] = 255;
+			oldpal++;
+		}
+		qgl3DfxSetPaletteEXT((GLuint *)table);
+		is8bit = true;
+
+	} else if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") &&
+		(qglColorTableEXT = dlsym(prjobj, "glColorTableEXT")) != NULL) {
+		char thePalette[256*3];
+		char *oldPalette, *newPalette;
+
+		Con_SafePrintf("8-bit GL extensions enabled.\n");
+		glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
+		oldPalette = (char *) d_8to24table; //d_8to24table3dfx;
+		newPalette = thePalette;
+		for (i=0;i<256;i++) {
+			*newPalette++ = *oldPalette++;
+			*newPalette++ = *oldPalette++;
+			*newPalette++ = *oldPalette++;
+			oldPalette++;
+		}
+		qglColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette);
+		is8bit = true;
+	}
+	
+	dlclose(prjobj);
+}
+
+static void Check_Gamma (unsigned char *pal)
+{
+	float	f, inf;
+	unsigned char	palette[768];
+	int		i;
+
+	if ((i = COM_CheckParm("-gamma")) == 0) {
+		if ((gl_renderer && strstr(gl_renderer, "Voodoo")) ||
+			(gl_vendor && strstr(gl_vendor, "3Dfx")))
+			vid_gamma = 1;
+		else
+			vid_gamma = 0.7; // default to 0.7 on non-3dfx hardware
+	} else
+		vid_gamma = Q_atof(com_argv[i+1]);
+
+	for (i=0 ; i<768 ; i++)
+	{
+		f = pow ( (pal[i]+1)/256.0 , vid_gamma );
+		inf = f*255 + 0.5;
+		if (inf < 0)
+			inf = 0;
+		if (inf > 255)
+			inf = 255;
+		palette[i] = inf;
+	}
+
+	memcpy (pal, palette, sizeof(palette));
+}
+
+void VID_Init(unsigned char *palette)
+{
+	int i;
+	int attrib[] = {
+		GLX_RGBA,
+		GLX_RED_SIZE, 1,
+		GLX_GREEN_SIZE, 1,
+		GLX_BLUE_SIZE, 1,
+		GLX_DOUBLEBUFFER,
+		GLX_DEPTH_SIZE, 1,
+		None
+	};
+	char	gldir[MAX_OSPATH];
+	int width = 640, height = 480;
+	XSetWindowAttributes attr;
+	unsigned long mask;
+	Window root;
+	XVisualInfo *visinfo;
+	qboolean fullscreen = true;
+	int MajorVersion, MinorVersion;
+	int actualWidth, actualHeight;
+
+	Cvar_RegisterVariable (&vid_mode);
+	Cvar_RegisterVariable (&in_mouse);
+	Cvar_RegisterVariable (&in_dgamouse);
+	Cvar_RegisterVariable (&m_filter);
+	Cvar_RegisterVariable (&gl_ztrick);
+	
+	vid.maxwarpwidth = WARP_WIDTH;
+	vid.maxwarpheight = WARP_HEIGHT;
+	vid.colormap = host_colormap;
+	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+
+// interpret command-line params
+
+// set vid parameters
+	if ((i = COM_CheckParm("-window")) != 0)
+		fullscreen = false;
+
+	if ((i = COM_CheckParm("-width")) != 0)
+		width = atoi(com_argv[i+1]);
+
+	if ((i = COM_CheckParm("-height")) != 0)
+		height = atoi(com_argv[i+1]);
+
+	if ((i = COM_CheckParm("-conwidth")) != 0)
+		vid.conwidth = Q_atoi(com_argv[i+1]);
+	else
+		vid.conwidth = 640;
+
+	vid.conwidth &= 0xfff8; // make it a multiple of eight
+
+	if (vid.conwidth < 320)
+		vid.conwidth = 320;
+
+	// pick a conheight that matches with correct aspect
+	vid.conheight = vid.conwidth*3 / 4;
+
+	if ((i = COM_CheckParm("-conheight")) != 0)
+		vid.conheight = Q_atoi(com_argv[i+1]);
+	if (vid.conheight < 200)
+		vid.conheight = 200;
+
+	if (!(dpy = XOpenDisplay(NULL))) {
+		fprintf(stderr, "Error couldn't open the X display\n");
+		exit(1);
+	}
+
+	scrnum = DefaultScreen(dpy);
+	root = RootWindow(dpy, scrnum);
+
+	// Get video mode list
+	MajorVersion = MinorVersion = 0;
+	if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) { 
+		vidmode_ext = false;
+	} else {
+		Con_Printf("Using XFree86-VidModeExtension Version %d.%d\n", MajorVersion, MinorVersion);
+		vidmode_ext = true;
+	}
+
+	visinfo = glXChooseVisual(dpy, scrnum, attrib);
+	if (!visinfo) {
+		fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n");
+		exit(1);
+	}
+
+	if (vidmode_ext) {
+		int best_fit, best_dist, dist, x, y;
+		
+		XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
+
+		// Are we going fullscreen?  If so, let's change video mode
+		if (fullscreen) {
+			best_dist = 9999999;
+			best_fit = -1;
+
+			for (i = 0; i < num_vidmodes; i++) {
+				if (width > vidmodes[i]->hdisplay ||
+					height > vidmodes[i]->vdisplay)
+					continue;
+
+				x = width - vidmodes[i]->hdisplay;
+				y = height - vidmodes[i]->vdisplay;
+				dist = (x * x) + (y * y);
+				if (dist < best_dist) {
+					best_dist = dist;
+					best_fit = i;
+				}
+			}
+
+			if (best_fit != -1) {
+				actualWidth = vidmodes[best_fit]->hdisplay;
+				actualHeight = vidmodes[best_fit]->vdisplay;
+
+				// change to the mode
+				XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
+				vidmode_active = true;
+
+				// Move the viewport to top left
+				XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
+			} else
+				fullscreen = 0;
+		}
+	}
+
+	/* window attributes */
+	attr.background_pixel = 0;
+	attr.border_pixel = 0;
+	attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
+	attr.event_mask = X_MASK;
+	if (vidmode_active) {
+		mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | 
+			CWEventMask | CWOverrideRedirect;
+		attr.override_redirect = True;
+		attr.backing_store = NotUseful;
+		attr.save_under = False;
+	} else
+		mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
+
+	win = XCreateWindow(dpy, root, 0, 0, width, height,
+						0, visinfo->depth, InputOutput,
+						visinfo->visual, mask, &attr);
+	XMapWindow(dpy, win);
+
+	if (vidmode_active) {
+		XMoveWindow(dpy, win, 0, 0);
+		XRaiseWindow(dpy, win);
+		XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
+		XFlush(dpy);
+		// Move the viewport to top left
+		XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
+	}
+
+	XFlush(dpy);
+
+	ctx = glXCreateContext(dpy, visinfo, NULL, True);
+
+	glXMakeCurrent(dpy, win, ctx);
+
+	scr_width = width;
+	scr_height = height;
+
+	if (vid.conheight > height)
+		vid.conheight = height;
+	if (vid.conwidth > width)
+		vid.conwidth = width;
+	vid.width = vid.conwidth;
+	vid.height = vid.conheight;
+
+	vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
+	vid.numpages = 2;
+
+	InitSig(); // trap evil signals
+
+	GL_Init();
+
+	sprintf (gldir, "%s/glquake", com_gamedir);
+	Sys_mkdir (gldir);
+
+	VID_SetPalette(palette);
+
+	// Check for 3DFX Extensions and initialize them.
+	VID_Init8bitPalette();
+
+	Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
+
+	vid.recalc_refdef = 1;				// force a surface cache flush
+}
+
+void Sys_SendKeyEvents(void)
+{
+	HandleEvents();
+}
+
+void Force_CenterView_f (void)
+{
+	cl.viewangles[PITCH] = 0;
+}
+
+void IN_Init(void)
+{
+}
+
+void IN_Shutdown(void)
+{
+}
+
+/*
+===========
+IN_Commands
+===========
+*/
+void IN_Commands (void)
+{
+	if (!dpy || !win)
+		return;
+
+	if (vidmode_active || key_dest == key_game)
+		IN_ActivateMouse();
+	else
+		IN_DeactivateMouse ();
+}
+
+/*
+===========
+IN_Move
+===========
+*/
+void IN_MouseMove (usercmd_t *cmd)
+{
+	if (!mouse_avail)
+		return;
+   
+	if (m_filter.value)
+	{
+		mx = (mx + old_mouse_x) * 0.5;
+		my = (my + old_mouse_y) * 0.5;
+	}
+	old_mouse_x = mx;
+	old_mouse_y = my;
+
+	mx *= sensitivity.value;
+	my *= sensitivity.value;
+
+// add mouse X/Y movement to cmd
+	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+		cmd->sidemove += m_side.value * mx;
+	else
+		cl.viewangles[YAW] -= m_yaw.value * mx;
+	
+	if (in_mlook.state & 1)
+		V_StopPitchDrift ();
+		
+	if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
+	{
+		cl.viewangles[PITCH] += m_pitch.value * my;
+		if (cl.viewangles[PITCH] > 80)
+			cl.viewangles[PITCH] = 80;
+		if (cl.viewangles[PITCH] < -70)
+			cl.viewangles[PITCH] = -70;
+	}
+	else
+	{
+		if ((in_strafe.state & 1) && noclip_anglehack)
+			cmd->upmove -= m_forward.value * my;
+		else
+			cmd->forwardmove -= m_forward.value * my;
+	}
+	mx = my = 0;
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+	IN_MouseMove(cmd);
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/host.c b/apps/plugins/sdl/progs/quake/host.c
new file mode 100644
index 0000000..9a5f7c2
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/host.c
@@ -0,0 +1,977 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// host.c -- coordinates spawning and killing of local servers
+
+#include "quakedef.h"
+#include "r_local.h"
+
+/*
+
+A server can allways be started, even if the system started out as a client
+to a remote system.
+
+A client can NOT be started if the system started as a dedicated server.
+
+Memory is cleared / released when a server or client begins, not when they end.
+
+*/
+
+quakeparms_t host_parms;
+
+qboolean	host_initialized;		// true if into command execution
+
+double		host_frametime;
+double		host_time;
+double		realtime;				// without any filtering or bounding
+double		oldrealtime;			// last frame run
+int			host_framecount;
+
+int			host_hunklevel;
+
+int			minimum_memory;
+
+client_t	*host_client;			// current client
+
+jmp_buf 	host_abortserver;
+
+byte		*host_basepal;
+byte		*host_colormap;
+
+cvar_t	host_framerate = {"host_framerate","0"};	// set for slow motion
+cvar_t	host_speeds = {"host_speeds","0"};			// set for running times
+
+cvar_t	sys_ticrate = {"sys_ticrate","0.05"};
+cvar_t	serverprofile = {"serverprofile","0"};
+
+cvar_t	fraglimit = {"fraglimit","0",false,true};
+cvar_t	timelimit = {"timelimit","0",false,true};
+cvar_t	teamplay = {"teamplay","0",false,true};
+
+cvar_t	samelevel = {"samelevel","0"};
+cvar_t	noexit = {"noexit","0",false,true};
+
+#ifdef QUAKE2
+cvar_t	developer = {"developer","1"};	// should be 0 for release!
+#else
+cvar_t	developer = {"developer","0"};
+#endif
+
+cvar_t	skill = {"skill","1"};						// 0 - 3
+cvar_t	deathmatch = {"deathmatch","0"};			// 0, 1, or 2
+cvar_t	coop = {"coop","0"};			// 0 or 1
+
+cvar_t	pausable = {"pausable","1"};
+
+cvar_t	temp1 = {"temp1","0"};
+
+
+/*
+================
+Host_EndGame
+================
+*/
+void Host_EndGame (char *message, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+	
+	va_start (argptr,message);
+	vsprintf (string,message,argptr);
+	va_end (argptr);
+	Con_DPrintf ("Host_EndGame: %s\n",string);
+	
+	if (sv.active)
+		Host_ShutdownServer (false);
+
+	if (cls.state == ca_dedicated)
+		Sys_Error ("Host_EndGame: %s\n",string);	// dedicated servers exit
+	
+	if (cls.demonum != -1)
+		CL_NextDemo ();
+	else
+		CL_Disconnect ();
+
+	longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_Error
+
+This shuts down both the client and server
+================
+*/
+void Host_Error (char *error, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+	static	qboolean inerror = false;
+	
+	if (inerror)
+		Sys_Error ("Host_Error: recursively entered");
+	inerror = true;
+	
+	SCR_EndLoadingPlaque ();		// reenable screen updates
+
+	va_start (argptr,error);
+	vsprintf (string,error,argptr);
+	va_end (argptr);
+	Con_Printf ("Host_Error: %s\n",string);
+	
+	if (sv.active)
+		Host_ShutdownServer (false);
+
+	if (cls.state == ca_dedicated)
+		Sys_Error ("Host_Error: %s\n",string);	// dedicated servers exit
+
+	CL_Disconnect ();
+	cls.demonum = -1;
+
+	inerror = false;
+
+	longjmp (host_abortserver, 1);
+}
+
+/*
+================
+Host_FindMaxClients
+================
+*/
+void	Host_FindMaxClients (void)
+{
+	int		i;
+
+	svs.maxclients = 1;
+		
+	i = COM_CheckParm ("-dedicated");
+	if (i)
+	{
+		cls.state = ca_dedicated;
+		if (i != (com_argc - 1))
+		{
+			svs.maxclients = Q_atoi (com_argv[i+1]);
+		}
+		else
+			svs.maxclients = 8;
+	}
+	else
+		cls.state = ca_disconnected;
+
+	i = COM_CheckParm ("-listen");
+	if (i)
+	{
+		if (cls.state == ca_dedicated)
+			Sys_Error ("Only one of -dedicated or -listen can be specified");
+		if (i != (com_argc - 1))
+			svs.maxclients = Q_atoi (com_argv[i+1]);
+		else
+			svs.maxclients = 8;
+	}
+	if (svs.maxclients < 1)
+		svs.maxclients = 8;
+	else if (svs.maxclients > MAX_SCOREBOARD)
+		svs.maxclients = MAX_SCOREBOARD;
+
+	svs.maxclientslimit = svs.maxclients;
+	if (svs.maxclientslimit < 4)
+		svs.maxclientslimit = 4;
+	svs.clients = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
+
+	if (svs.maxclients > 1)
+		Cvar_SetValue ("deathmatch", 1.0);
+	else
+		Cvar_SetValue ("deathmatch", 0.0);
+}
+
+
+/*
+=======================
+Host_InitLocal
+======================
+*/
+void Host_InitLocal (void)
+{
+	Host_InitCommands ();
+	
+	Cvar_RegisterVariable (&host_framerate);
+	Cvar_RegisterVariable (&host_speeds);
+
+	Cvar_RegisterVariable (&sys_ticrate);
+	Cvar_RegisterVariable (&serverprofile);
+
+	Cvar_RegisterVariable (&fraglimit);
+	Cvar_RegisterVariable (&timelimit);
+	Cvar_RegisterVariable (&teamplay);
+	Cvar_RegisterVariable (&samelevel);
+	Cvar_RegisterVariable (&noexit);
+	Cvar_RegisterVariable (&skill);
+	Cvar_RegisterVariable (&developer);
+	Cvar_RegisterVariable (&deathmatch);
+	Cvar_RegisterVariable (&coop);
+
+	Cvar_RegisterVariable (&pausable);
+
+	Cvar_RegisterVariable (&temp1);
+
+	Host_FindMaxClients ();
+	
+	host_time = 1.0;		// so a think at time 0 won't get called
+}
+
+
+/*
+===============
+Host_WriteConfiguration
+
+Writes key bindings and archived cvars to config.cfg
+===============
+*/
+void Host_WriteConfiguration (void)
+{
+	FILE	*f;
+
+// dedicated servers initialize the host but don't parse and set the
+// config.cfg cvars
+	if (host_initialized & !isDedicated)
+	{
+		f = fopen (va("%s/config.cfg",com_gamedir), "w");
+		if (!f)
+		{
+			Con_Printf ("Couldn't write config.cfg.\n");
+			return;
+		}
+		
+		Key_WriteBindings (f);
+		Cvar_WriteVariables (f);
+
+		fclose (f);
+	}
+}
+
+
+/*
+=================
+SV_ClientPrintf
+
+Sends text across to be displayed 
+FIXME: make this just a stuffed echo?
+=================
+*/
+void SV_ClientPrintf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+	
+	va_start (argptr,fmt);
+	vsprintf (string, fmt,argptr);
+	va_end (argptr);
+	
+	MSG_WriteByte (&host_client->message, svc_print);
+	MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=================
+SV_BroadcastPrintf
+
+Sends text to all active clients
+=================
+*/
+void SV_BroadcastPrintf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+	int			i;
+	
+	va_start (argptr,fmt);
+	vsprintf (string, fmt,argptr);
+	va_end (argptr);
+	
+	for (i=0 ; i<svs.maxclients ; i++)
+		if (svs.clients[i].active && svs.clients[i].spawned)
+		{
+			MSG_WriteByte (&svs.clients[i].message, svc_print);
+			MSG_WriteString (&svs.clients[i].message, string);
+		}
+}
+
+/*
+=================
+Host_ClientCommands
+
+Send text over to the client to be executed
+=================
+*/
+void Host_ClientCommands (char *fmt, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+	
+	va_start (argptr,fmt);
+	vsprintf (string, fmt,argptr);
+	va_end (argptr);
+	
+	MSG_WriteByte (&host_client->message, svc_stufftext);
+	MSG_WriteString (&host_client->message, string);
+}
+
+/*
+=====================
+SV_DropClient
+
+Called when the player is getting totally kicked off the host
+if (crash = true), don't bother sending signofs
+=====================
+*/
+void SV_DropClient (qboolean crash)
+{
+	int		saveSelf;
+	int		i;
+	client_t *client;
+
+	if (!crash)
+	{
+		// send any final messages (don't check for errors)
+		if (NET_CanSendMessage (host_client->netconnection))
+		{
+			MSG_WriteByte (&host_client->message, svc_disconnect);
+			NET_SendMessage (host_client->netconnection, &host_client->message);
+		}
+	
+		if (host_client->edict && host_client->spawned)
+		{
+		// call the prog function for removing a client
+		// this will set the body to a dead frame, among other things
+			saveSelf = pr_global_struct->self;
+			pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
+			PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
+			pr_global_struct->self = saveSelf;
+		}
+
+		Sys_Printf ("Client %s removed\n",host_client->name);
+	}
+
+// break the net connection
+	NET_Close (host_client->netconnection);
+	host_client->netconnection = NULL;
+
+// free the client (the body stays around)
+	host_client->active = false;
+	host_client->name[0] = 0;
+	host_client->old_frags = -999999;
+	net_activeconnections--;
+
+// send notification to all clients
+	for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+	{
+		if (!client->active)
+			continue;
+		MSG_WriteByte (&client->message, svc_updatename);
+		MSG_WriteByte (&client->message, host_client - svs.clients);
+		MSG_WriteString (&client->message, "");
+		MSG_WriteByte (&client->message, svc_updatefrags);
+		MSG_WriteByte (&client->message, host_client - svs.clients);
+		MSG_WriteShort (&client->message, 0);
+		MSG_WriteByte (&client->message, svc_updatecolors);
+		MSG_WriteByte (&client->message, host_client - svs.clients);
+		MSG_WriteByte (&client->message, 0);
+	}
+}
+
+/*
+==================
+Host_ShutdownServer
+
+This only happens at the end of a game, not between levels
+==================
+*/
+void Host_ShutdownServer(qboolean crash)
+{
+	int		i;
+	int		count;
+	sizebuf_t	buf;
+	char		message[4];
+	double	start;
+
+	if (!sv.active)
+		return;
+
+	sv.active = false;
+
+// stop all client sounds immediately
+	if (cls.state == ca_connected)
+		CL_Disconnect ();
+
+// flush any pending messages - like the score!!!
+	start = Sys_FloatTime();
+	do
+	{
+		count = 0;
+		for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+		{
+			if (host_client->active && host_client->message.cursize)
+			{
+				if (NET_CanSendMessage (host_client->netconnection))
+				{
+					NET_SendMessage(host_client->netconnection, &host_client->message);
+					SZ_Clear (&host_client->message);
+				}
+				else
+				{
+					NET_GetMessage(host_client->netconnection);
+					count++;
+				}
+			}
+		}
+		if ((Sys_FloatTime() - start) > 3.0)
+			break;
+	}
+	while (count);
+
+// make sure all the clients know we're disconnecting
+	buf.data = message;
+	buf.maxsize = 4;
+	buf.cursize = 0;
+	MSG_WriteByte(&buf, svc_disconnect);
+	count = NET_SendToAll(&buf, 5);
+	if (count)
+		Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
+
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+		if (host_client->active)
+			SV_DropClient(crash);
+
+//
+// clear structures
+//
+	memset (&sv, 0, sizeof(sv));
+	memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
+}
+
+
+/*
+================
+Host_ClearMemory
+
+This clears all the memory used by both the client and server, but does
+not reinitialize anything.
+================
+*/
+void Host_ClearMemory (void)
+{
+	Con_DPrintf ("Clearing memory\n");
+	D_FlushCaches ();
+	Mod_ClearAll ();
+	if (host_hunklevel)
+		Hunk_FreeToLowMark (host_hunklevel);
+
+	cls.signon = 0;
+	memset (&sv, 0, sizeof(sv));
+	memset (&cl, 0, sizeof(cl));
+}
+
+
+//============================================================================
+
+
+/*
+===================
+Host_FilterTime
+
+Returns false if the time is too short to run a frame
+===================
+*/
+qboolean Host_FilterTime (float time)
+{
+	realtime += time;
+
+	if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)
+		return false;		// framerate is too high
+
+	host_frametime = realtime - oldrealtime;
+	oldrealtime = realtime;
+
+	if (host_framerate.value > 0)
+		host_frametime = host_framerate.value;
+	else
+	{	// don't allow really long or short frames
+		if (host_frametime > 0.1)
+			host_frametime = 0.1;
+		if (host_frametime < 0.001)
+			host_frametime = 0.001;
+	}
+	
+	return true;
+}
+
+
+/*
+===================
+Host_GetConsoleCommands
+
+Add them exactly as if they had been typed at the console
+===================
+*/
+void Host_GetConsoleCommands (void)
+{
+	char	*cmd;
+
+	while (1)
+	{
+		cmd = Sys_ConsoleInput ();
+		if (!cmd)
+			break;
+		Cbuf_AddText (cmd);
+	}
+}
+
+
+/*
+==================
+Host_ServerFrame
+
+==================
+*/
+#ifdef FPS_20
+
+void _Host_ServerFrame (void)
+{
+// run the world state	
+	pr_global_struct->frametime = host_frametime;
+
+// read client messages
+	SV_RunClients ();
+	
+// move things around and think
+// always pause in single player if in console or menus
+	if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+		SV_Physics ();
+}
+
+void Host_ServerFrame (void)
+{
+	float	save_host_frametime;
+	float	temp_host_frametime;
+
+// run the world state	
+	pr_global_struct->frametime = host_frametime;
+
+// set the time and clear the general datagram
+	SV_ClearDatagram ();
+	
+// check for new clients
+	SV_CheckForNewClients ();
+
+	temp_host_frametime = save_host_frametime = host_frametime;
+	while(temp_host_frametime > (1.0/72.0))
+	{
+		if (temp_host_frametime > 0.05)
+			host_frametime = 0.05;
+		else
+			host_frametime = temp_host_frametime;
+		temp_host_frametime -= host_frametime;
+		_Host_ServerFrame ();
+	}
+	host_frametime = save_host_frametime;
+
+// send all messages to the clients
+	SV_SendClientMessages ();
+}
+
+#else
+
+void Host_ServerFrame (void)
+{
+// run the world state	
+	pr_global_struct->frametime = host_frametime;
+
+// set the time and clear the general datagram
+	SV_ClearDatagram ();
+	
+// check for new clients
+	SV_CheckForNewClients ();
+
+// read client messages
+	SV_RunClients ();
+	
+// move things around and think
+// always pause in single player if in console or menus
+	if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+		SV_Physics ();
+
+// send all messages to the clients
+	SV_SendClientMessages ();
+}
+
+#endif
+
+
+/*
+==================
+Host_Frame
+
+Runs all active servers
+==================
+*/
+void _Host_Frame (float time)
+{
+	static double		time1 = 0;
+	static double		time2 = 0;
+	static double		time3 = 0;
+	int			pass1, pass2, pass3;
+
+	if (setjmp (host_abortserver) )
+		return;			// something bad happened, or the server disconnected
+
+// keep the random time dependent
+	rand ();
+	
+// decide the simulation time
+	if (!Host_FilterTime (time))
+		return;			// don't run too fast, or packets will flood out
+		
+// get new key events
+	Sys_SendKeyEvents ();
+
+// allow mice or other external controllers to add commands
+	IN_Commands ();
+
+// process console commands
+	Cbuf_Execute ();
+
+	NET_Poll();
+
+// if running the server locally, make intentions now
+	if (sv.active)
+		CL_SendCmd ();
+	
+//-------------------
+//
+// server operations
+//
+//-------------------
+
+// check for commands typed to the host
+	Host_GetConsoleCommands ();
+	
+	if (sv.active)
+		Host_ServerFrame ();
+
+//-------------------
+//
+// client operations
+//
+//-------------------
+
+// if running the server remotely, send intentions now after
+// the incoming messages have been read
+	if (!sv.active)
+		CL_SendCmd ();
+
+	host_time += host_frametime;
+
+// fetch results from server
+	if (cls.state == ca_connected)
+	{
+		CL_ReadFromServer ();
+	}
+
+// update video
+	if (host_speeds.value)
+		time1 = Sys_FloatTime ();
+		
+	SCR_UpdateScreen ();
+
+	if (host_speeds.value)
+		time2 = Sys_FloatTime ();
+		
+// update audio
+	if (cls.signon == SIGNONS)
+	{
+		S_Update (r_origin, vpn, vright, vup);
+		CL_DecayLights ();
+	}
+	else
+		S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
+	
+	CDAudio_Update();
+
+	if (host_speeds.value)
+	{
+		pass1 = (time1 - time3)*1000;
+		time3 = Sys_FloatTime ();
+		pass2 = (time2 - time1)*1000;
+		pass3 = (time3 - time2)*1000;
+		Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
+					pass1+pass2+pass3, pass1, pass2, pass3);
+	}
+	
+	host_framecount++;
+}
+
+void Host_Frame (float time)
+{
+	double	time1, time2;
+	static double	timetotal;
+	static int		timecount;
+	int		i, c, m;
+
+	if (!serverprofile.value)
+	{
+		_Host_Frame (time);
+		return;
+	}
+	
+	time1 = Sys_FloatTime ();
+	_Host_Frame (time);
+	time2 = Sys_FloatTime ();	
+	
+	timetotal += time2 - time1;
+	timecount++;
+	
+	if (timecount < 1000)
+		return;
+
+	m = timetotal*1000/timecount;
+	timecount = 0;
+	timetotal = 0;
+	c = 0;
+	for (i=0 ; i<svs.maxclients ; i++)
+	{
+		if (svs.clients[i].active)
+			c++;
+	}
+
+	Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
+}
+
+//============================================================================
+
+
+extern int vcrFile;
+#define	VCR_SIGNATURE	0x56435231
+// "VCR1"
+
+void Host_InitVCR (quakeparms_t *parms)
+{
+	int		i, len, n;
+	char	*p;
+	
+	if (COM_CheckParm("-playback"))
+	{
+		if (com_argc != 2)
+			Sys_Error("No other parameters allowed with -playback\n");
+
+		Sys_FileOpenRead("quake.vcr", &vcrFile);
+		if (vcrFile == -1)
+			Sys_Error("playback file not found\n");
+
+		Sys_FileRead (vcrFile, &i, sizeof(int));
+		if (i != VCR_SIGNATURE)
+			Sys_Error("Invalid signature in vcr file\n");
+
+		Sys_FileRead (vcrFile, &com_argc, sizeof(int));
+		com_argv = malloc(com_argc * sizeof(char *));
+		com_argv[0] = parms->argv[0];
+		for (i = 0; i < com_argc; i++)
+		{
+			Sys_FileRead (vcrFile, &len, sizeof(int));
+			p = malloc(len);
+			Sys_FileRead (vcrFile, p, len);
+			com_argv[i+1] = p;
+		}
+		com_argc++; /* add one for arg[0] */
+		parms->argc = com_argc;
+		parms->argv = com_argv;
+	}
+
+	if ( (n = COM_CheckParm("-record")) != 0)
+	{
+		vcrFile = Sys_FileOpenWrite("quake.vcr");
+
+		i = VCR_SIGNATURE;
+		Sys_FileWrite(vcrFile, &i, sizeof(int));
+		i = com_argc - 1;
+		Sys_FileWrite(vcrFile, &i, sizeof(int));
+		for (i = 1; i < com_argc; i++)
+		{
+			if (i == n)
+			{
+				len = 10;
+				Sys_FileWrite(vcrFile, &len, sizeof(int));
+				Sys_FileWrite(vcrFile, "-playback", len);
+				continue;
+			}
+			len = Q_strlen(com_argv[i]) + 1;
+			Sys_FileWrite(vcrFile, &len, sizeof(int));
+			Sys_FileWrite(vcrFile, com_argv[i], len);
+		}
+	}
+	
+}
+
+/*
+====================
+Host_Init
+====================
+*/
+void Host_Init (quakeparms_t *parms)
+{
+
+	if (standard_quake)
+		minimum_memory = MINIMUM_MEMORY;
+	else
+		minimum_memory = MINIMUM_MEMORY_LEVELPAK;
+
+	if (COM_CheckParm ("-minmemory"))
+		parms->memsize = minimum_memory;
+
+	host_parms = *parms;
+
+	if (parms->memsize < minimum_memory)
+		Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
+
+	com_argc = parms->argc;
+	com_argv = parms->argv;
+
+        //printf("init 1");
+        
+	Memory_Init (parms->membase, parms->memsize);
+        //printf("init 2");
+	Cbuf_Init ();
+        //printf("init 3");
+	Cmd_Init ();
+        //printf("init 4");	
+	V_Init ();
+        //printf("init 5");
+	Chase_Init ();
+        //printf("init 6");
+	Host_InitVCR (parms);
+        //printf("init 7");
+	COM_Init (parms->basedir);
+        //printf("init 8");
+	Host_InitLocal ();
+        //printf("init 9");
+	W_LoadWadFile ("gfx.wad");
+        //printf("init 10");
+	Key_Init ();
+	Con_Init ();	
+	M_Init ();	
+	PR_Init ();
+	Mod_Init ();
+	NET_Init ();
+        //printf("init 11");
+	SV_Init ();
+        //printf("init 12");
+
+	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+	Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
+	
+	R_InitTextures ();		// needed even for dedicated servers
+ 
+        //printf("init 13");
+	if (cls.state != ca_dedicated)
+	{
+		host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
+		if (!host_basepal)
+			Sys_Error ("Couldn't load gfx/palette.lmp");
+		host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
+		if (!host_colormap)
+			Sys_Error ("Couldn't load gfx/colormap.lmp");
+
+#ifndef _WIN32 // on non win32, mouse comes before video for security reasons
+		IN_Init ();
+#endif
+		VID_Init (host_basepal);
+
+		Draw_Init ();
+		SCR_Init ();
+		R_Init ();
+#ifndef	_WIN32
+	// on Win32, sound initialization has to come before video initialization, so we
+	// can put up a popup if the sound hardware is in use
+		S_Init ();
+#else
+
+#ifdef	GLQUAKE
+	// FIXME: doesn't use the new one-window approach yet
+		S_Init ();
+#endif
+
+#endif	// _WIN32
+		CDAudio_Init ();
+		Sbar_Init ();
+		CL_Init ();
+#ifdef _WIN32 // on non win32, mouse comes before video for security reasons
+		IN_Init ();
+#endif
+	}
+
+	Cbuf_InsertText ("exec quake.rc\n");
+
+	Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
+	host_hunklevel = Hunk_LowMark ();
+
+	host_initialized = true;
+	
+	Sys_Printf ("========Quake Initialized=========\n");
+
+        extern int enable_printf;
+        enable_printf = 0;
+}
+
+
+/*
+===============
+Host_Shutdown
+
+FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
+to run quit through here before the final handoff to the sys code.
+===============
+*/
+void Host_Shutdown(void)
+{
+	static qboolean isdown = false;
+	
+	if (isdown)
+	{
+		printf ("recursive shutdown\n");
+		return;
+	}
+	isdown = true;
+
+// keep Con_Printf from trying to update the screen
+	scr_disabled_for_loading = true;
+
+	Host_WriteConfiguration (); 
+
+	CDAudio_Shutdown ();
+	NET_Shutdown ();
+	S_Shutdown();
+	IN_Shutdown ();
+
+        Sys_Shutdown();
+
+	if (cls.state != ca_dedicated)
+	{
+		VID_Shutdown();
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/host_cmd.c b/apps/plugins/sdl/progs/quake/host_cmd.c
new file mode 100644
index 0000000..498eec0
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/host_cmd.c
@@ -0,0 +1,1931 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+extern cvar_t	pausable;
+
+int	current_skill;
+
+void Mod_Print (void);
+
+/*
+==================
+Host_Quit_f
+==================
+*/
+
+extern void M_Menu_Quit_f (void);
+
+void Host_Quit_f (void)
+{
+	if (key_dest != key_console && cls.state != ca_dedicated)
+	{
+		M_Menu_Quit_f ();
+		return;
+	}
+	CL_Disconnect ();
+	Host_ShutdownServer(false);		
+
+	Sys_Quit ();
+}
+
+
+/*
+==================
+Host_Status_f
+==================
+*/
+void Host_Status_f (void)
+{
+	client_t	*client;
+	int			seconds;
+	int			minutes;
+	int			hours = 0;
+	int			j;
+	void		(*print) (char *fmt, ...);
+	
+	if (cmd_source == src_command)
+	{
+		if (!sv.active)
+		{
+			Cmd_ForwardToServer ();
+			return;
+		}
+		print = Con_Printf;
+	}
+	else
+		print = SV_ClientPrintf;
+
+	print ("host:    %s\n", Cvar_VariableString ("hostname"));
+	print ("version: %4.2f\n", VERSION);
+	if (tcpipAvailable)
+		print ("tcp/ip:  %s\n", my_tcpip_address);
+	if (ipxAvailable)
+		print ("ipx:     %s\n", my_ipx_address);
+	print ("map:     %s\n", sv.name);
+	print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
+	for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+	{
+		if (!client->active)
+			continue;
+		seconds = (int)(net_time - client->netconnection->connecttime);
+		minutes = seconds / 60;
+		if (minutes)
+		{
+			seconds -= (minutes * 60);
+			hours = minutes / 60;
+			if (hours)
+				minutes -= (hours * 60);
+		}
+		else
+			hours = 0;
+		print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds);
+		print ("   %s\n", client->netconnection->address);
+	}
+}
+
+
+/*
+==================
+Host_God_f
+
+Sets client to godmode
+==================
+*/
+void Host_God_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
+	if (!((int)sv_player->v.flags & FL_GODMODE) )
+		SV_ClientPrintf ("godmode OFF\n");
+	else
+		SV_ClientPrintf ("godmode ON\n");
+}
+
+void Host_Notarget_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
+	if (!((int)sv_player->v.flags & FL_NOTARGET) )
+		SV_ClientPrintf ("notarget OFF\n");
+	else
+		SV_ClientPrintf ("notarget ON\n");
+}
+
+qboolean noclip_anglehack;
+
+void Host_Noclip_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	if (sv_player->v.movetype != MOVETYPE_NOCLIP)
+	{
+		noclip_anglehack = true;
+		sv_player->v.movetype = MOVETYPE_NOCLIP;
+		SV_ClientPrintf ("noclip ON\n");
+	}
+	else
+	{
+		noclip_anglehack = false;
+		sv_player->v.movetype = MOVETYPE_WALK;
+		SV_ClientPrintf ("noclip OFF\n");
+	}
+}
+
+/*
+==================
+Host_Fly_f
+
+Sets client to flymode
+==================
+*/
+void Host_Fly_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	if (sv_player->v.movetype != MOVETYPE_FLY)
+	{
+		sv_player->v.movetype = MOVETYPE_FLY;
+		SV_ClientPrintf ("flymode ON\n");
+	}
+	else
+	{
+		sv_player->v.movetype = MOVETYPE_WALK;
+		SV_ClientPrintf ("flymode OFF\n");
+	}
+}
+
+
+/*
+==================
+Host_Ping_f
+
+==================
+*/
+void Host_Ping_f (void)
+{
+	int		i, j;
+	float	total;
+	client_t	*client;
+	
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	SV_ClientPrintf ("Client ping times:\n");
+	for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+	{
+		if (!client->active)
+			continue;
+		total = 0;
+		for (j=0 ; j<NUM_PING_TIMES ; j++)
+			total+=client->ping_times[j];
+		total /= NUM_PING_TIMES;
+		SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name);
+	}
+}
+
+/*
+===============================================================================
+
+SERVER TRANSITIONS
+
+===============================================================================
+*/
+
+
+/*
+======================
+Host_Map_f
+
+handle a 
+map <servername>
+command from the console.  Active clients are kicked off.
+======================
+*/
+void Host_Map_f (void)
+{
+	int		i;
+	char	name[MAX_QPATH];
+
+	if (cmd_source != src_command)
+		return;
+
+	cls.demonum = -1;		// stop demo loop in case this fails
+
+	CL_Disconnect ();
+	Host_ShutdownServer(false);		
+
+	key_dest = key_game;			// remove console or menu
+	SCR_BeginLoadingPlaque ();
+
+	cls.mapstring[0] = 0;
+	for (i=0 ; i<Cmd_Argc() ; i++)
+	{
+		strcat (cls.mapstring, Cmd_Argv(i));
+		strcat (cls.mapstring, " ");
+	}
+	strcat (cls.mapstring, "\n");
+
+	svs.serverflags = 0;			// haven't completed an episode yet
+	strcpy (name, Cmd_Argv(1));
+#ifdef QUAKE2
+	SV_SpawnServer (name, NULL);
+#else
+	SV_SpawnServer (name);
+#endif
+	if (!sv.active)
+		return;
+	
+	if (cls.state != ca_dedicated)
+	{
+		strcpy (cls.spawnparms, "");
+
+		for (i=2 ; i<Cmd_Argc() ; i++)
+		{
+			strcat (cls.spawnparms, Cmd_Argv(i));
+			strcat (cls.spawnparms, " ");
+		}
+		
+		Cmd_ExecuteString ("connect local", src_command);
+	}	
+}
+
+/*
+==================
+Host_Changelevel_f
+
+Goes to a new map, taking all clients along
+==================
+*/
+void Host_Changelevel_f (void)
+{
+#ifdef QUAKE2
+	char	level[MAX_QPATH];
+	char	_startspot[MAX_QPATH];
+	char	*startspot;
+
+	if (Cmd_Argc() < 2)
+	{
+		Con_Printf ("changelevel <levelname> : continue game on a new level\n");
+		return;
+	}
+	if (!sv.active || cls.demoplayback)
+	{
+		Con_Printf ("Only the server may changelevel\n");
+		return;
+	}
+
+	strcpy (level, Cmd_Argv(1));
+	if (Cmd_Argc() == 2)
+		startspot = NULL;
+	else
+	{
+		strcpy (_startspot, Cmd_Argv(2));
+		startspot = _startspot;
+	}
+
+	SV_SaveSpawnparms ();
+	SV_SpawnServer (level, startspot);
+#else
+	char	level[MAX_QPATH];
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("changelevel <levelname> : continue game on a new level\n");
+		return;
+	}
+	if (!sv.active || cls.demoplayback)
+	{
+		Con_Printf ("Only the server may changelevel\n");
+		return;
+	}
+	SV_SaveSpawnparms ();
+	strcpy (level, Cmd_Argv(1));
+	SV_SpawnServer (level);
+#endif
+}
+
+/*
+==================
+Host_Restart_f
+
+Restarts the current server for a dead player
+==================
+*/
+void Host_Restart_f (void)
+{
+	char	mapname[MAX_QPATH];
+#ifdef QUAKE2
+	char	startspot[MAX_QPATH];
+#endif
+
+	if (cls.demoplayback || !sv.active)
+		return;
+
+	if (cmd_source != src_command)
+		return;
+	strcpy (mapname, sv.name);	// must copy out, because it gets cleared
+								// in sv_spawnserver
+#ifdef QUAKE2
+	strcpy(startspot, sv.startspot);
+	SV_SpawnServer (mapname, startspot);
+#else
+	SV_SpawnServer (mapname);
+#endif
+}
+
+/*
+==================
+Host_Reconnect_f
+
+This command causes the client to wait for the signon messages again.
+This is sent just before a server changes levels
+==================
+*/
+void Host_Reconnect_f (void)
+{
+	SCR_BeginLoadingPlaque ();
+	cls.signon = 0;		// need new connection messages
+}
+
+/*
+=====================
+Host_Connect_f
+
+User command to connect to server
+=====================
+*/
+void Host_Connect_f (void)
+{
+	char	name[MAX_QPATH];
+	
+	cls.demonum = -1;		// stop demo loop in case this fails
+	if (cls.demoplayback)
+	{
+		CL_StopPlayback ();
+		CL_Disconnect ();
+	}
+	strcpy (name, Cmd_Argv(1));
+	CL_EstablishConnection (name);
+	Host_Reconnect_f ();
+}
+
+
+/*
+===============================================================================
+
+LOAD / SAVE GAME
+
+===============================================================================
+*/
+
+#define	SAVEGAME_VERSION	5
+
+/*
+===============
+Host_SavegameComment
+
+Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current 
+===============
+*/
+void Host_SavegameComment (char *text)
+{
+	int		i;
+	char	kills[20];
+
+	for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+		text[i] = ' ';
+	memcpy (text, cl.levelname, strlen(cl.levelname));
+	sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
+	memcpy (text+22, kills, strlen(kills));
+// convert space to _ to make stdio happy
+	for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
+		if (text[i] == ' ')
+			text[i] = '_';
+	text[SAVEGAME_COMMENT_LENGTH] = '\0';
+}
+
+
+/*
+===============
+Host_Savegame_f
+===============
+*/
+void Host_Savegame_f (void)
+{
+	char	name[256];
+	FILE	*f;
+	int		i;
+	char	comment[SAVEGAME_COMMENT_LENGTH+1];
+
+	if (cmd_source != src_command)
+		return;
+
+	if (!sv.active)
+	{
+		Con_Printf ("Not playing a local game.\n");
+		return;
+	}
+
+	if (cl.intermission)
+	{
+		Con_Printf ("Can't save in intermission.\n");
+		return;
+	}
+
+	if (svs.maxclients != 1)
+	{
+		Con_Printf ("Can't save multiplayer games.\n");
+		return;
+	}
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("save <savename> : save a game\n");
+		return;
+	}
+
+	if (strstr(Cmd_Argv(1), ".."))
+	{
+		Con_Printf ("Relative pathnames are not allowed.\n");
+		return;
+	}
+		
+	for (i=0 ; i<svs.maxclients ; i++)
+	{
+		if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) )
+		{
+			Con_Printf ("Can't savegame with a dead player\n");
+			return;
+		}
+	}
+
+	sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+	COM_DefaultExtension (name, ".sav");
+	
+	Con_Printf ("Saving game to %s...\n", name);
+	f = fopen (name, "w");
+	if (!f)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		return;
+	}
+	
+	fprintf (f, "%i\n", SAVEGAME_VERSION);
+	Host_SavegameComment (comment);
+	fprintf (f, "%s\n", comment);
+	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+		fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
+	fprintf (f, "%d\n", current_skill);
+	fprintf (f, "%s\n", sv.name);
+	fprintf (f, "%f\n",sv.time);
+
+// write the light styles
+
+	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+	{
+		if (sv.lightstyles[i])
+			fprintf (f, "%s\n", sv.lightstyles[i]);
+		else
+			fprintf (f,"m\n");
+	}
+
+
+	ED_WriteGlobals (f);
+	for (i=0 ; i<sv.num_edicts ; i++)
+	{
+		ED_Write (f, EDICT_NUM(i));
+		fflush (f);
+	}
+	fclose (f);
+	Con_Printf ("done.\n");
+}
+
+
+/*
+===============
+Host_Loadgame_f
+===============
+*/
+void Host_Loadgame_f (void)
+{
+	char	name[MAX_OSPATH];
+	FILE	*f;
+	char	mapname[MAX_QPATH];
+	float	time, tfloat;
+	char	str[32768], *start;
+	int		i, r;
+	edict_t	*ent;
+	int		entnum;
+	int		version;
+	float			spawn_parms[NUM_SPAWN_PARMS];
+
+	if (cmd_source != src_command)
+		return;
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("load <savename> : load a game\n");
+		return;
+	}
+
+	cls.demonum = -1;		// stop demo loop in case this fails
+
+	sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
+	COM_DefaultExtension (name, ".sav");
+	
+// we can't call SCR_BeginLoadingPlaque, because too much stack space has
+// been used.  The menu calls it before stuffing loadgame command
+//	SCR_BeginLoadingPlaque ();
+
+	Con_Printf ("Loading game from %s...\n", name);
+	f = fopen (name, "r");
+	if (!f)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		return;
+	}
+
+	fscanf (f, "%i\n", &version);
+	if (version != SAVEGAME_VERSION)
+	{
+		fclose (f);
+		Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+		return;
+	}
+	fscanf (f, "%s\n", str);
+	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+		fscanf (f, "%f\n", &spawn_parms[i]);
+// this silliness is so we can load 1.06 save files, which have float skill values
+	fscanf (f, "%f\n", &tfloat);
+	current_skill = (int)(tfloat + 0.1);
+	Cvar_SetValue ("skill", (float)current_skill);
+
+#ifdef QUAKE2
+	Cvar_SetValue ("deathmatch", 0);
+	Cvar_SetValue ("coop", 0);
+	Cvar_SetValue ("teamplay", 0);
+#endif
+
+	fscanf (f, "%s\n",mapname);
+	fscanf (f, "%f\n",&time);
+
+	CL_Disconnect_f ();
+	
+#ifdef QUAKE2
+	SV_SpawnServer (mapname, NULL);
+#else
+	SV_SpawnServer (mapname);
+#endif
+	if (!sv.active)
+	{
+		Con_Printf ("Couldn't load map\n");
+		return;
+	}
+	sv.paused = true;		// pause until all clients connect
+	sv.loadgame = true;
+
+// load the light styles
+
+	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+	{
+		fscanf (f, "%s\n", str);
+		sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1);
+		strcpy (sv.lightstyles[i], str);
+	}
+
+// load the edicts out of the savegame file
+	entnum = -1;		// -1 is the globals
+        bool done = false;
+	while (!done)
+	{
+		for (i=0 ; i<sizeof(str)-1 ; i++)
+		{
+			r = fgetc (f);
+			if (r == EOF || !r || r < 0)
+                        {
+                            if(r == EOF)
+                                done = true;
+                            break;
+                        }
+			str[i] = r;
+			if (r == '}')
+			{
+				i++;
+				break;
+			}
+                        LOGF("get char %d", r);
+		}
+		if (i == sizeof(str)-1)
+			Sys_Error ("Loadgame buffer overflow");
+		str[i] = 0;
+		start = str;
+		start = COM_Parse(str);
+		if (!com_token[0])
+			break;		// end of file
+		if (strcmp(com_token,"{"))
+			Sys_Error ("First token isn't a brace");
+			
+		if (entnum == -1)
+		{	// parse the global vars
+			ED_ParseGlobals (start);
+		}
+		else
+		{	// parse an edict
+
+			ent = EDICT_NUM(entnum);
+			memset (&ent->v, 0, progs->entityfields * 4);
+			ent->free = false;
+			ED_ParseEdict (start, ent);
+	
+		// link it into the bsp tree
+			if (!ent->free)
+				SV_LinkEdict (ent, false);
+		}
+
+		entnum++;
+	}
+	
+	sv.num_edicts = entnum;
+	sv.time = time;
+
+	fclose (f);
+
+	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+		svs.clients->spawn_parms[i] = spawn_parms[i];
+
+	if (cls.state != ca_dedicated)
+	{
+		CL_EstablishConnection ("local");
+		Host_Reconnect_f ();
+	}
+}
+
+#ifdef QUAKE2
+void SaveGamestate()
+{
+	char	name[256];
+	FILE	*f;
+	int		i;
+	char	comment[SAVEGAME_COMMENT_LENGTH+1];
+	edict_t	*ent;
+
+	sprintf (name, "%s/%s.gip", com_gamedir, sv.name);
+	
+	Con_Printf ("Saving game to %s...\n", name);
+	f = fopen (name, "w");
+	if (!f)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		return;
+	}
+	
+	fprintf (f, "%i\n", SAVEGAME_VERSION);
+	Host_SavegameComment (comment);
+	fprintf (f, "%s\n", comment);
+//	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+//		fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
+	fprintf (f, "%f\n", skill.value);
+	fprintf (f, "%s\n", sv.name);
+	fprintf (f, "%f\n", sv.time);
+
+// write the light styles
+
+	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+	{
+		if (sv.lightstyles[i])
+			fprintf (f, "%s\n", sv.lightstyles[i]);
+		else
+			fprintf (f,"m\n");
+	}
+
+
+	for (i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
+	{
+		ent = EDICT_NUM(i);
+		if ((int)ent->v.flags & FL_ARCHIVE_OVERRIDE)
+			continue;
+		fprintf (f, "%i\n",i);
+		ED_Write (f, ent);
+		fflush (f);
+	}
+	fclose (f);
+	Con_Printf ("done.\n");
+}
+
+int LoadGamestate(char *level, char *startspot)
+{
+	char	name[MAX_OSPATH];
+	FILE	*f;
+	char	mapname[MAX_QPATH];
+	float	time, sk;
+	char	str[32768], *start;
+	int		i, r;
+	edict_t	*ent;
+	int		entnum;
+	int		version;
+//	float	spawn_parms[NUM_SPAWN_PARMS];
+
+	sprintf (name, "%s/%s.gip", com_gamedir, level);
+	
+	Con_Printf ("Loading game from %s...\n", name);
+	f = fopen (name, "r");
+	if (!f)
+	{
+		Con_Printf ("ERROR: couldn't open.\n");
+		return -1;
+	}
+
+	fscanf (f, "%i\n", &version);
+	if (version != SAVEGAME_VERSION)
+	{
+		fclose (f);
+		Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
+		return -1;
+	}
+	fscanf (f, "%s\n", str);
+	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+		fscanf (f, "%f\n", &spawn_parms[i]);
+	fscanf (f, "%f\n", &sk);
+	Cvar_SetValue ("skill", sk);
+
+	fscanf (f, "%s\n",mapname);
+	fscanf (f, "%f\n",&time);
+
+	SV_SpawnServer (mapname, startspot);
+
+	if (!sv.active)
+	{
+		Con_Printf ("Couldn't load map\n");
+		return -1;
+	}
+
+// load the light styles
+	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+	{
+            fscanf (f, "%s\n", str);
+		sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1);
+		strcpy (sv.lightstyles[i], str);
+	}
+
+// load the edicts out of the savegame file
+	while (!feof(f))
+	{
+            fscanf (f, "%i\n",&entnum);
+		for (i=0 ; i<sizeof(str)-1 ; i++)
+		{
+			r = fgetc (f);
+			if (r == EOF || !r)
+				break;
+			str[i] = r;
+			if (r == '}')
+			{
+				i++;
+				break;
+			}
+		}
+		if (i == sizeof(str)-1)
+			Sys_Error ("Loadgame buffer overflow");
+		str[i] = 0;
+		start = str;
+		start = COM_Parse(str);
+		if (!com_token[0])
+			break;		// end of file
+		if (strcmp(com_token,"{"))
+			Sys_Error ("First token isn't a brace");
+			
+		// parse an edict
+
+		ent = EDICT_NUM(entnum);
+		memset (&ent->v, 0, progs->entityfields * 4);
+		ent->free = false;
+		ED_ParseEdict (start, ent);
+	
+		// link it into the bsp tree
+		if (!ent->free)
+			SV_LinkEdict (ent, false);
+	}
+	
+//	sv.num_edicts = entnum;
+	sv.time = time;
+	fclose (f);
+
+//	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+//		svs.clients->spawn_parms[i] = spawn_parms[i];
+
+	return 0;
+}
+
+// changing levels within a unit
+void Host_Changelevel2_f (void)
+{
+	char	level[MAX_QPATH];
+	char	_startspot[MAX_QPATH];
+	char	*startspot;
+
+	if (Cmd_Argc() < 2)
+	{
+		Con_Printf ("changelevel2 <levelname> : continue game on a new level in the unit\n");
+		return;
+	}
+	if (!sv.active || cls.demoplayback)
+	{
+		Con_Printf ("Only the server may changelevel\n");
+		return;
+	}
+
+	strcpy (level, Cmd_Argv(1));
+	if (Cmd_Argc() == 2)
+		startspot = NULL;
+	else
+	{
+		strcpy (_startspot, Cmd_Argv(2));
+		startspot = _startspot;
+	}
+
+	SV_SaveSpawnparms ();
+
+	// save the current level's state
+	SaveGamestate ();
+
+	// try to restore the new level
+	if (LoadGamestate (level, startspot))
+		SV_SpawnServer (level, startspot);
+}
+#endif
+
+
+//============================================================================
+
+/*
+======================
+Host_Name_f
+======================
+*/
+void Host_Name_f (void)
+{
+	char	*newName;
+
+	if (Cmd_Argc () == 1)
+	{
+		Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
+		return;
+	}
+	if (Cmd_Argc () == 2)
+		newName = Cmd_Argv(1);	
+	else
+		newName = Cmd_Args();
+	newName[15] = 0;
+
+	if (cmd_source == src_command)
+	{
+		if (Q_strcmp(cl_name.string, newName) == 0)
+			return;
+		Cvar_Set ("_cl_name", newName);
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (host_client->name[0] && strcmp(host_client->name, "unconnected") )
+		if (Q_strcmp(host_client->name, newName) != 0)
+			Con_Printf ("%s renamed to %s\n", host_client->name, newName);
+	Q_strcpy (host_client->name, newName);
+	host_client->edict->v.netname = host_client->name - pr_strings;
+	
+// send notification to all clients
+	
+	MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
+	MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+	MSG_WriteString (&sv.reliable_datagram, host_client->name);
+}
+
+	
+void Host_Version_f (void)
+{
+	Con_Printf ("Version %4.2f\n", VERSION);
+	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
+}
+
+#ifdef IDGODS
+void Host_Please_f (void)
+{
+	client_t *cl;
+	int			j;
+	
+	if (cmd_source != src_command)
+		return;
+
+	if ((Cmd_Argc () == 3) && Q_strcmp(Cmd_Argv(1), "#") == 0)
+	{
+		j = Q_atof(Cmd_Argv(2)) - 1;
+		if (j < 0 || j >= svs.maxclients)
+			return;
+		if (!svs.clients[j].active)
+			return;
+		cl = &svs.clients[j];
+		if (cl->privileged)
+		{
+			cl->privileged = false;
+			cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+			cl->edict->v.movetype = MOVETYPE_WALK;
+			noclip_anglehack = false;
+		}
+		else
+			cl->privileged = true;
+	}
+
+	if (Cmd_Argc () != 2)
+		return;
+
+	for (j=0, cl = svs.clients ; j<svs.maxclients ; j++, cl++)
+	{
+		if (!cl->active)
+			continue;
+		if (Q_strcasecmp(cl->name, Cmd_Argv(1)) == 0)
+		{
+			if (cl->privileged)
+			{
+				cl->privileged = false;
+				cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET);
+				cl->edict->v.movetype = MOVETYPE_WALK;
+				noclip_anglehack = false;
+			}
+			else
+				cl->privileged = true;
+			break;
+		}
+	}
+}
+#endif
+
+
+void Host_Say(qboolean teamonly)
+{
+	client_t *client;
+	client_t *save;
+	int		j;
+	char	*p;
+	unsigned char	text[64];
+	qboolean	fromServer = false;
+
+	if (cmd_source == src_command)
+	{
+		if (cls.state == ca_dedicated)
+		{
+			fromServer = true;
+			teamonly = false;
+		}
+		else
+		{
+			Cmd_ForwardToServer ();
+			return;
+		}
+	}
+
+	if (Cmd_Argc () < 2)
+		return;
+
+	save = host_client;
+
+	p = Cmd_Args();
+// remove quotes if present
+	if (*p == '"')
+	{
+		p++;
+		p[Q_strlen(p)-1] = 0;
+	}
+
+// turn on color set 1
+	if (!fromServer)
+		sprintf (text, "%c%s: ", 1, save->name);
+	else
+		sprintf (text, "%c<%s> ", 1, hostname.string);
+
+	j = sizeof(text) - 2 - Q_strlen(text);  // -2 for /n and null terminator
+	if (Q_strlen(p) > j)
+		p[j] = 0;
+
+	strcat (text, p);
+	strcat (text, "\n");
+
+	for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+	{
+		if (!client || !client->active || !client->spawned)
+			continue;
+		if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team)
+			continue;
+		host_client = client;
+		SV_ClientPrintf("%s", text);
+	}
+	host_client = save;
+
+	Sys_Printf("%s", &text[1]);
+}
+
+
+void Host_Say_f(void)
+{
+	Host_Say(false);
+}
+
+
+void Host_Say_Team_f(void)
+{
+	Host_Say(true);
+}
+
+
+void Host_Tell_f(void)
+{
+	client_t *client;
+	client_t *save;
+	int		j;
+	char	*p;
+	char	text[64];
+
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (Cmd_Argc () < 3)
+		return;
+
+	Q_strcpy(text, host_client->name);
+	Q_strcat(text, ": ");
+
+	p = Cmd_Args();
+
+// remove quotes if present
+	if (*p == '"')
+	{
+		p++;
+		p[Q_strlen(p)-1] = 0;
+	}
+
+// check length & truncate if necessary
+	j = sizeof(text) - 2 - Q_strlen(text);  // -2 for /n and null terminator
+	if (Q_strlen(p) > j)
+		p[j] = 0;
+
+	strcat (text, p);
+	strcat (text, "\n");
+
+	save = host_client;
+	for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
+	{
+		if (!client->active || !client->spawned)
+			continue;
+		if (Q_strcasecmp(client->name, Cmd_Argv(1)))
+			continue;
+		host_client = client;
+		SV_ClientPrintf("%s", text);
+		break;
+	}
+	host_client = save;
+}
+
+
+/*
+==================
+Host_Color_f
+==================
+*/
+void Host_Color_f(void)
+{
+	int		top, bottom;
+	int		playercolor;
+	
+	if (Cmd_Argc() == 1)
+	{
+		Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f);
+		Con_Printf ("color <0-13> [0-13]\n");
+		return;
+	}
+
+	if (Cmd_Argc() == 2)
+		top = bottom = atoi(Cmd_Argv(1));
+	else
+	{
+		top = atoi(Cmd_Argv(1));
+		bottom = atoi(Cmd_Argv(2));
+	}
+	
+	top &= 15;
+	if (top > 13)
+		top = 13;
+	bottom &= 15;
+	if (bottom > 13)
+		bottom = 13;
+	
+	playercolor = top*16 + bottom;
+
+	if (cmd_source == src_command)
+	{
+		Cvar_SetValue ("_cl_color", playercolor);
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+		return;
+	}
+
+	host_client->colors = playercolor;
+	host_client->edict->v.team = bottom + 1;
+
+// send notification to all clients
+	MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
+	MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
+	MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
+}
+
+/*
+==================
+Host_Kill_f
+==================
+*/
+void Host_Kill_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (sv_player->v.health <= 0)
+	{
+		SV_ClientPrintf ("Can't suicide -- allready dead!\n");
+		return;
+	}
+	
+	pr_global_struct->time = sv.time;
+	pr_global_struct->self = EDICT_TO_PROG(sv_player);
+	PR_ExecuteProgram (pr_global_struct->ClientKill);
+}
+
+
+/*
+==================
+Host_Pause_f
+==================
+*/
+void Host_Pause_f (void)
+{
+	
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+	if (!pausable.value)
+		SV_ClientPrintf ("Pause not allowed.\n");
+	else
+	{
+		sv.paused ^= 1;
+
+		if (sv.paused)
+		{
+			SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname);
+		}
+		else
+		{
+			SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + sv_player->v.netname);
+		}
+
+	// send notification to all clients
+		MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
+		MSG_WriteByte (&sv.reliable_datagram, sv.paused);
+	}
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_PreSpawn_f
+==================
+*/
+void Host_PreSpawn_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Con_Printf ("prespawn is not valid from the console\n");
+		return;
+	}
+
+	if (host_client->spawned)
+	{
+		Con_Printf ("prespawn not valid -- allready spawned\n");
+		return;
+	}
+	
+	SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
+	MSG_WriteByte (&host_client->message, svc_signonnum);
+	MSG_WriteByte (&host_client->message, 2);
+	host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Spawn_f
+==================
+*/
+void Host_Spawn_f (void)
+{
+	int		i;
+	client_t	*client;
+	edict_t	*ent;
+
+	if (cmd_source == src_command)
+	{
+		Con_Printf ("spawn is not valid from the console\n");
+		return;
+	}
+
+	if (host_client->spawned)
+	{
+		Con_Printf ("Spawn not valid -- allready spawned\n");
+		return;
+	}
+
+// run the entrance script
+	if (sv.loadgame)
+	{	// loaded games are fully inited allready
+		// if this is the last client to be connected, unpause
+		sv.paused = false;
+	}
+	else
+	{
+		// set up the edict
+		ent = host_client->edict;
+
+		memset (&ent->v, 0, progs->entityfields * 4);
+		ent->v.colormap = NUM_FOR_EDICT(ent);
+		ent->v.team = (host_client->colors & 15) + 1;
+		ent->v.netname = host_client->name - pr_strings;
+
+		// copy spawn parms out of the client_t
+
+		for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+			(&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
+
+		// call the spawn function
+
+		pr_global_struct->time = sv.time;
+		pr_global_struct->self = EDICT_TO_PROG(sv_player);
+		PR_ExecuteProgram (pr_global_struct->ClientConnect);
+
+		if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
+			Sys_Printf ("%s entered the game\n", host_client->name);
+
+		PR_ExecuteProgram (pr_global_struct->PutClientInServer);	
+	}
+
+
+// send all current names, colors, and frag counts
+	SZ_Clear (&host_client->message);
+
+// send time of update
+	MSG_WriteByte (&host_client->message, svc_time);
+	MSG_WriteFloat (&host_client->message, sv.time);
+
+	for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
+	{
+		MSG_WriteByte (&host_client->message, svc_updatename);
+		MSG_WriteByte (&host_client->message, i);
+		MSG_WriteString (&host_client->message, client->name);
+		MSG_WriteByte (&host_client->message, svc_updatefrags);
+		MSG_WriteByte (&host_client->message, i);
+		MSG_WriteShort (&host_client->message, client->old_frags);
+		MSG_WriteByte (&host_client->message, svc_updatecolors);
+		MSG_WriteByte (&host_client->message, i);
+		MSG_WriteByte (&host_client->message, client->colors);
+	}
+	
+// send all current light styles
+	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
+	{
+		MSG_WriteByte (&host_client->message, svc_lightstyle);
+		MSG_WriteByte (&host_client->message, (char)i);
+		MSG_WriteString (&host_client->message, sv.lightstyles[i]);
+	}
+
+//
+// send some stats
+//
+	MSG_WriteByte (&host_client->message, svc_updatestat);
+	MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
+	MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
+
+	MSG_WriteByte (&host_client->message, svc_updatestat);
+	MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
+	MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
+
+	MSG_WriteByte (&host_client->message, svc_updatestat);
+	MSG_WriteByte (&host_client->message, STAT_SECRETS);
+	MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
+
+	MSG_WriteByte (&host_client->message, svc_updatestat);
+	MSG_WriteByte (&host_client->message, STAT_MONSTERS);
+	MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
+
+	
+//
+// send a fixangle
+// Never send a roll angle, because savegames can catch the server
+// in a state where it is expecting the client to correct the angle
+// and it won't happen if the game was just loaded, so you wind up
+// with a permanent head tilt
+	ent = EDICT_NUM( 1 + (host_client - svs.clients) );
+	MSG_WriteByte (&host_client->message, svc_setangle);
+	for (i=0 ; i < 2 ; i++)
+		MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
+	MSG_WriteAngle (&host_client->message, 0 );
+
+	SV_WriteClientdataToMessage (sv_player, &host_client->message);
+
+	MSG_WriteByte (&host_client->message, svc_signonnum);
+	MSG_WriteByte (&host_client->message, 3);
+	host_client->sendsignon = true;
+}
+
+/*
+==================
+Host_Begin_f
+==================
+*/
+void Host_Begin_f (void)
+{
+	if (cmd_source == src_command)
+	{
+		Con_Printf ("begin is not valid from the console\n");
+		return;
+	}
+
+	host_client->spawned = true;
+}
+
+//===========================================================================
+
+
+/*
+==================
+Host_Kick_f
+
+Kicks a user off of the server
+==================
+*/
+void Host_Kick_f (void)
+{
+	char		*who;
+	char		*message = NULL;
+	client_t	*save;
+	int			i;
+	qboolean	byNumber = false;
+
+	if (cmd_source == src_command)
+	{
+		if (!sv.active)
+		{
+			Cmd_ForwardToServer ();
+			return;
+		}
+	}
+	else if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	save = host_client;
+
+	if (Cmd_Argc() > 2 && Q_strcmp(Cmd_Argv(1), "#") == 0)
+	{
+		i = Q_atof(Cmd_Argv(2)) - 1;
+		if (i < 0 || i >= svs.maxclients)
+			return;
+		if (!svs.clients[i].active)
+			return;
+		host_client = &svs.clients[i];
+		byNumber = true;
+	}
+	else
+	{
+		for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
+		{
+			if (!host_client->active)
+				continue;
+			if (Q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
+				break;
+		}
+	}
+
+	if (i < svs.maxclients)
+	{
+		if (cmd_source == src_command)
+			if (cls.state == ca_dedicated)
+				who = "Console";
+			else
+				who = cl_name.string;
+		else
+			who = save->name;
+
+		// can't kick yourself!
+		if (host_client == save)
+			return;
+
+		if (Cmd_Argc() > 2)
+		{
+			message = COM_Parse(Cmd_Args());
+			if (byNumber)
+			{
+				message++;							// skip the #
+				while (*message == ' ')				// skip white space
+					message++;
+				message += Q_strlen(Cmd_Argv(2));	// skip the number
+			}
+			while (*message && *message == ' ')
+				message++;
+		}
+		if (message)
+			SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
+		else
+			SV_ClientPrintf ("Kicked by %s\n", who);
+		SV_DropClient (false);
+	}
+
+	host_client = save;
+}
+
+/*
+===============================================================================
+
+DEBUGGING TOOLS
+
+===============================================================================
+*/
+
+/*
+==================
+Host_Give_f
+==================
+*/
+void Host_Give_f (void)
+{
+	char	*t;
+	int		v, w;
+	eval_t	*val;
+
+	if (cmd_source == src_command)
+	{
+		Cmd_ForwardToServer ();
+		return;
+	}
+
+	if (pr_global_struct->deathmatch && !host_client->privileged)
+		return;
+
+	t = Cmd_Argv(1);
+	v = atoi (Cmd_Argv(2));
+	
+	switch (t[0])
+	{
+   case '0':
+   case '1':
+   case '2':
+   case '3':
+   case '4':
+   case '5':
+   case '6':
+   case '7':
+   case '8':
+   case '9':
+      // MED 01/04/97 added hipnotic give stuff
+      if (hipnotic)
+      {
+         if (t[0] == '6')
+         {
+            if (t[1] == 'a')
+               sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN;
+            else
+               sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER;
+         }
+         else if (t[0] == '9')
+            sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON;
+         else if (t[0] == '0')
+            sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR;
+         else if (t[0] >= '2')
+            sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+      }
+      else
+      {
+         if (t[0] >= '2')
+            sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
+      }
+		break;
+	
+    case 's':
+		if (rogue)
+		{
+	        val = GetEdictFieldValue(sv_player, "ammo_shells1");
+		    if (val)
+			    val->_float = v;
+		}
+
+        sv_player->v.ammo_shells = v;
+        break;		
+    case 'n':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_nails1");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon <= IT_LIGHTNING)
+					sv_player->v.ammo_nails = v;
+			}
+		}
+		else
+		{
+			sv_player->v.ammo_nails = v;
+		}
+        break;		
+    case 'l':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon > IT_LIGHTNING)
+					sv_player->v.ammo_nails = v;
+			}
+		}
+        break;
+    case 'r':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_rockets1");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon <= IT_LIGHTNING)
+					sv_player->v.ammo_rockets = v;
+			}
+		}
+		else
+		{
+			sv_player->v.ammo_rockets = v;
+		}
+        break;		
+    case 'm':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon > IT_LIGHTNING)
+					sv_player->v.ammo_rockets = v;
+			}
+		}
+        break;		
+    case 'h':
+        sv_player->v.health = v;
+        break;		
+    case 'c':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_cells1");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon <= IT_LIGHTNING)
+					sv_player->v.ammo_cells = v;
+			}
+		}
+		else
+		{
+			sv_player->v.ammo_cells = v;
+		}
+        break;		
+    case 'p':
+		if (rogue)
+		{
+			val = GetEdictFieldValue(sv_player, "ammo_plasma");
+			if (val)
+			{
+				val->_float = v;
+				if (sv_player->v.weapon > IT_LIGHTNING)
+					sv_player->v.ammo_cells = v;
+			}
+		}
+        break;		
+    }
+}
+
+edict_t	*FindViewthing (void)
+{
+	int		i;
+	edict_t	*e;
+	
+	for (i=0 ; i<sv.num_edicts ; i++)
+	{
+		e = EDICT_NUM(i);
+		if ( !strcmp (pr_strings + e->v.classname, "viewthing") )
+			return e;
+	}
+	Con_Printf ("No viewthing on map\n");
+	return NULL;
+}
+
+/*
+==================
+Host_Viewmodel_f
+==================
+*/
+void Host_Viewmodel_f (void)
+{
+	edict_t	*e;
+	model_t	*m;
+
+	e = FindViewthing ();
+	if (!e)
+		return;
+
+	m = Mod_ForName (Cmd_Argv(1), false);
+	if (!m)
+	{
+		Con_Printf ("Can't load %s\n", Cmd_Argv(1));
+		return;
+	}
+	
+	e->v.frame = 0;
+	cl.model_precache[(int)e->v.modelindex] = m;
+}
+
+/*
+==================
+Host_Viewframe_f
+==================
+*/
+void Host_Viewframe_f (void)
+{
+	edict_t	*e;
+	int		f;
+	model_t	*m;
+
+	e = FindViewthing ();
+	if (!e)
+		return;
+	m = cl.model_precache[(int)e->v.modelindex];
+
+	f = atoi(Cmd_Argv(1));
+	if (f >= m->numframes)
+		f = m->numframes-1;
+
+	e->v.frame = f;		
+}
+
+
+void PrintFrameName (model_t *m, int frame)
+{
+	aliashdr_t 			*hdr;
+	maliasframedesc_t	*pframedesc;
+
+	hdr = (aliashdr_t *)Mod_Extradata (m);
+	if (!hdr)
+		return;
+	pframedesc = &hdr->frames[frame];
+	
+	Con_Printf ("frame %i: %s\n", frame, pframedesc->name);
+}
+
+/*
+==================
+Host_Viewnext_f
+==================
+*/
+void Host_Viewnext_f (void)
+{
+	edict_t	*e;
+	model_t	*m;
+	
+	e = FindViewthing ();
+	if (!e)
+		return;
+	m = cl.model_precache[(int)e->v.modelindex];
+
+	e->v.frame = e->v.frame + 1;
+	if (e->v.frame >= m->numframes)
+		e->v.frame = m->numframes - 1;
+
+	PrintFrameName (m, e->v.frame);		
+}
+
+/*
+==================
+Host_Viewprev_f
+==================
+*/
+void Host_Viewprev_f (void)
+{
+	edict_t	*e;
+	model_t	*m;
+
+	e = FindViewthing ();
+	if (!e)
+		return;
+
+	m = cl.model_precache[(int)e->v.modelindex];
+
+	e->v.frame = e->v.frame - 1;
+	if (e->v.frame < 0)
+		e->v.frame = 0;
+
+	PrintFrameName (m, e->v.frame);		
+}
+
+/*
+===============================================================================
+
+DEMO LOOP CONTROL
+
+===============================================================================
+*/
+
+
+/*
+==================
+Host_Startdemos_f
+==================
+*/
+void Host_Startdemos_f (void)
+{
+	int		i, c;
+
+	if (cls.state == ca_dedicated)
+	{
+		if (!sv.active)
+			Cbuf_AddText ("map start\n");
+		return;
+	}
+
+	c = Cmd_Argc() - 1;
+	if (c > MAX_DEMOS)
+	{
+		Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
+		c = MAX_DEMOS;
+	}
+	Con_Printf ("%i demo(s) in loop\n", c);
+
+	for (i=1 ; i<c+1 ; i++)
+		strncpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])-1);
+
+	if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
+	{
+		cls.demonum = 0;
+		CL_NextDemo ();
+	}
+	else
+		cls.demonum = -1;
+}
+
+
+/*
+==================
+Host_Demos_f
+
+Return to looping demos
+==================
+*/
+void Host_Demos_f (void)
+{
+	if (cls.state == ca_dedicated)
+		return;
+	if (cls.demonum == -1)
+		cls.demonum = 1;
+	CL_Disconnect_f ();
+	CL_NextDemo ();
+}
+
+/*
+==================
+Host_Stopdemo_f
+
+Return to looping demos
+==================
+*/
+void Host_Stopdemo_f (void)
+{
+	if (cls.state == ca_dedicated)
+		return;
+	if (!cls.demoplayback)
+		return;
+	CL_StopPlayback ();
+	CL_Disconnect ();
+}
+
+//=============================================================================
+
+/*
+==================
+Host_InitCommands
+==================
+*/
+void Host_InitCommands (void)
+{
+	Cmd_AddCommand ("status", Host_Status_f);
+	Cmd_AddCommand ("quit", Host_Quit_f);
+	Cmd_AddCommand ("god", Host_God_f);
+	Cmd_AddCommand ("notarget", Host_Notarget_f);
+	Cmd_AddCommand ("fly", Host_Fly_f);
+	Cmd_AddCommand ("map", Host_Map_f);
+	Cmd_AddCommand ("restart", Host_Restart_f);
+	Cmd_AddCommand ("changelevel", Host_Changelevel_f);
+#ifdef QUAKE2
+	Cmd_AddCommand ("changelevel2", Host_Changelevel2_f);
+#endif
+	Cmd_AddCommand ("connect", Host_Connect_f);
+	Cmd_AddCommand ("reconnect", Host_Reconnect_f);
+	Cmd_AddCommand ("name", Host_Name_f);
+	Cmd_AddCommand ("noclip", Host_Noclip_f);
+	Cmd_AddCommand ("version", Host_Version_f);
+#ifdef IDGODS
+	Cmd_AddCommand ("please", Host_Please_f);
+#endif
+	Cmd_AddCommand ("say", Host_Say_f);
+	Cmd_AddCommand ("say_team", Host_Say_Team_f);
+	Cmd_AddCommand ("tell", Host_Tell_f);
+	Cmd_AddCommand ("color", Host_Color_f);
+	Cmd_AddCommand ("kill", Host_Kill_f);
+	Cmd_AddCommand ("pause", Host_Pause_f);
+	Cmd_AddCommand ("spawn", Host_Spawn_f);
+	Cmd_AddCommand ("begin", Host_Begin_f);
+	Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
+	Cmd_AddCommand ("kick", Host_Kick_f);
+	Cmd_AddCommand ("ping", Host_Ping_f);
+	Cmd_AddCommand ("load", Host_Loadgame_f);
+	Cmd_AddCommand ("save", Host_Savegame_f);
+	Cmd_AddCommand ("give", Host_Give_f);
+
+	Cmd_AddCommand ("startdemos", Host_Startdemos_f);
+	Cmd_AddCommand ("demos", Host_Demos_f);
+	Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
+
+	Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
+	Cmd_AddCommand ("viewframe", Host_Viewframe_f);
+	Cmd_AddCommand ("viewnext", Host_Viewnext_f);
+	Cmd_AddCommand ("viewprev", Host_Viewprev_f);
+
+	Cmd_AddCommand ("mcache", Mod_Print);
+}
diff --git a/apps/plugins/sdl/progs/quake/in_null.c b/apps/plugins/sdl/progs/quake/in_null.c
new file mode 100644
index 0000000..1c54ee4
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/in_null.c
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// in_null.c -- for systems without a mouse
+
+#include "quakedef.h"
+
+void IN_Init (void)
+{
+}
+
+void IN_Shutdown (void)
+{
+}
+
+void IN_Commands (void)
+{
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+}
+
diff --git a/apps/plugins/sdl/progs/quake/input.h b/apps/plugins/sdl/progs/quake/input.h
new file mode 100644
index 0000000..c3daa17
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/input.h
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// input.h -- external (non-keyboard) input devices
+
+void IN_Init (void);
+
+void IN_Shutdown (void);
+
+void IN_Commands (void);
+// oportunity for devices to stick commands on the script buffer
+
+void IN_Move (usercmd_t *cmd);
+// add additional movement on top of the keyboard move cmd
+
+void IN_ClearStates (void);
+// restores all button and position states to defaults
+
diff --git a/apps/plugins/sdl/progs/quake/keys.c b/apps/plugins/sdl/progs/quake/keys.c
new file mode 100644
index 0000000..896bbaa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/keys.c
@@ -0,0 +1,804 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+/*
+
+key up events are sent even if in console mode
+
+*/
+
+
+#define		MAXCMDLINE	256
+char	key_lines[32][MAXCMDLINE];
+int		key_linepos;
+int		shift_down=false;
+int		key_lastpress;
+
+int		edit_line=0;
+int		history_line=0;
+
+keydest_t	key_dest;
+
+int		key_count;			// incremented every key event
+
+char	*keybindings[256];
+qboolean	consolekeys[256];	// if true, can't be rebound while in console
+qboolean	menubound[256];	// if true, can't be rebound while in menu
+int		keyshift[256];		// key to map to if shift held down in console
+int		key_repeats[256];	// if > 1, it is autorepeating
+qboolean	keydown[256];
+
+typedef struct
+{
+	char	*name;
+	int		keynum;
+} keyname_t;
+
+keyname_t keynames[] =
+{
+	{"TAB", K_TAB},
+	{"ENTER", K_ENTER},
+	{"ESCAPE", K_ESCAPE},
+	{"SPACE", K_SPACE},
+	{"BACKSPACE", K_BACKSPACE},
+	{"UPARROW", K_UPARROW},
+	{"DOWNARROW", K_DOWNARROW},
+	{"LEFTARROW", K_LEFTARROW},
+	{"RIGHTARROW", K_RIGHTARROW},
+
+	{"ALT", K_ALT},
+	{"CTRL", K_CTRL},
+	{"SHIFT", K_SHIFT},
+	
+	{"F1", K_F1},
+	{"F2", K_F2},
+	{"F3", K_F3},
+	{"F4", K_F4},
+	{"F5", K_F5},
+	{"F6", K_F6},
+	{"F7", K_F7},
+	{"F8", K_F8},
+	{"F9", K_F9},
+	{"F10", K_F10},
+	{"F11", K_F11},
+	{"F12", K_F12},
+
+	{"INS", K_INS},
+	{"DEL", K_DEL},
+	{"PGDN", K_PGDN},
+	{"PGUP", K_PGUP},
+	{"HOME", K_HOME},
+	{"END", K_END},
+
+	{"MOUSE1", K_MOUSE1},
+	{"MOUSE2", K_MOUSE2},
+	{"MOUSE3", K_MOUSE3},
+
+	{"JOY1", K_JOY1},
+	{"JOY2", K_JOY2},
+	{"JOY3", K_JOY3},
+	{"JOY4", K_JOY4},
+
+	{"AUX1", K_AUX1},
+	{"AUX2", K_AUX2},
+	{"AUX3", K_AUX3},
+	{"AUX4", K_AUX4},
+	{"AUX5", K_AUX5},
+	{"AUX6", K_AUX6},
+	{"AUX7", K_AUX7},
+	{"AUX8", K_AUX8},
+	{"AUX9", K_AUX9},
+	{"AUX10", K_AUX10},
+	{"AUX11", K_AUX11},
+	{"AUX12", K_AUX12},
+	{"AUX13", K_AUX13},
+	{"AUX14", K_AUX14},
+	{"AUX15", K_AUX15},
+	{"AUX16", K_AUX16},
+	{"AUX17", K_AUX17},
+	{"AUX18", K_AUX18},
+	{"AUX19", K_AUX19},
+	{"AUX20", K_AUX20},
+	{"AUX21", K_AUX21},
+	{"AUX22", K_AUX22},
+	{"AUX23", K_AUX23},
+	{"AUX24", K_AUX24},
+	{"AUX25", K_AUX25},
+	{"AUX26", K_AUX26},
+	{"AUX27", K_AUX27},
+	{"AUX28", K_AUX28},
+	{"AUX29", K_AUX29},
+	{"AUX30", K_AUX30},
+	{"AUX31", K_AUX31},
+	{"AUX32", K_AUX32},
+
+	{"PAUSE", K_PAUSE},
+
+	{"MWHEELUP", K_MWHEELUP},
+	{"MWHEELDOWN", K_MWHEELDOWN},
+
+	{"SEMICOLON", ';'},	// because a raw semicolon seperates commands
+
+	{NULL,0}
+};
+
+/*
+==============================================================================
+
+			LINE TYPING INTO THE CONSOLE
+
+==============================================================================
+*/
+
+/* Rockbox hack */
+void rb_console(void)
+{
+    rb->kbd_input(key_lines[edit_line] + 1, MAXCMDLINE-1);
+}
+
+/*
+====================
+Key_Console
+
+Interactive line editing and console scrollback
+====================
+*/
+void Key_Console (int key)
+{
+	char	*cmd;
+	
+	if (key == K_ENTER)
+	{
+		Cbuf_AddText (key_lines[edit_line]+1);	// skip the >
+		Cbuf_AddText ("\n");
+		Con_Printf ("%s\n",key_lines[edit_line]);
+		edit_line = (edit_line + 1) & 31;
+		history_line = edit_line;
+		key_lines[edit_line][0] = ']';
+		key_linepos = 1;
+		if (cls.state == ca_disconnected)
+			SCR_UpdateScreen ();	// force an update, because the command
+									// may take some time
+		return;
+	}
+
+	if (key == K_TAB)
+	{	// command completion
+		cmd = Cmd_CompleteCommand (key_lines[edit_line]+1);
+		if (!cmd)
+			cmd = Cvar_CompleteVariable (key_lines[edit_line]+1);
+		if (cmd)
+		{
+			Q_strcpy (key_lines[edit_line]+1, cmd);
+			key_linepos = Q_strlen(cmd)+1;
+			key_lines[edit_line][key_linepos] = ' ';
+			key_linepos++;
+			key_lines[edit_line][key_linepos] = 0;
+			return;
+		}
+	}
+	
+	if (key == K_BACKSPACE || key == K_LEFTARROW)
+	{
+		if (key_linepos > 1)
+			key_linepos--;
+		return;
+	}
+
+	if (key == K_UPARROW)
+	{
+		do
+		{
+			history_line = (history_line - 1) & 31;
+		} while (history_line != edit_line
+				&& !key_lines[history_line][1]);
+		if (history_line == edit_line)
+			history_line = (edit_line+1)&31;
+		Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+		key_linepos = Q_strlen(key_lines[edit_line]);
+		return;
+	}
+
+	if (key == K_DOWNARROW)
+	{
+		if (history_line == edit_line) return;
+		do
+		{
+			history_line = (history_line + 1) & 31;
+		}
+		while (history_line != edit_line
+			&& !key_lines[history_line][1]);
+		if (history_line == edit_line)
+		{
+			key_lines[edit_line][0] = ']';
+			key_linepos = 1;
+		}
+		else
+		{
+			Q_strcpy(key_lines[edit_line], key_lines[history_line]);
+			key_linepos = Q_strlen(key_lines[edit_line]);
+		}
+		return;
+	}
+
+	if (key == K_PGUP || key==K_MWHEELUP)
+	{
+		con_backscroll += 2;
+		if (con_backscroll > con_totallines - (vid.height>>3) - 1)
+			con_backscroll = con_totallines - (vid.height>>3) - 1;
+		return;
+	}
+
+	if (key == K_PGDN || key==K_MWHEELDOWN)
+	{
+		con_backscroll -= 2;
+		if (con_backscroll < 0)
+			con_backscroll = 0;
+		return;
+	}
+
+	if (key == K_HOME)
+	{
+		con_backscroll = con_totallines - (vid.height>>3) - 1;
+		return;
+	}
+
+	if (key == K_END)
+	{
+		con_backscroll = 0;
+		return;
+	}
+	
+	if (key < 32 || key > 127)
+		return;	// non printable
+		
+	if (key_linepos < MAXCMDLINE-1)
+	{
+		key_lines[edit_line][key_linepos] = key;
+		key_linepos++;
+		key_lines[edit_line][key_linepos] = 0;
+	}
+
+}
+
+//============================================================================
+
+char chat_buffer[32];
+qboolean team_message = false;
+
+void Key_Message (int key)
+{
+	static int chat_bufferlen = 0;
+
+	if (key == K_ENTER)
+	{
+		if (team_message)
+			Cbuf_AddText ("say_team \"");
+		else
+			Cbuf_AddText ("say \"");
+		Cbuf_AddText(chat_buffer);
+		Cbuf_AddText("\"\n");
+
+		key_dest = key_game;
+		chat_bufferlen = 0;
+		chat_buffer[0] = 0;
+		return;
+	}
+
+	if (key == K_ESCAPE)
+	{
+		key_dest = key_game;
+		chat_bufferlen = 0;
+		chat_buffer[0] = 0;
+		return;
+	}
+
+	if (key < 32 || key > 127)
+		return;	// non printable
+
+	if (key == K_BACKSPACE)
+	{
+		if (chat_bufferlen)
+		{
+			chat_bufferlen--;
+			chat_buffer[chat_bufferlen] = 0;
+		}
+		return;
+	}
+
+	if (chat_bufferlen == 31)
+		return; // all full
+
+	chat_buffer[chat_bufferlen++] = key;
+	chat_buffer[chat_bufferlen] = 0;
+}
+
+//============================================================================
+
+
+/*
+===================
+Key_StringToKeynum
+
+Returns a key number to be used to index keybindings[] by looking at
+the given string.  Single ascii characters return themselves, while
+the K_* names are matched up.
+===================
+*/
+int Key_StringToKeynum (char *str)
+{
+	keyname_t	*kn;
+	
+	if (!str || !str[0])
+		return -1;
+	if (!str[1])
+		return str[0];
+
+	for (kn=keynames ; kn->name ; kn++)
+	{
+		if (!Q_strcasecmp(str,kn->name))
+			return kn->keynum;
+	}
+	return -1;
+}
+
+/*
+===================
+Key_KeynumToString
+
+Returns a string (either a single ascii char, or a K_* name) for the
+given keynum.
+FIXME: handle quote special (general escape sequence?)
+===================
+*/
+char *Key_KeynumToString (int keynum)
+{
+	keyname_t	*kn;	
+	static	char	tinystr[2];
+	
+	if (keynum == -1)
+		return "<KEY NOT FOUND>";
+	if (keynum > 32 && keynum < 127)
+	{	// printable ascii
+		tinystr[0] = keynum;
+		tinystr[1] = 0;
+		return tinystr;
+	}
+	
+	for (kn=keynames ; kn->name ; kn++)
+		if (keynum == kn->keynum)
+			return kn->name;
+
+	return "<UNKNOWN KEYNUM>";
+}
+
+
+/*
+===================
+Key_SetBinding
+===================
+*/
+void Key_SetBinding (int keynum, char *binding)
+{
+    LOGF("binding %d -> %s\n", keynum, binding);
+    
+	char	*new;
+	int		l;
+			
+	if (keynum == -1)
+		return;
+
+// free old bindings
+	if (keybindings[keynum])
+	{
+		Z_Free (keybindings[keynum]);
+		keybindings[keynum] = NULL;
+	}
+			
+// allocate memory for new binding
+	l = Q_strlen (binding);	
+	new = Z_Malloc (l+1);
+	Q_strcpy (new, binding);
+	new[l] = 0;
+	keybindings[keynum] = new;	
+}
+
+/*
+===================
+Key_Unbind_f
+===================
+*/
+void Key_Unbind_f (void)
+{
+	int		b;
+
+	if (Cmd_Argc() != 2)
+	{
+		Con_Printf ("unbind <key> : remove commands from a key\n");
+		return;
+	}
+	
+	b = Key_StringToKeynum (Cmd_Argv(1));
+	if (b==-1)
+	{
+		Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+		return;
+	}
+
+	Key_SetBinding (b, "");
+}
+
+void Key_Unbindall_f (void)
+{
+	int		i;
+	
+	for (i=0 ; i<256 ; i++)
+		if (keybindings[i])
+			Key_SetBinding (i, "");
+}
+
+int bind_nooverride = 0;
+
+/*
+===================
+Key_Bind_f
+===================
+*/
+void Key_Bind_f (void)
+{
+	int			i, c, b;
+	char		cmd[1024];
+	
+	c = Cmd_Argc();
+
+	if (c != 2 && c != 3)
+	{
+		Con_Printf ("bind <key> [command] : attach a command to a key\n");
+		return;
+	}
+	b = Key_StringToKeynum (Cmd_Argv(1));
+	if (b==-1)
+	{
+		Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+		return;
+	}
+
+	if (c == 2)
+	{
+		if (keybindings[b])
+			Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
+		else
+			Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+		return;
+	}
+
+        bool overridden = false;
+        
+        // Rockbox hack to set defaults
+        if(!bind_nooverride)
+        {
+            //rb->splashf(HZ, "overriding defaults");
+
+            switch(b)
+            {
+            case K_ENTER:
+                overridden = true;
+                strcpy(cmd, "+attack");
+                break;
+            case K_DOWNARROW:
+                overridden = true;
+                strcpy(cmd, "impulse 10; +jump; wait; -jump");
+                break;
+            case 'a':
+                overridden = true;
+                strcpy(cmd, "+moveleft");
+                break;
+            case 'd':
+                overridden = true;
+                strcpy(cmd, "+moveright");
+                break;
+            }
+        }
+
+        if(!overridden)
+        {
+// copy the rest of the command line
+            cmd[0] = 0;		// start out with a null string
+            for (i=2 ; i< c ; i++)
+            {
+		if (i > 2)
+                    strcat (cmd, " ");
+		strcat (cmd, Cmd_Argv(i));
+            }
+        }
+
+	Key_SetBinding (b, cmd);
+}
+
+/*
+============
+Key_WriteBindings
+
+Writes lines containing "bind key value"
+============
+*/
+void Key_WriteBindings (FILE *f)
+{
+	int		i;
+
+	for (i=0 ; i<256 ; i++)
+		if (keybindings[i])
+			if (*keybindings[i])
+				fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+
+/*
+===================
+Key_Init
+===================
+*/
+void Key_Init (void)
+{
+	int		i;
+
+	for (i=0 ; i<32 ; i++)
+	{
+		key_lines[i][0] = ']';
+		key_lines[i][1] = 0;
+	}
+	key_linepos = 1;
+	
+//
+// init ascii characters in console mode
+//
+	for (i=32 ; i<128 ; i++)
+		consolekeys[i] = true;
+	consolekeys[K_ENTER] = true;
+	consolekeys[K_TAB] = true;
+	consolekeys[K_LEFTARROW] = true;
+	consolekeys[K_RIGHTARROW] = true;
+	consolekeys[K_UPARROW] = true;
+	consolekeys[K_DOWNARROW] = true;
+	consolekeys[K_BACKSPACE] = true;
+	consolekeys[K_PGUP] = true;
+	consolekeys[K_PGDN] = true;
+	consolekeys[K_SHIFT] = true;
+	consolekeys[K_MWHEELUP] = true;
+	consolekeys[K_MWHEELDOWN] = true;
+	consolekeys['`'] = false;
+	consolekeys['~'] = false;
+
+	for (i=0 ; i<256 ; i++)
+		keyshift[i] = i;
+	for (i='a' ; i<='z' ; i++)
+		keyshift[i] = i - 'a' + 'A';
+	keyshift['1'] = '!';
+	keyshift['2'] = '@';
+	keyshift['3'] = '#';
+	keyshift['4'] = '$';
+	keyshift['5'] = '%';
+	keyshift['6'] = '^';
+	keyshift['7'] = '&';
+	keyshift['8'] = '*';
+	keyshift['9'] = '(';
+	keyshift['0'] = ')';
+	keyshift['-'] = '_';
+	keyshift['='] = '+';
+	keyshift[','] = '<';
+	keyshift['.'] = '>';
+	keyshift['/'] = '?';
+	keyshift[';'] = ':';
+	keyshift['\''] = '"';
+	keyshift['['] = '{';
+	keyshift[']'] = '}';
+	keyshift['`'] = '~';
+	keyshift['\\'] = '|';
+
+	menubound[K_ESCAPE] = true;
+	for (i=0 ; i<12 ; i++)
+		menubound[K_F1+i] = true;
+
+//
+// register our functions
+//
+	Cmd_AddCommand ("bind",Key_Bind_f);
+	Cmd_AddCommand ("unbind",Key_Unbind_f);
+	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+
+
+}
+
+/*
+===================
+Key_Event
+
+Called by the system between frames for both key up and key down events
+Should NOT be called during an interrupt!
+===================
+*/
+void Key_Event (int key, qboolean down)
+{
+	char	*kb;
+	char	cmd[1024];
+
+	keydown[key] = down;
+
+	if (!down)
+		key_repeats[key] = 0;
+
+	key_lastpress = key;
+	key_count++;
+	if (key_count <= 0)
+	{
+		return;		// just catching keys for Con_NotifyBox
+	}
+
+// update auto-repeat status
+	if (down)
+	{
+		key_repeats[key]++;
+		if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1)
+		{
+			return;	// ignore most autorepeats
+		}
+			
+		if (key >= 200 && !keybindings[key])
+			Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
+	}
+
+	if (key == K_SHIFT)
+		shift_down = down;
+
+//
+// handle escape specialy, so the user can never unbind it
+//
+	if (key == K_ESCAPE)
+	{
+		if (!down)
+			return;
+		switch (key_dest)
+		{
+		case key_message:
+			Key_Message (key);
+			break;
+		case key_menu:
+			M_Keydown (key);
+			break;
+		case key_game:
+		case key_console:
+			M_ToggleMenu_f ();
+			break;
+		default:
+			Sys_Error ("Bad key_dest");
+		}
+		return;
+	}
+
+//
+// key up events only generate commands if the game key binding is
+// a button command (leading + sign).  These will occur even in console mode,
+// to keep the character from continuing an action started before a console
+// switch.  Button commands include the kenum as a parameter, so multiple
+// downs can be matched with ups
+//
+	if (!down)
+	{
+		kb = keybindings[key];
+		if (kb && kb[0] == '+')
+		{
+			sprintf (cmd, "-%s %i\n", kb+1, key);
+			Cbuf_AddText (cmd);
+		}
+		if (keyshift[key] != key)
+		{
+			kb = keybindings[keyshift[key]];
+			if (kb && kb[0] == '+')
+			{
+				sprintf (cmd, "-%s %i\n", kb+1, key);
+				Cbuf_AddText (cmd);
+			}
+		}
+		return;
+	}
+
+//
+// during demo playback, most keys bring up the main menu
+//
+	if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
+	{
+		M_ToggleMenu_f ();
+		return;
+	}
+
+//
+// if not a consolekey, send to the interpreter no matter what mode is
+//
+	if ( (key_dest == key_menu && menubound[key])
+	|| (key_dest == key_console && !consolekeys[key])
+	|| (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) )
+	{
+		kb = keybindings[key];
+		if (kb)
+		{
+			if (kb[0] == '+')
+			{	// button commands add keynum as a parm
+				sprintf (cmd, "%s %i\n", kb, key);
+				Cbuf_AddText (cmd);
+			}
+			else
+			{
+				Cbuf_AddText (kb);
+				Cbuf_AddText ("\n");
+			}
+		}
+		return;
+	}
+
+	if (!down)
+		return;		// other systems only care about key down events
+
+	if (shift_down)
+	{
+		key = keyshift[key];
+	}
+
+	switch (key_dest)
+	{
+	case key_message:
+		Key_Message (key);
+		break;
+	case key_menu:
+		M_Keydown (key);
+		break;
+
+	case key_game:
+	case key_console:
+            if(key == K_ENTER)
+            {
+                rb_console();
+		Key_Console (K_ENTER);
+            }
+            else
+                Key_Console (key);
+		break;
+	default:
+		Sys_Error ("Bad key_dest");
+	}
+}
+
+
+/*
+===================
+Key_ClearStates
+===================
+*/
+void Key_ClearStates (void)
+{
+	int		i;
+
+	for (i=0 ; i<256 ; i++)
+	{
+		keydown[i] = false;
+		key_repeats[i] = 0;
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/keys.h b/apps/plugins/sdl/progs/quake/keys.h
new file mode 100644
index 0000000..ed26a8d
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/keys.h
@@ -0,0 +1,134 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// these are the key numbers that should be passed to Key_Event
+//
+#define	K_TAB			9
+#define	K_ENTER			13
+#define	K_ESCAPE		27
+#define	K_SPACE			32
+
+// normal keys should be passed as lowercased ascii
+
+#define	K_BACKSPACE		127
+#define	K_UPARROW		128
+#define	K_DOWNARROW		129
+#define	K_LEFTARROW		130
+#define	K_RIGHTARROW	131
+
+#define	K_ALT			132
+#define	K_CTRL			133
+#define	K_SHIFT			134
+#define	K_F1			135
+#define	K_F2			136
+#define	K_F3			137
+#define	K_F4			138
+#define	K_F5			139
+#define	K_F6			140
+#define	K_F7			141
+#define	K_F8			142
+#define	K_F9			143
+#define	K_F10			144
+#define	K_F11			145
+#define	K_F12			146
+#define	K_INS			147
+#define	K_DEL			148
+#define	K_PGDN			149
+#define	K_PGUP			150
+#define	K_HOME			151
+#define	K_END			152
+
+#define K_PAUSE			255
+
+//
+// mouse buttons generate virtual keys
+//
+#define	K_MOUSE1		200
+#define	K_MOUSE2		201
+#define	K_MOUSE3		202
+
+//
+// joystick buttons
+//
+#define	K_JOY1			203
+#define	K_JOY2			204
+#define	K_JOY3			205
+#define	K_JOY4			206
+
+//
+// aux keys are for multi-buttoned joysticks to generate so they can use
+// the normal binding process
+//
+#define	K_AUX1			207
+#define	K_AUX2			208
+#define	K_AUX3			209
+#define	K_AUX4			210
+#define	K_AUX5			211
+#define	K_AUX6			212
+#define	K_AUX7			213
+#define	K_AUX8			214
+#define	K_AUX9			215
+#define	K_AUX10			216
+#define	K_AUX11			217
+#define	K_AUX12			218
+#define	K_AUX13			219
+#define	K_AUX14			220
+#define	K_AUX15			221
+#define	K_AUX16			222
+#define	K_AUX17			223
+#define	K_AUX18			224
+#define	K_AUX19			225
+#define	K_AUX20			226
+#define	K_AUX21			227
+#define	K_AUX22			228
+#define	K_AUX23			229
+#define	K_AUX24			230
+#define	K_AUX25			231
+#define	K_AUX26			232
+#define	K_AUX27			233
+#define	K_AUX28			234
+#define	K_AUX29			235
+#define	K_AUX30			236
+#define	K_AUX31			237
+#define	K_AUX32			238
+
+// JACK: Intellimouse(c) Mouse Wheel Support
+
+#define K_MWHEELUP		239
+#define K_MWHEELDOWN	240
+
+
+
+typedef int keydest_t;
+enum {key_game, key_console, key_message, key_menu};
+
+extern keydest_t	key_dest;
+extern char *keybindings[256];
+extern	int		key_repeats[256];
+extern	int		key_count;			// incremented every key event
+extern	int		key_lastpress;
+
+void Key_Event (int key, qboolean down);
+void Key_Init (void);
+void Key_WriteBindings (FILE *f);
+void Key_SetBinding (int keynum, char *binding);
+void Key_ClearStates (void);
+
diff --git a/apps/plugins/sdl/progs/quake/mathlib.c b/apps/plugins/sdl/progs/quake/mathlib.c
new file mode 100644
index 0000000..ddd5435
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/mathlib.c
@@ -0,0 +1,550 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// mathlib.c -- math primitives
+
+#include <math.h>
+#include "quakedef.h"
+
+void Sys_Error (char *error, ...);
+
+vec3_t vec3_origin = {0,0,0};
+int nanmask = 255<<23;
+
+/*-----------------------------------------------------------------*/
+
+#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
+
+void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
+{
+	float d;
+	vec3_t n;
+	float inv_denom;
+
+	inv_denom = 1.0F / DotProduct( normal, normal );
+
+	d = DotProduct( normal, p ) * inv_denom;
+
+	n[0] = normal[0] * inv_denom;
+	n[1] = normal[1] * inv_denom;
+	n[2] = normal[2] * inv_denom;
+
+	dst[0] = p[0] - d * n[0];
+	dst[1] = p[1] - d * n[1];
+	dst[2] = p[2] - d * n[2];
+}
+
+/*
+** assumes "src" is normalized
+*/
+void PerpendicularVector( vec3_t dst, const vec3_t src )
+{
+	int	pos;
+	int i;
+	float minelem = 1.0F;
+	vec3_t tempvec;
+
+	/*
+	** find the smallest magnitude axially aligned vector
+	*/
+	for ( pos = 0, i = 0; i < 3; i++ )
+	{
+		if ( fabs( src[i] ) < minelem )
+		{
+			pos = i;
+			minelem = fabs( src[i] );
+		}
+	}
+	tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
+	tempvec[pos] = 1.0F;
+
+	/*
+	** project the point onto the plane defined by src
+	*/
+	ProjectPointOnPlane( dst, tempvec, src );
+
+	/*
+	** normalize the result
+	*/
+	VectorNormalizeNoRet( dst );
+}
+
+#ifdef _WIN32
+#pragma optimize( "", off )
+#endif
+
+
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees )
+{
+	float	m[3][3];
+	float	im[3][3];
+	float	zrot[3][3];
+	float	tmpmat[3][3];
+	float	rot[3][3];
+	int	i;
+	vec3_t vr, vup, vf;
+
+	vf[0] = dir[0];
+	vf[1] = dir[1];
+	vf[2] = dir[2];
+
+	PerpendicularVector( vr, dir );
+	CrossProduct( vr, vf, vup );
+
+	m[0][0] = vr[0];
+	m[1][0] = vr[1];
+	m[2][0] = vr[2];
+
+	m[0][1] = vup[0];
+	m[1][1] = vup[1];
+	m[2][1] = vup[2];
+
+	m[0][2] = vf[0];
+	m[1][2] = vf[1];
+	m[2][2] = vf[2];
+
+	memcpy( im, m, sizeof( im ) );
+
+	im[0][1] = m[1][0];
+	im[0][2] = m[2][0];
+	im[1][0] = m[0][1];
+	im[1][2] = m[2][1];
+	im[2][0] = m[0][2];
+	im[2][1] = m[1][2];
+
+	memset( zrot, 0, sizeof( zrot ) );
+	zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
+
+	zrot[0][0] = cos( DEG2RAD( degrees ) );
+	zrot[0][1] = sin( DEG2RAD( degrees ) );
+	zrot[1][0] = -sin( DEG2RAD( degrees ) );
+	zrot[1][1] = cos( DEG2RAD( degrees ) );
+
+	R_ConcatRotations( m, zrot, tmpmat );
+	R_ConcatRotations( tmpmat, im, rot );
+
+	for ( i = 0; i < 3; i++ )
+	{
+		dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
+	}
+}
+
+#ifdef _WIN32
+#pragma optimize( "", on )
+#endif
+
+/*-----------------------------------------------------------------*/
+
+
+float	anglemod(float a)
+{
+#if 0
+	if (a >= 0)
+		a -= 360*(int)(a/360);
+	else
+		a += 360*( 1 + (int)(-a/360) );
+#endif
+	a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
+	return a;
+}
+
+/*
+==================
+BOPS_Error
+
+Split out like this for ASM to call.
+==================
+*/
+void BOPS_Error (void)
+{
+	Sys_Error ("BoxOnPlaneSide:  Bad signbits");
+}
+
+
+#if	!id386
+
+/*
+==================
+BoxOnPlaneSide
+
+Returns 1, 2, or 1 + 2
+==================
+*/
+int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p)
+{
+	float	dist1, dist2;
+	int		sides;
+
+#if 0	// this is done by the BOX_ON_PLANE_SIDE macro before calling this
+		// function
+// fast axial cases
+	if (p->type < 3)
+	{
+		if (p->dist <= emins[p->type])
+			return 1;
+		if (p->dist >= emaxs[p->type])
+			return 2;
+		return 3;
+	}
+#endif
+	
+// general case
+	switch (p->signbits)
+	{
+	case 0:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+		break;
+	case 1:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+		break;
+	case 2:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+		break;
+	case 3:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+		break;
+	case 4:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+		break;
+	case 5:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
+		break;
+	case 6:
+dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+		break;
+	case 7:
+dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
+dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
+		break;
+	default:
+		dist1 = dist2 = 0;		// shut up compiler
+		BOPS_Error ();
+		break;
+	}
+
+#if 0
+	int		i;
+	vec3_t	corners[2];
+
+	for (i=0 ; i<3 ; i++)
+	{
+		if (plane->normal[i] < 0)
+		{
+			corners[0][i] = emins[i];
+			corners[1][i] = emaxs[i];
+		}
+		else
+		{
+			corners[1][i] = emins[i];
+			corners[0][i] = emaxs[i];
+		}
+	}
+	dist = DotProduct (plane->normal, corners[0]) - plane->dist;
+	dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+	sides = 0;
+	if (dist1 >= 0)
+		sides = 1;
+	if (dist2 < 0)
+		sides |= 2;
+
+#endif
+
+	sides = 0;
+	if (dist1 >= p->dist)
+		sides = 1;
+	if (dist2 < p->dist)
+		sides |= 2;
+
+#ifdef PARANOID
+if (sides == 0)
+	Sys_Error ("BoxOnPlaneSide: sides==0");
+#endif
+
+	return sides;
+}
+
+#endif
+
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
+{
+	float		angle;
+	float		sr, sp, sy, cr, cp, cy;
+	
+	angle = angles[YAW] * (M_PI*2 / 360);
+	sy = sin(angle);
+	cy = cos(angle);
+	angle = angles[PITCH] * (M_PI*2 / 360);
+	sp = sin(angle);
+	cp = cos(angle);
+	angle = angles[ROLL] * (M_PI*2 / 360);
+	sr = sin(angle);
+	cr = cos(angle);
+
+	forward[0] = cp*cy;
+	forward[1] = cp*sy;
+	forward[2] = -sp;
+	right[0] = (-1*sr*sp*cy+-1*cr*-sy);
+	right[1] = (-1*sr*sp*sy+-1*cr*cy);
+	right[2] = -1*sr*cp;
+	up[0] = (cr*sp*cy+-sr*-sy);
+	up[1] = (cr*sp*sy+-sr*cy);
+	up[2] = cr*cp;
+}
+
+int VectorCompare (vec3_t v1, vec3_t v2)
+{
+	int		i;
+	
+	for (i=0 ; i<3 ; i++)
+		if (v1[i] != v2[i])
+			return 0;
+			
+	return 1;
+}
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
+{
+	vecc[0] = veca[0] + scale*vecb[0];
+	vecc[1] = veca[1] + scale*vecb[1];
+	vecc[2] = veca[2] + scale*vecb[2];
+}
+
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2)
+{
+	return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+	out[0] = veca[0]-vecb[0];
+	out[1] = veca[1]-vecb[1];
+	out[2] = veca[2]-vecb[2];
+}
+
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
+{
+	out[0] = veca[0]+vecb[0];
+	out[1] = veca[1]+vecb[1];
+	out[2] = veca[2]+vecb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+	out[0] = in[0];
+	out[1] = in[1];
+	out[2] = in[2];
+}
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
+{
+	cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
+	cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
+	cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+vec_t Length(vec3_t v)
+{
+	int		i;
+	float	length;
+	
+	length = 0;
+	for (i=0 ; i< 3 ; i++)
+		length += v[i]*v[i];
+	length = sqrt (length);		// FIXME
+
+	return length;
+}
+
+
+int Q_log2(int val)
+{
+	int answer=0;
+	while (val>>=1)
+		answer++;
+	return answer;
+}
+
+
+/*
+================
+R_ConcatRotations
+================
+*/
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
+{
+	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
+				in1[0][2] * in2[2][0];
+	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
+				in1[0][2] * in2[2][1];
+	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
+				in1[0][2] * in2[2][2];
+	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
+				in1[1][2] * in2[2][0];
+	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
+				in1[1][2] * in2[2][1];
+	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
+				in1[1][2] * in2[2][2];
+	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
+				in1[2][2] * in2[2][0];
+	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
+				in1[2][2] * in2[2][1];
+	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
+				in1[2][2] * in2[2][2];
+}
+
+
+/*
+================
+R_ConcatTransforms
+================
+*/
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
+{
+	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
+				in1[0][2] * in2[2][0];
+	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
+				in1[0][2] * in2[2][1];
+	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
+				in1[0][2] * in2[2][2];
+	out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
+				in1[0][2] * in2[2][3] + in1[0][3];
+	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
+				in1[1][2] * in2[2][0];
+	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
+				in1[1][2] * in2[2][1];
+	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
+				in1[1][2] * in2[2][2];
+	out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
+				in1[1][2] * in2[2][3] + in1[1][3];
+	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
+				in1[2][2] * in2[2][0];
+	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
+				in1[2][2] * in2[2][1];
+	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
+				in1[2][2] * in2[2][2];
+	out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
+				in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+
+/*
+===================
+FloorDivMod
+
+Returns mathematically correct (floor-based) quotient and remainder for
+numer and denom, both of which should contain no fractional part. The
+quotient must fit in 32 bits.
+====================
+*/
+
+void FloorDivMod (double numer, double denom, int *quotient,
+		int *rem)
+{
+	int		q, r;
+	double	x;
+
+#ifndef PARANOID
+	if (denom <= 0.0)
+		Sys_Error ("FloorDivMod: bad denominator %d\n", denom);
+
+//	if ((floor(numer) != numer) || (floor(denom) != denom))
+//		Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
+//				numer, denom);
+#endif
+
+	if (numer >= 0.0)
+	{
+
+		x = floor(numer / denom);
+		q = (int)x;
+		r = (int)floor(numer - (x * denom));
+	}
+	else
+	{
+	//
+	// perform operations with positive values, and fix mod to make floor-based
+	//
+		x = floor(-numer / denom);
+		q = -(int)x;
+		r = (int)floor(-numer - (x * denom));
+		if (r != 0)
+		{
+			q--;
+			r = (int)denom - r;
+		}
+	}
+
+	*quotient = q;
+	*rem = r;
+}
+
+
+/*
+===================
+GreatestCommonDivisor
+====================
+*/
+int GreatestCommonDivisor (int i1, int i2)
+{
+	if (i1 > i2)
+	{
+		if (i2 == 0)
+			return (i1);
+		return GreatestCommonDivisor (i2, i1 % i2);
+	}
+	else
+	{
+		if (i1 == 0)
+			return (i2);
+		return GreatestCommonDivisor (i1, i2 % i1);
+	}
+}
+
+
+#if	!id386
+
+// TODO: move to nonintel.c
+
+/*
+===================
+Invert24To16
+
+Inverts an 8.24 value to a 16.16 value
+====================
+*/
+
+fixed16_t Invert24To16(fixed16_t val)
+{
+	if (val < 256)
+		return (0xFFFFFFFF);
+
+	return (fixed16_t)
+			(((double)0x10000 * (double)0x1000000 / (double)val) + 0.5);
+}
+
+#endif
diff --git a/apps/plugins/sdl/progs/quake/mathlib.h b/apps/plugins/sdl/progs/quake/mathlib.h
new file mode 100644
index 0000000..b754966
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/mathlib.h
@@ -0,0 +1,154 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// mathlib.h
+
+typedef float vec_t;
+typedef vec_t vec3_t[3];
+typedef vec_t vec5_t[5];
+
+typedef	int	fixed4_t;
+typedef	int	fixed8_t;
+typedef	int	fixed16_t;
+
+#ifndef M_PI
+#define M_PI		3.14159265358979323846	// matches value in gcc v2 math.h
+#endif
+
+struct mplane_s;
+
+extern vec3_t vec3_origin;
+extern	int nanmask;
+
+#define	IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+
+void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
+
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+int VectorCompare (vec3_t v1, vec3_t v2);
+vec_t Length (vec3_t v);
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+//void VectorNormalizeNoRet (vec3_t v); // uses finvsqrt
+//float VectorNormalize (vec3_t v);		// returns vector length
+
+static inline float InvSqrt( float number ) {
+    long i;
+    float x2, y;
+    const float threehalfs = 1.5F;
+ 
+    x2 = number * 0.5F;
+    y  = number;
+    i  = * ( long * ) &y;			// evil floating point bit level hacking
+    i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
+    y  = * ( float * ) &i;
+    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
+//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
+
+    return y;
+}
+
+static inline void VectorNormalizeNoRet (vec3_t v)
+{
+	float	length, ilength;
+
+	ilength = InvSqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+        
+        v[0] *= ilength;
+        v[1] *= ilength;
+        v[2] *= ilength;
+}
+
+static inline float VectorNormalize (vec3_t v)
+{
+	float	length, ilength;
+
+	length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+	length = sqrt (length);		// FIXME
+
+	if (length)
+	{
+		ilength = 1/length;
+		v[0] *= ilength;
+		v[1] *= ilength;
+		v[2] *= ilength;
+	}
+		
+	return length;
+
+}
+
+//void VectorInverse (vec3_t v);
+//void VectorScale (vec3_t in, vec_t scale, vec3_t out);
+
+
+static inline void VectorInverse (vec3_t v)
+{
+	v[0] = -v[0];
+	v[1] = -v[1];
+	v[2] = -v[2];
+}
+
+static inline void VectorScale (vec3_t in, vec_t scale, vec3_t out)
+{
+	out[0] = in[0]*scale;
+	out[1] = in[1]*scale;
+	out[2] = in[2]*scale;
+}
+
+
+int Q_log2(int val);
+
+void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
+void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]);
+
+void FloorDivMod (double numer, double denom, int *quotient,
+		int *rem);
+fixed16_t Invert24To16(fixed16_t val);
+int GreatestCommonDivisor (int i1, int i2);
+
+void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
+int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
+float	anglemod(float a);
+
+
+
+#define BOX_ON_PLANE_SIDE(emins, emaxs, p)	\
+	(((p)->type < 3)?						\
+	(										\
+		((p)->dist <= (emins)[(p)->type])?	\
+			1								\
+		:									\
+		(									\
+			((p)->dist >= (emaxs)[(p)->type])?\
+				2							\
+			:								\
+				3							\
+		)									\
+	)										\
+	:										\
+		BoxOnPlaneSide( (emins), (emaxs), (p)))
diff --git a/apps/plugins/sdl/progs/quake/menu.c b/apps/plugins/sdl/progs/quake/menu.c
new file mode 100644
index 0000000..8c67c86
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/menu.c
@@ -0,0 +1,3234 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+#ifdef _WIN32
+#include "winquake.h"
+#endif
+
+void (*vid_menudrawfn)(void);
+void (*vid_menukeyfn)(int key);
+
+enum {m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, m_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, m_search, m_slist} m_state;
+
+void M_Menu_Main_f (void);
+	void M_Menu_SinglePlayer_f (void);
+		void M_Menu_Load_f (void);
+		void M_Menu_Save_f (void);
+	void M_Menu_MultiPlayer_f (void);
+		void M_Menu_Setup_f (void);
+		void M_Menu_Net_f (void);
+	void M_Menu_Options_f (void);
+		void M_Menu_Keys_f (void);
+		void M_Menu_Video_f (void);
+	void M_Menu_Help_f (void);
+	void M_Menu_Quit_f (void);
+void M_Menu_SerialConfig_f (void);
+	void M_Menu_ModemConfig_f (void);
+void M_Menu_LanConfig_f (void);
+void M_Menu_GameOptions_f (void);
+void M_Menu_Search_f (void);
+void M_Menu_ServerList_f (void);
+
+void M_Main_Draw (void);
+	void M_SinglePlayer_Draw (void);
+		void M_Load_Draw (void);
+		void M_Save_Draw (void);
+	void M_MultiPlayer_Draw (void);
+		void M_Setup_Draw (void);
+		void M_Net_Draw (void);
+	void M_Options_Draw (void);
+		void M_Keys_Draw (void);
+		void M_Video_Draw (void);
+	void M_Help_Draw (void);
+	void M_Quit_Draw (void);
+void M_SerialConfig_Draw (void);
+	void M_ModemConfig_Draw (void);
+void M_LanConfig_Draw (void);
+void M_GameOptions_Draw (void);
+void M_Search_Draw (void);
+void M_ServerList_Draw (void);
+
+void M_Main_Key (int key);
+	void M_SinglePlayer_Key (int key);
+		void M_Load_Key (int key);
+		void M_Save_Key (int key);
+	void M_MultiPlayer_Key (int key);
+		void M_Setup_Key (int key);
+		void M_Net_Key (int key);
+	void M_Options_Key (int key);
+		void M_Keys_Key (int key);
+		void M_Video_Key (int key);
+	void M_Help_Key (int key);
+	void M_Quit_Key (int key);
+void M_SerialConfig_Key (int key);
+	void M_ModemConfig_Key (int key);
+void M_LanConfig_Key (int key);
+void M_GameOptions_Key (int key);
+void M_Search_Key (int key);
+void M_ServerList_Key (int key);
+
+qboolean	m_entersound;		// play after drawing a frame, so caching
+								// won't disrupt the sound
+qboolean	m_recursiveDraw;
+
+int			m_return_state;
+qboolean	m_return_onerror;
+char		m_return_reason [32];
+
+#define StartingGame	(m_multiplayer_cursor == 1)
+#define JoiningGame		(m_multiplayer_cursor == 0)
+#define SerialConfig	(m_net_cursor == 0)
+#define DirectConfig	(m_net_cursor == 1)
+#define	IPXConfig		(m_net_cursor == 2)
+#define	TCPIPConfig		(m_net_cursor == 3)
+
+void M_ConfigureNetSubsystem(void);
+
+/*
+================
+M_DrawCharacter
+
+Draws one solid graphics character
+================
+*/
+void M_DrawCharacter (int cx, int line, int num)
+{
+	Draw_Character ( cx + ((vid.width - 320)>>1), line, num);
+}
+
+void M_Print (int cx, int cy, char *str)
+{
+	while (*str)
+	{
+		M_DrawCharacter (cx, cy, (*str)+128);
+		str++;
+		cx += 8;
+	}
+}
+
+void M_PrintWhite (int cx, int cy, char *str)
+{
+	while (*str)
+	{
+		M_DrawCharacter (cx, cy, *str);
+		str++;
+		cx += 8;
+	}
+}
+
+void M_DrawTransPic (int x, int y, qpic_t *pic)
+{
+	Draw_TransPic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+void M_DrawPic (int x, int y, qpic_t *pic)
+{
+	Draw_Pic (x + ((vid.width - 320)>>1), y, pic);
+}
+
+byte identityTable[256];
+byte translationTable[256];
+
+void M_BuildTranslationTable(int top, int bottom)
+{
+	int		j;
+	byte	*dest, *source;
+
+	for (j = 0; j < 256; j++)
+		identityTable[j] = j;
+	dest = translationTable;
+	source = identityTable;
+	memcpy (dest, source, 256);
+
+	if (top < 128)	// the artists made some backwards ranges.  sigh.
+		memcpy (dest + TOP_RANGE, source + top, 16);
+	else
+		for (j=0 ; j<16 ; j++)
+			dest[TOP_RANGE+j] = source[top+15-j];
+
+	if (bottom < 128)
+		memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+	else
+		for (j=0 ; j<16 ; j++)
+			dest[BOTTOM_RANGE+j] = source[bottom+15-j];
+}
+
+
+void M_DrawTransPicTranslate (int x, int y, qpic_t *pic)
+{
+	Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable);
+}
+
+
+void M_DrawTextBox (int x, int y, int width, int lines)
+{
+	qpic_t	*p;
+	int		cx, cy;
+	int		n;
+
+	// draw left side
+	cx = x;
+	cy = y;
+	p = Draw_CachePic ("gfx/box_tl.lmp");
+	M_DrawTransPic (cx, cy, p);
+	p = Draw_CachePic ("gfx/box_ml.lmp");
+	for (n = 0; n < lines; n++)
+	{
+		cy += 8;
+		M_DrawTransPic (cx, cy, p);
+	}
+	p = Draw_CachePic ("gfx/box_bl.lmp");
+	M_DrawTransPic (cx, cy+8, p);
+
+	// draw middle
+	cx += 8;
+	while (width > 0)
+	{
+		cy = y;
+		p = Draw_CachePic ("gfx/box_tm.lmp");
+		M_DrawTransPic (cx, cy, p);
+		p = Draw_CachePic ("gfx/box_mm.lmp");
+		for (n = 0; n < lines; n++)
+		{
+			cy += 8;
+			if (n == 1)
+				p = Draw_CachePic ("gfx/box_mm2.lmp");
+			M_DrawTransPic (cx, cy, p);
+		}
+		p = Draw_CachePic ("gfx/box_bm.lmp");
+		M_DrawTransPic (cx, cy+8, p);
+		width -= 2;
+		cx += 16;
+	}
+
+	// draw right side
+	cy = y;
+	p = Draw_CachePic ("gfx/box_tr.lmp");
+	M_DrawTransPic (cx, cy, p);
+	p = Draw_CachePic ("gfx/box_mr.lmp");
+	for (n = 0; n < lines; n++)
+	{
+		cy += 8;
+		M_DrawTransPic (cx, cy, p);
+	}
+	p = Draw_CachePic ("gfx/box_br.lmp");
+	M_DrawTransPic (cx, cy+8, p);
+}
+
+//=============================================================================
+
+int m_save_demonum;
+
+/*
+================
+M_ToggleMenu_f
+================
+*/
+void M_ToggleMenu_f (void)
+{
+	m_entersound = true;
+
+	if (key_dest == key_menu)
+	{
+		if (m_state != m_main)
+		{
+			M_Menu_Main_f ();
+			return;
+		}
+		key_dest = key_game;
+		m_state = m_none;
+		return;
+	}
+	if (key_dest == key_console)
+	{
+		Con_ToggleConsole_f ();
+	}
+	else
+	{
+		M_Menu_Main_f ();
+	}
+}
+
+
+//=============================================================================
+/* MAIN MENU */
+
+int	m_main_cursor;
+#define	MAIN_ITEMS	5
+
+
+void M_Menu_Main_f (void)
+{
+	if (key_dest != key_menu)
+	{
+		m_save_demonum = cls.demonum;
+		cls.demonum = -1;
+	}
+	key_dest = key_menu;
+	m_state = m_main;
+	m_entersound = true;
+}
+
+
+void M_Main_Draw (void)
+{
+	int		f;
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/ttl_main.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+	M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp") );
+
+	f = (int)(host_time * 10)%6;
+
+	M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_Main_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+		key_dest = key_game;
+		m_state = m_none;
+		cls.demonum = m_save_demonum;
+		if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected)
+			CL_NextDemo ();
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (++m_main_cursor >= MAIN_ITEMS)
+			m_main_cursor = 0;
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (--m_main_cursor < 0)
+			m_main_cursor = MAIN_ITEMS - 1;
+		break;
+
+	case K_ENTER:
+		m_entersound = true;
+
+		switch (m_main_cursor)
+		{
+		case 0:
+			M_Menu_SinglePlayer_f ();
+			break;
+
+		case 1:
+			M_Menu_MultiPlayer_f ();
+			break;
+
+		case 2:
+			M_Menu_Options_f ();
+			break;
+
+		case 3:
+			M_Menu_Help_f ();
+			break;
+
+		case 4:
+			M_Menu_Quit_f ();
+			break;
+		}
+	}
+}
+
+//=============================================================================
+/* SINGLE PLAYER MENU */
+
+int	m_singleplayer_cursor;
+#define	SINGLEPLAYER_ITEMS	3
+
+
+void M_Menu_SinglePlayer_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_singleplayer;
+	m_entersound = true;
+}
+
+
+void M_SinglePlayer_Draw (void)
+{
+	int		f;
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/ttl_sgl.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+	M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") );
+
+	f = (int)(host_time * 10)%6;
+
+	M_DrawTransPic (54, 32 + m_singleplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_SinglePlayer_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Main_f ();
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS)
+			m_singleplayer_cursor = 0;
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (--m_singleplayer_cursor < 0)
+			m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
+		break;
+
+	case K_ENTER:
+		m_entersound = true;
+
+		switch (m_singleplayer_cursor)
+		{
+		case 0:
+			if (sv.active)
+				if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n"))
+					break;
+			key_dest = key_game;
+			if (sv.active)
+				Cbuf_AddText ("disconnect\n");
+			Cbuf_AddText ("maxplayers 1\n");
+			Cbuf_AddText ("map start\n");
+			break;
+
+		case 1:
+			M_Menu_Load_f ();
+			break;
+
+		case 2:
+			M_Menu_Save_f ();
+			break;
+		}
+	}
+}
+
+//=============================================================================
+/* LOAD/SAVE MENU */
+
+int		load_cursor;		// 0 < load_cursor < MAX_SAVEGAMES
+
+#define	MAX_SAVEGAMES		12
+char	m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1];
+int		loadable[MAX_SAVEGAMES];
+
+void M_ScanSaves (void)
+{
+	int		i, j;
+	char	name[MAX_OSPATH];
+	FILE	*f;
+	int		version;
+
+	for (i=0 ; i<MAX_SAVEGAMES ; i++)
+	{
+		strcpy (m_filenames[i], "--- UNUSED SLOT ---");
+		loadable[i] = false;
+		sprintf (name, "%s/s%i.sav", com_gamedir, i);
+		f = fopen (name, "r");
+		if (!f)
+			continue;
+		fscanf (f, "%i\n", &version);
+		fscanf (f, "%s\n", name);
+		strncpy (m_filenames[i], name, sizeof(m_filenames[i])-1);
+
+	// change _ back to space
+		for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
+			if (m_filenames[i][j] == '_')
+				m_filenames[i][j] = ' ';
+		loadable[i] = true;
+		fclose (f);
+	}
+}
+
+void M_Menu_Load_f (void)
+{
+	m_entersound = true;
+	m_state = m_load;
+	key_dest = key_menu;
+	M_ScanSaves ();
+}
+
+
+void M_Menu_Save_f (void)
+{
+	if (!sv.active)
+		return;
+	if (cl.intermission)
+		return;
+	if (svs.maxclients != 1)
+		return;
+	m_entersound = true;
+	m_state = m_save;
+	key_dest = key_menu;
+	M_ScanSaves ();
+}
+
+
+void M_Load_Draw (void)
+{
+	int		i;
+	qpic_t	*p;
+
+	p = Draw_CachePic ("gfx/p_load.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	for (i=0 ; i< MAX_SAVEGAMES; i++)
+		M_Print (16, 32 + 8*i, m_filenames[i]);
+
+// line cursor
+	M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Save_Draw (void)
+{
+	int		i;
+	qpic_t	*p;
+
+	p = Draw_CachePic ("gfx/p_save.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	for (i=0 ; i<MAX_SAVEGAMES ; i++)
+		M_Print (16, 32 + 8*i, m_filenames[i]);
+
+// line cursor
+	M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Load_Key (int k)
+{
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_SinglePlayer_f ();
+		break;
+
+	case K_ENTER:
+		S_LocalSound ("misc/menu2.wav");
+		if (!loadable[load_cursor])
+			return;
+		m_state = m_none;
+		key_dest = key_game;
+
+	// Host_Loadgame_f can't bring up the loading plaque because too much
+	// stack space has been used, so do it now
+		SCR_BeginLoadingPlaque ();
+
+	// issue the load command
+		Cbuf_AddText (va ("load s%i\n", load_cursor) );
+		return;
+
+	case K_UPARROW:
+	case K_LEFTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		load_cursor--;
+		if (load_cursor < 0)
+			load_cursor = MAX_SAVEGAMES-1;
+		break;
+
+	case K_DOWNARROW:
+	case K_RIGHTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		load_cursor++;
+		if (load_cursor >= MAX_SAVEGAMES)
+			load_cursor = 0;
+		break;
+	}
+}
+
+
+void M_Save_Key (int k)
+{
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_SinglePlayer_f ();
+		break;
+
+	case K_ENTER:
+		m_state = m_none;
+		key_dest = key_game;
+		Cbuf_AddText (va("save s%i\n", load_cursor));
+		return;
+
+	case K_UPARROW:
+	case K_LEFTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		load_cursor--;
+		if (load_cursor < 0)
+			load_cursor = MAX_SAVEGAMES-1;
+		break;
+
+	case K_DOWNARROW:
+	case K_RIGHTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		load_cursor++;
+		if (load_cursor >= MAX_SAVEGAMES)
+			load_cursor = 0;
+		break;
+	}
+}
+
+//=============================================================================
+/* MULTIPLAYER MENU */
+
+int	m_multiplayer_cursor;
+#define	MULTIPLAYER_ITEMS	3
+
+
+void M_Menu_MultiPlayer_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_multiplayer;
+	m_entersound = true;
+}
+
+
+void M_MultiPlayer_Draw (void)
+{
+	int		f;
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+	M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") );
+
+	f = (int)(host_time * 10)%6;
+
+	M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+
+	if (serialAvailable || ipxAvailable || tcpipAvailable)
+		return;
+	M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
+}
+
+
+void M_MultiPlayer_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Main_f ();
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
+			m_multiplayer_cursor = 0;
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (--m_multiplayer_cursor < 0)
+			m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
+		break;
+
+	case K_ENTER:
+		m_entersound = true;
+		switch (m_multiplayer_cursor)
+		{
+		case 0:
+			if (serialAvailable || ipxAvailable || tcpipAvailable)
+				M_Menu_Net_f ();
+			break;
+
+		case 1:
+			if (serialAvailable || ipxAvailable || tcpipAvailable)
+				M_Menu_Net_f ();
+			break;
+
+		case 2:
+			M_Menu_Setup_f ();
+			break;
+		}
+	}
+}
+
+//=============================================================================
+/* SETUP MENU */
+
+int		setup_cursor = 4;
+int		setup_cursor_table[] = {40, 56, 80, 104, 140};
+
+char	setup_hostname[16];
+char	setup_myname[16];
+int		setup_oldtop;
+int		setup_oldbottom;
+int		setup_top;
+int		setup_bottom;
+
+#define	NUM_SETUP_CMDS	5
+
+void M_Menu_Setup_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_setup;
+	m_entersound = true;
+	Q_strcpy(setup_myname, cl_name.string);
+	Q_strcpy(setup_hostname, hostname.string);
+	setup_top = setup_oldtop = ((int)cl_color.value) >> 4;
+	setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15;
+}
+
+
+void M_Setup_Draw (void)
+{
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	M_Print (64, 40, "Hostname");
+	M_DrawTextBox (160, 32, 16, 1);
+	M_Print (168, 40, setup_hostname);
+
+	M_Print (64, 56, "Your name");
+	M_DrawTextBox (160, 48, 16, 1);
+	M_Print (168, 56, setup_myname);
+
+	M_Print (64, 80, "Shirt color");
+	M_Print (64, 104, "Pants color");
+
+	M_DrawTextBox (64, 140-8, 14, 1);
+	M_Print (72, 140, "Accept Changes");
+
+	p = Draw_CachePic ("gfx/bigbox.lmp");
+	M_DrawTransPic (160, 64, p);
+	p = Draw_CachePic ("gfx/menuplyr.lmp");
+	M_BuildTranslationTable(setup_top*16, setup_bottom*16);
+	M_DrawTransPicTranslate (172, 72, p);
+
+	M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
+
+	if (setup_cursor == 0)
+		M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
+
+	if (setup_cursor == 1)
+		M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
+}
+
+
+void M_Setup_Key (int k)
+{
+	int			l;
+
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_MultiPlayer_f ();
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		setup_cursor--;
+		if (setup_cursor < 0)
+			setup_cursor = NUM_SETUP_CMDS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		setup_cursor++;
+		if (setup_cursor >= NUM_SETUP_CMDS)
+			setup_cursor = 0;
+		break;
+
+	case K_LEFTARROW:
+		if (setup_cursor < 2)
+			return;
+		S_LocalSound ("misc/menu3.wav");
+		if (setup_cursor == 2)
+			setup_top = setup_top - 1;
+		if (setup_cursor == 3)
+			setup_bottom = setup_bottom - 1;
+		break;
+	case K_RIGHTARROW:
+		if (setup_cursor < 2)
+			return;
+forward:
+		S_LocalSound ("misc/menu3.wav");
+		if (setup_cursor == 2)
+			setup_top = setup_top + 1;
+		if (setup_cursor == 3)
+			setup_bottom = setup_bottom + 1;
+		break;
+
+	case K_ENTER:
+		if (setup_cursor == 0 || setup_cursor == 1)
+			return;
+
+		if (setup_cursor == 2 || setup_cursor == 3)
+			goto forward;
+
+		// setup_cursor == 4 (OK)
+		if (Q_strcmp(cl_name.string, setup_myname) != 0)
+			Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
+		if (Q_strcmp(hostname.string, setup_hostname) != 0)
+			Cvar_Set("hostname", setup_hostname);
+		if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
+			Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
+		m_entersound = true;
+		M_Menu_MultiPlayer_f ();
+		break;
+
+	case K_BACKSPACE:
+		if (setup_cursor == 0)
+		{
+			if (strlen(setup_hostname))
+				setup_hostname[strlen(setup_hostname)-1] = 0;
+		}
+
+		if (setup_cursor == 1)
+		{
+			if (strlen(setup_myname))
+				setup_myname[strlen(setup_myname)-1] = 0;
+		}
+		break;
+
+	default:
+		if (k < 32 || k > 127)
+			break;
+		if (setup_cursor == 0)
+		{
+			l = strlen(setup_hostname);
+			if (l < 15)
+			{
+				setup_hostname[l+1] = 0;
+				setup_hostname[l] = k;
+			}
+		}
+		if (setup_cursor == 1)
+		{
+			l = strlen(setup_myname);
+			if (l < 15)
+			{
+				setup_myname[l+1] = 0;
+				setup_myname[l] = k;
+			}
+		}
+	}
+
+	if (setup_top > 13)
+		setup_top = 0;
+	if (setup_top < 0)
+		setup_top = 13;
+	if (setup_bottom > 13)
+		setup_bottom = 0;
+	if (setup_bottom < 0)
+		setup_bottom = 13;
+}
+
+//=============================================================================
+/* NET MENU */
+
+int	m_net_cursor;
+int m_net_items;
+int m_net_saveHeight;
+
+char *net_helpMessage [] =
+{
+/* .........1.........2.... */
+  "                        ",
+  " Two computers connected",
+  "   through two modems.  ",
+  "                        ",
+
+  "                        ",
+  " Two computers connected",
+  " by a null-modem cable. ",
+  "                        ",
+
+  " Novell network LANs    ",
+  " or Windows 95 DOS-box. ",
+  "                        ",
+  "(LAN=Local Area Network)",
+
+  " Commonly used to play  ",
+  " over the Internet, but ",
+  " also used on a Local   ",
+  " Area Network.          "
+};
+
+void M_Menu_Net_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_net;
+	m_entersound = true;
+	m_net_items = 4;
+
+	if (m_net_cursor >= m_net_items)
+		m_net_cursor = 0;
+	m_net_cursor--;
+	M_Net_Key (K_DOWNARROW);
+}
+
+
+void M_Net_Draw (void)
+{
+	int		f;
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	f = 32;
+
+	if (serialAvailable)
+	{
+		p = Draw_CachePic ("gfx/netmen1.lmp");
+	}
+	else
+	{
+#ifdef _WIN32
+		p = NULL;
+#else
+		p = Draw_CachePic ("gfx/dim_modm.lmp");
+#endif
+	}
+
+	if (p)
+		M_DrawTransPic (72, f, p);
+
+	f += 19;
+
+	if (serialAvailable)
+	{
+		p = Draw_CachePic ("gfx/netmen2.lmp");
+	}
+	else
+	{
+#ifdef _WIN32
+		p = NULL;
+#else
+		p = Draw_CachePic ("gfx/dim_drct.lmp");
+#endif
+	}
+
+	if (p)
+		M_DrawTransPic (72, f, p);
+
+	f += 19;
+	if (ipxAvailable)
+		p = Draw_CachePic ("gfx/netmen3.lmp");
+	else
+		p = Draw_CachePic ("gfx/dim_ipx.lmp");
+	M_DrawTransPic (72, f, p);
+
+	f += 19;
+	if (tcpipAvailable)
+		p = Draw_CachePic ("gfx/netmen4.lmp");
+	else
+		p = Draw_CachePic ("gfx/dim_tcp.lmp");
+	M_DrawTransPic (72, f, p);
+
+	if (m_net_items == 5)	// JDC, could just be removed
+	{
+		f += 19;
+		p = Draw_CachePic ("gfx/netmen5.lmp");
+		M_DrawTransPic (72, f, p);
+	}
+
+	f = (320-26*8)/2;
+	M_DrawTextBox (f, 134, 24, 4);
+	f += 8;
+	M_Print (f, 142, net_helpMessage[m_net_cursor*4+0]);
+	M_Print (f, 150, net_helpMessage[m_net_cursor*4+1]);
+	M_Print (f, 158, net_helpMessage[m_net_cursor*4+2]);
+	M_Print (f, 166, net_helpMessage[m_net_cursor*4+3]);
+
+	f = (int)(host_time * 10)%6;
+	M_DrawTransPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
+}
+
+
+void M_Net_Key (int k)
+{
+again:
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_MultiPlayer_f ();
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (++m_net_cursor >= m_net_items)
+			m_net_cursor = 0;
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		if (--m_net_cursor < 0)
+			m_net_cursor = m_net_items - 1;
+		break;
+
+	case K_ENTER:
+		m_entersound = true;
+
+		switch (m_net_cursor)
+		{
+		case 0:
+			M_Menu_SerialConfig_f ();
+			break;
+
+		case 1:
+			M_Menu_SerialConfig_f ();
+			break;
+
+		case 2:
+			M_Menu_LanConfig_f ();
+			break;
+
+		case 3:
+			M_Menu_LanConfig_f ();
+			break;
+
+		case 4:
+// multiprotocol
+			break;
+		}
+	}
+
+	if (m_net_cursor == 0 && !serialAvailable)
+		goto again;
+	if (m_net_cursor == 1 && !serialAvailable)
+		goto again;
+	if (m_net_cursor == 2 && !ipxAvailable)
+		goto again;
+	if (m_net_cursor == 3 && !tcpipAvailable)
+		goto again;
+}
+
+//=============================================================================
+/* OPTIONS MENU */
+
+#ifdef _WIN32
+#define	OPTIONS_ITEMS	14
+#else
+#define	OPTIONS_ITEMS	13
+#endif
+
+#define	SLIDER_RANGE	10
+
+int		options_cursor;
+
+void M_Menu_Options_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_options;
+	m_entersound = true;
+
+#ifdef _WIN32
+	if ((options_cursor == 13) && (modestate != MS_WINDOWED))
+	{
+		options_cursor = 0;
+	}
+#endif
+}
+
+
+void M_AdjustSliders (int dir)
+{
+	S_LocalSound ("misc/menu3.wav");
+
+	switch (options_cursor)
+	{
+	case 3:	// screen size
+		scr_viewsize.value += dir * 10;
+		if (scr_viewsize.value < 30)
+			scr_viewsize.value = 30;
+		if (scr_viewsize.value > 120)
+			scr_viewsize.value = 120;
+		Cvar_SetValue ("viewsize", scr_viewsize.value);
+		break;
+	case 4:	// gamma
+		v_gamma.value -= dir * 0.05;
+		if (v_gamma.value < 0.5)
+			v_gamma.value = 0.5;
+		if (v_gamma.value > 1)
+			v_gamma.value = 1;
+		Cvar_SetValue ("gamma", v_gamma.value);
+		break;
+	case 5:	// mouse speed
+		sensitivity.value += dir * 0.5;
+		if (sensitivity.value < 1)
+			sensitivity.value = 1;
+		if (sensitivity.value > 11)
+			sensitivity.value = 11;
+		Cvar_SetValue ("sensitivity", sensitivity.value);
+		break;
+	case 6:	// music volume
+#ifdef _WIN32
+		bgmvolume.value += dir * 1.0;
+#else
+		bgmvolume.value += dir * 0.1;
+#endif
+		if (bgmvolume.value < 0)
+			bgmvolume.value = 0;
+		if (bgmvolume.value > 1)
+			bgmvolume.value = 1;
+		Cvar_SetValue ("bgmvolume", bgmvolume.value);
+		break;
+	case 7:	// sfx volume
+		volume.value += dir * 0.1;
+		if (volume.value < 0)
+			volume.value = 0;
+		if (volume.value > 1)
+			volume.value = 1;
+		Cvar_SetValue ("volume", volume.value);
+		break;
+
+	case 8:	// allways run
+		if (cl_forwardspeed.value > 200)
+		{
+			Cvar_SetValue ("cl_forwardspeed", 200);
+			Cvar_SetValue ("cl_backspeed", 200);
+		}
+		else
+		{
+			Cvar_SetValue ("cl_forwardspeed", 400);
+			Cvar_SetValue ("cl_backspeed", 400);
+		}
+		break;
+
+	case 9:	// invert mouse
+		Cvar_SetValue ("m_pitch", -m_pitch.value);
+		break;
+
+	case 10:	// lookspring
+		Cvar_SetValue ("lookspring", !lookspring.value);
+		break;
+
+	case 11:	// lookstrafe
+		Cvar_SetValue ("lookstrafe", !lookstrafe.value);
+		break;
+
+#ifdef _WIN32
+	case 13:	// _windowed_mouse
+		Cvar_SetValue ("_windowed_mouse", !_windowed_mouse.value);
+		break;
+#endif
+	}
+}
+
+
+void M_DrawSlider (int x, int y, float range)
+{
+	int	i;
+
+	if (range < 0)
+		range = 0;
+	if (range > 1)
+		range = 1;
+	M_DrawCharacter (x-8, y, 128);
+	for (i=0 ; i<SLIDER_RANGE ; i++)
+		M_DrawCharacter (x + i*8, y, 129);
+	M_DrawCharacter (x+i*8, y, 130);
+	M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
+}
+
+void M_DrawCheckbox (int x, int y, int on)
+{
+#if 0
+	if (on)
+		M_DrawCharacter (x, y, 131);
+	else
+		M_DrawCharacter (x, y, 129);
+#endif
+	if (on)
+		M_Print (x, y, "on");
+	else
+		M_Print (x, y, "off");
+}
+
+void M_Options_Draw (void)
+{
+	float		r;
+	qpic_t	*p;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_option.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	M_Print (16, 32, "    Customize controls");
+	M_Print (16, 40, "         Go to console");
+	M_Print (16, 48, "     Reset to defaults");
+
+	M_Print (16, 56, "           Screen size");
+	r = (scr_viewsize.value - 30) / (120 - 30);
+	M_DrawSlider (220, 56, r);
+
+	M_Print (16, 64, "            Brightness");
+	r = (1.0 - v_gamma.value) / 0.5;
+	M_DrawSlider (220, 64, r);
+
+	M_Print (16, 72, "           Mouse Speed");
+	r = (sensitivity.value - 1)/10;
+	M_DrawSlider (220, 72, r);
+
+	M_Print (16, 80, "       CD Music Volume");
+	r = bgmvolume.value;
+	M_DrawSlider (220, 80, r);
+
+	M_Print (16, 88, "          Sound Volume");
+	r = volume.value;
+	M_DrawSlider (220, 88, r);
+
+	M_Print (16, 96,  "            Always Run");
+	M_DrawCheckbox (220, 96, cl_forwardspeed.value > 200);
+
+	M_Print (16, 104, "          Invert Mouse");
+	M_DrawCheckbox (220, 104, m_pitch.value < 0);
+
+	M_Print (16, 112, "            Lookspring");
+	M_DrawCheckbox (220, 112, lookspring.value);
+
+	M_Print (16, 120, "            Lookstrafe");
+	M_DrawCheckbox (220, 120, lookstrafe.value);
+
+	if (vid_menudrawfn)
+		M_Print (16, 128, "         Video Options");
+
+#ifdef _WIN32
+	if (modestate == MS_WINDOWED)
+	{
+		M_Print (16, 136, "             Use Mouse");
+		M_DrawCheckbox (220, 136, _windowed_mouse.value);
+	}
+#endif
+
+// cursor
+	M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Options_Key (int k)
+{
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_Main_f ();
+		break;
+
+	case K_ENTER:
+		m_entersound = true;
+		switch (options_cursor)
+		{
+		case 0:
+			M_Menu_Keys_f ();
+			break;
+		case 1:
+			m_state = m_none;
+			Con_ToggleConsole_f ();
+			break;
+		case 2:
+			Cbuf_AddText ("exec default.cfg\n");
+			break;
+		case 12:
+			M_Menu_Video_f ();
+			break;
+		default:
+			M_AdjustSliders (1);
+			break;
+		}
+		return;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		options_cursor--;
+		if (options_cursor < 0)
+			options_cursor = OPTIONS_ITEMS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		options_cursor++;
+		if (options_cursor >= OPTIONS_ITEMS)
+			options_cursor = 0;
+		break;
+
+	case K_LEFTARROW:
+		M_AdjustSliders (-1);
+		break;
+
+	case K_RIGHTARROW:
+		M_AdjustSliders (1);
+		break;
+	}
+
+	if (options_cursor == 12 && vid_menudrawfn == NULL)
+	{
+		if (k == K_UPARROW)
+			options_cursor = 11;
+		else
+			options_cursor = 0;
+	}
+
+#ifdef _WIN32
+	if ((options_cursor == 13) && (modestate != MS_WINDOWED))
+	{
+		if (k == K_UPARROW)
+			options_cursor = 12;
+		else
+			options_cursor = 0;
+	}
+#endif
+}
+
+//=============================================================================
+/* KEYS MENU */
+
+char *bindnames[][2] =
+{
+{"+attack", 		"attack"},
+{"impulse 10", 		"change weapon"},
+{"+jump", 			"jump / swim up"},
+{"+forward", 		"walk forward"},
+{"+back", 			"backpedal"},
+{"+left", 			"turn left"},
+{"+right", 			"turn right"},
+{"+speed", 			"run"},
+{"+moveleft", 		"step left"},
+{"+moveright", 		"step right"},
+{"+strafe", 		"sidestep"},
+{"+lookup", 		"look up"},
+{"+lookdown", 		"look down"},
+{"centerview", 		"center view"},
+{"+mlook", 			"mouse look"},
+{"+klook", 			"keyboard look"},
+{"+moveup",			"swim up"},
+{"+movedown",		"swim down"}
+};
+
+#define	NUMCOMMANDS	(sizeof(bindnames)/sizeof(bindnames[0]))
+
+int		keys_cursor;
+int		bind_grab;
+extern int bind_nooverride;
+
+void M_Menu_Keys_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_keys;
+	m_entersound = true;
+        bind_nooverride = 1; // don't override binds
+}
+
+
+void M_FindKeysForCommand (char *command, int *twokeys)
+{
+	int		count;
+	int		j;
+	int		l;
+	char	*b;
+
+	twokeys[0] = twokeys[1] = -1;
+	l = strlen(command);
+	count = 0;
+
+	for (j=0 ; j<256 ; j++)
+	{
+		b = keybindings[j];
+		if (!b)
+			continue;
+		if (!strncmp (b, command, l) )
+		{
+			twokeys[count] = j;
+			count++;
+			if (count == 2)
+				break;
+		}
+	}
+}
+
+void M_UnbindCommand (char *command)
+{
+	int		j;
+	int		l;
+	char	*b;
+
+	l = strlen(command);
+
+	for (j=0 ; j<256 ; j++)
+	{
+		b = keybindings[j];
+		if (!b)
+			continue;
+		if (!strncmp (b, command, l) )
+			Key_SetBinding (j, "");
+	}
+}
+
+
+void M_Keys_Draw (void)
+{
+	int		i, l;
+	int		keys[2];
+	char	*name;
+	int		x, y;
+	qpic_t	*p;
+
+	p = Draw_CachePic ("gfx/ttl_cstm.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	if (bind_grab)
+		M_Print (12, 32, "Press a key or button for this action");
+	else
+		M_Print (18, 32, "Enter to change, backspace to clear");
+
+// search for known bindings
+	for (i=0 ; i<NUMCOMMANDS ; i++)
+	{
+		y = 48 + 8*i;
+
+		M_Print (16, y, bindnames[i][1]);
+
+		l = strlen (bindnames[i][0]);
+
+		M_FindKeysForCommand (bindnames[i][0], keys);
+
+		if (keys[0] == -1)
+		{
+			M_Print (140, y, "???");
+		}
+		else
+		{
+			name = Key_KeynumToString (keys[0]);
+			M_Print (140, y, name);
+			x = strlen(name) * 8;
+			if (keys[1] != -1)
+			{
+				M_Print (140 + x + 8, y, "or");
+				M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
+			}
+		}
+	}
+
+	if (bind_grab)
+		M_DrawCharacter (130, 48 + keys_cursor*8, '=');
+	else
+		M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
+}
+
+
+void M_Keys_Key (int k)
+{
+	char	cmd[80];
+	int		keys[2];
+
+	if (bind_grab)
+	{	// defining a key
+		S_LocalSound ("misc/menu1.wav");
+		if (k == K_ESCAPE)
+		{
+			bind_grab = false;
+		}
+		else if (k != '`')
+		{
+			sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
+			Cbuf_InsertText (cmd);
+		}
+
+		bind_grab = false;
+		return;
+	}
+
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_Options_f ();
+		break;
+
+	case K_LEFTARROW:
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		keys_cursor--;
+		if (keys_cursor < 0)
+			keys_cursor = NUMCOMMANDS-1;
+		break;
+
+	case K_DOWNARROW:
+	case K_RIGHTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		keys_cursor++;
+		if (keys_cursor >= NUMCOMMANDS)
+			keys_cursor = 0;
+		break;
+
+	case K_ENTER:		// go into bind mode
+		M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
+		S_LocalSound ("misc/menu2.wav");
+		if (keys[1] != -1)
+			M_UnbindCommand (bindnames[keys_cursor][0]);
+		bind_grab = true;
+		break;
+
+	case K_BACKSPACE:		// delete bindings
+	case K_DEL:				// delete bindings
+		S_LocalSound ("misc/menu2.wav");
+		M_UnbindCommand (bindnames[keys_cursor][0]);
+		break;
+	}
+}
+
+//=============================================================================
+/* VIDEO MENU */
+
+void M_Menu_Video_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_video;
+	m_entersound = true;
+}
+
+
+void M_Video_Draw (void)
+{
+	(*vid_menudrawfn) ();
+}
+
+
+void M_Video_Key (int key)
+{
+	(*vid_menukeyfn) (key);
+}
+
+//=============================================================================
+/* HELP MENU */
+
+int		help_page;
+#define	NUM_HELP_PAGES	6
+
+
+void M_Menu_Help_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_help;
+	m_entersound = true;
+	help_page = 0;
+}
+
+
+
+void M_Help_Draw (void)
+{
+	M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) );
+}
+
+
+void M_Help_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Main_f ();
+		break;
+
+	case K_UPARROW:
+	case K_RIGHTARROW:
+		m_entersound = true;
+		if (++help_page >= NUM_HELP_PAGES)
+			help_page = 0;
+		break;
+
+	case K_DOWNARROW:
+	case K_LEFTARROW:
+		m_entersound = true;
+		if (--help_page < 0)
+			help_page = NUM_HELP_PAGES-1;
+		break;
+	}
+
+}
+
+//=============================================================================
+/* QUIT MENU */
+
+int		msgNumber;
+int		m_quit_prevstate;
+qboolean	wasInMenus;
+
+#ifndef	_WIN32
+char *quitMessage [] = 
+{
+/* .........1.........2.... */
+  "  Are you gonna quit    ",
+  "  this game just like   ",
+  "   everything else?     ",
+  "                        ",
+ 
+  " Milord, methinks that  ",
+  "   thou art a lowly     ",
+  " quitter. Is this true? ",
+  "                        ",
+
+  " Do I need to bust your ",
+  "  face open for trying  ",
+  "        to quit?        ",
+  "                        ",
+
+  " Man, I oughta smack you",
+  "   for trying to quit!  ",
+  "     Press Y to get     ",
+  "      smacked out.      ",
+ 
+  " Press Y to quit like a ",
+  "   big loser in life.   ",
+  "  Press N to stay proud ",
+  "    and successful!     ",
+ 
+  "   If you press Y to    ",
+  "  quit, I will summon   ",
+  "  Satan all over your   ",
+  "      hard drive!       ",
+ 
+  "  Um, Asmodeus dislikes ",
+  " his children trying to ",
+  " quit. Press Y to return",
+  "   to your Tinkertoys.  ",
+ 
+  "  If you quit now, I'll ",
+  "  throw a blanket-party ",
+  "   for you next time!   ",
+  "                        "
+};
+#endif
+
+void M_Menu_Quit_f (void)
+{
+	if (m_state == m_quit)
+		return;
+	wasInMenus = (key_dest == key_menu);
+	key_dest = key_menu;
+	m_quit_prevstate = m_state;
+	m_state = m_quit;
+	m_entersound = true;
+	msgNumber = rand()&7;
+}
+
+
+void M_Quit_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+	case 'n':
+	case 'N':
+		if (wasInMenus)
+		{
+			m_state = m_quit_prevstate;
+			m_entersound = true;
+		}
+		else
+		{
+			key_dest = key_game;
+			m_state = m_none;
+		}
+		break;
+
+        case K_ENTER:
+	case 'Y':
+	case 'y':
+		key_dest = key_console;
+		Host_Quit_f ();
+		break;
+
+	default:
+		break;
+	}
+
+}
+
+
+void M_Quit_Draw (void)
+{
+	if (wasInMenus)
+	{
+		m_state = m_quit_prevstate;
+		m_recursiveDraw = true;
+		M_Draw ();
+		m_state = m_quit;
+	}
+
+#ifdef _WIN32
+	M_DrawTextBox (0, 0, 38, 23);
+	M_PrintWhite (16, 12,  "  Quake version 1.09 by id Software\n\n");
+	M_PrintWhite (16, 28,  "Programming        Art \n");
+	M_Print (16, 36,  " John Carmack       Adrian Carmack\n");
+	M_Print (16, 44,  " Michael Abrash     Kevin Cloud\n");
+	M_Print (16, 52,  " John Cash          Paul Steed\n");
+	M_Print (16, 60,  " Dave 'Zoid' Kirsch\n");
+	M_PrintWhite (16, 68,  "Design             Biz\n");
+	M_Print (16, 76,  " John Romero        Jay Wilbur\n");
+	M_Print (16, 84,  " Sandy Petersen     Mike Wilson\n");
+	M_Print (16, 92,  " American McGee     Donna Jackson\n");
+	M_Print (16, 100,  " Tim Willits        Todd Hollenshead\n");
+	M_PrintWhite (16, 108, "Support            Projects\n");
+	M_Print (16, 116, " Barrett Alexander  Shawn Green\n");
+	M_PrintWhite (16, 124, "Sound Effects\n");
+	M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n");
+	M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n");
+	M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n");
+	M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n");
+	M_PrintWhite (16, 164, "registered trademark licensed to\n");
+	M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n");
+	M_PrintWhite (16, 180, "reserved. Press y to exit\n");
+#else
+	M_DrawTextBox (56, 76, 24, 4);
+	M_Print (64, 84,  quitMessage[msgNumber*4+0]);
+	M_Print (64, 92,  quitMessage[msgNumber*4+1]);
+	M_Print (64, 100, quitMessage[msgNumber*4+2]);
+	M_Print (64, 108, quitMessage[msgNumber*4+3]);
+#endif
+}
+
+//=============================================================================
+
+/* SERIAL CONFIG MENU */
+
+int		serialConfig_cursor;
+int		serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132};
+#define	NUM_SERIALCONFIG_CMDS	6
+
+static int ISA_uarts[]	= {0x3f8,0x2f8,0x3e8,0x2e8};
+static int ISA_IRQs[]	= {4,3,4,3};
+int serialConfig_baudrate[] = {9600,14400,19200,28800,38400,57600};
+
+int		serialConfig_comport;
+int		serialConfig_irq ;
+int		serialConfig_baud;
+char	serialConfig_phone[16];
+
+void M_Menu_SerialConfig_f (void)
+{
+	int		n;
+	int		port;
+	int		baudrate;
+	qboolean	useModem;
+
+	key_dest = key_menu;
+	m_state = m_serialconfig;
+	m_entersound = true;
+	if (JoiningGame && SerialConfig)
+		serialConfig_cursor = 4;
+	else
+		serialConfig_cursor = 5;
+
+	(*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem);
+
+	// map uart's port to COMx
+	for (n = 0; n < 4; n++)
+		if (ISA_uarts[n] == port)
+			break;
+	if (n == 4)
+	{
+		n = 0;
+		serialConfig_irq = 4;
+	}
+	serialConfig_comport = n + 1;
+
+	// map baudrate to index
+	for (n = 0; n < 6; n++)
+		if (serialConfig_baudrate[n] == baudrate)
+			break;
+	if (n == 6)
+		n = 5;
+	serialConfig_baud = n;
+
+	m_return_onerror = false;
+	m_return_reason[0] = 0;
+}
+
+
+void M_SerialConfig_Draw (void)
+{
+	qpic_t	*p;
+	int		basex;
+	char	*startJoin;
+	char	*directModem;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	basex = (320-p->width)/2;
+	M_DrawPic (basex, 4, p);
+
+	if (StartingGame)
+		startJoin = "New Game";
+	else
+		startJoin = "Join Game";
+	if (SerialConfig)
+		directModem = "Modem";
+	else
+		directModem = "Direct Connect";
+	M_Print (basex, 32, va ("%s - %s", startJoin, directModem));
+	basex += 8;
+
+	M_Print (basex, serialConfig_cursor_table[0], "Port");
+	M_DrawTextBox (160, 40, 4, 1);
+	M_Print (168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport));
+
+	M_Print (basex, serialConfig_cursor_table[1], "IRQ");
+	M_DrawTextBox (160, serialConfig_cursor_table[1]-8, 1, 1);
+	M_Print (168, serialConfig_cursor_table[1], va("%u", serialConfig_irq));
+
+	M_Print (basex, serialConfig_cursor_table[2], "Baud");
+	M_DrawTextBox (160, serialConfig_cursor_table[2]-8, 5, 1);
+	M_Print (168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud]));
+
+	if (SerialConfig)
+	{
+		M_Print (basex, serialConfig_cursor_table[3], "Modem Setup...");
+		if (JoiningGame)
+		{
+			M_Print (basex, serialConfig_cursor_table[4], "Phone number");
+			M_DrawTextBox (160, serialConfig_cursor_table[4]-8, 16, 1);
+			M_Print (168, serialConfig_cursor_table[4], serialConfig_phone);
+		}
+	}
+
+	if (JoiningGame)
+	{
+		M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1);
+		M_Print (basex+8, serialConfig_cursor_table[5], "Connect");
+	}
+	else
+	{
+		M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1);
+		M_Print (basex+8, serialConfig_cursor_table[5], "OK");
+	}
+
+	M_DrawCharacter (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1));
+
+	if (serialConfig_cursor == 4)
+		M_DrawCharacter (168 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1));
+
+	if (*m_return_reason)
+		M_PrintWhite (basex, 148, m_return_reason);
+}
+
+
+void M_SerialConfig_Key (int key)
+{
+	int		l;
+
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Net_f ();
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		serialConfig_cursor--;
+		if (serialConfig_cursor < 0)
+			serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		serialConfig_cursor++;
+		if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS)
+			serialConfig_cursor = 0;
+		break;
+
+	case K_LEFTARROW:
+		if (serialConfig_cursor > 2)
+			break;
+		S_LocalSound ("misc/menu3.wav");
+
+		if (serialConfig_cursor == 0)
+		{
+			serialConfig_comport--;
+			if (serialConfig_comport == 0)
+				serialConfig_comport = 4;
+			serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+		}
+
+		if (serialConfig_cursor == 1)
+		{
+			serialConfig_irq--;
+			if (serialConfig_irq == 6)
+				serialConfig_irq = 5;
+			if (serialConfig_irq == 1)
+				serialConfig_irq = 7;
+		}
+
+		if (serialConfig_cursor == 2)
+		{
+			serialConfig_baud--;
+			if (serialConfig_baud < 0)
+				serialConfig_baud = 5;
+		}
+
+		break;
+
+	case K_RIGHTARROW:
+		if (serialConfig_cursor > 2)
+			break;
+forward:
+		S_LocalSound ("misc/menu3.wav");
+
+		if (serialConfig_cursor == 0)
+		{
+			serialConfig_comport++;
+			if (serialConfig_comport > 4)
+				serialConfig_comport = 1;
+			serialConfig_irq = ISA_IRQs[serialConfig_comport-1];
+		}
+
+		if (serialConfig_cursor == 1)
+		{
+			serialConfig_irq++;
+			if (serialConfig_irq == 6)
+				serialConfig_irq = 7;
+			if (serialConfig_irq == 8)
+				serialConfig_irq = 2;
+		}
+
+		if (serialConfig_cursor == 2)
+		{
+			serialConfig_baud++;
+			if (serialConfig_baud > 5)
+				serialConfig_baud = 0;
+		}
+
+		break;
+
+	case K_ENTER:
+		if (serialConfig_cursor < 3)
+			goto forward;
+
+		m_entersound = true;
+
+		if (serialConfig_cursor == 3)
+		{
+			(*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+			M_Menu_ModemConfig_f ();
+			break;
+		}
+
+		if (serialConfig_cursor == 4)
+		{
+			serialConfig_cursor = 5;
+			break;
+		}
+
+		// serialConfig_cursor == 5 (OK/CONNECT)
+		(*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
+
+		M_ConfigureNetSubsystem ();
+
+		if (StartingGame)
+		{
+			M_Menu_GameOptions_f ();
+			break;
+		}
+
+		m_return_state = m_state;
+		m_return_onerror = true;
+		key_dest = key_game;
+		m_state = m_none;
+
+		if (SerialConfig)
+			Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone));
+		else
+			Cbuf_AddText ("connect\n");
+		break;
+
+	case K_BACKSPACE:
+		if (serialConfig_cursor == 4)
+		{
+			if (strlen(serialConfig_phone))
+				serialConfig_phone[strlen(serialConfig_phone)-1] = 0;
+		}
+		break;
+
+	default:
+		if (key < 32 || key > 127)
+			break;
+		if (serialConfig_cursor == 4)
+		{
+			l = strlen(serialConfig_phone);
+			if (l < 15)
+			{
+				serialConfig_phone[l+1] = 0;
+				serialConfig_phone[l] = key;
+			}
+		}
+	}
+
+	if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4))
+		if (key == K_UPARROW)
+			serialConfig_cursor = 2;
+		else
+			serialConfig_cursor = 5;
+
+	if (SerialConfig && StartingGame && serialConfig_cursor == 4)
+		if (key == K_UPARROW)
+			serialConfig_cursor = 3;
+		else
+			serialConfig_cursor = 5;
+}
+
+//=============================================================================
+/* MODEM CONFIG MENU */
+
+int		modemConfig_cursor;
+int		modemConfig_cursor_table [] = {40, 56, 88, 120, 156};
+#define NUM_MODEMCONFIG_CMDS	5
+
+char	modemConfig_dialing;
+char	modemConfig_clear [16];
+char	modemConfig_init [32];
+char	modemConfig_hangup [16];
+
+void M_Menu_ModemConfig_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_modemconfig;
+	m_entersound = true;
+	(*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup);
+}
+
+
+void M_ModemConfig_Draw (void)
+{
+	qpic_t	*p;
+	int		basex;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	basex = (320-p->width)/2;
+	M_DrawPic (basex, 4, p);
+	basex += 8;
+
+	if (modemConfig_dialing == 'P')
+		M_Print (basex, modemConfig_cursor_table[0], "Pulse Dialing");
+	else
+		M_Print (basex, modemConfig_cursor_table[0], "Touch Tone Dialing");
+
+	M_Print (basex, modemConfig_cursor_table[1], "Clear");
+	M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1);
+	M_Print (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear);
+	if (modemConfig_cursor == 1)
+		M_DrawCharacter (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1));
+
+	M_Print (basex, modemConfig_cursor_table[2], "Init");
+	M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1);
+	M_Print (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init);
+	if (modemConfig_cursor == 2)
+		M_DrawCharacter (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1));
+
+	M_Print (basex, modemConfig_cursor_table[3], "Hangup");
+	M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1);
+	M_Print (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup);
+	if (modemConfig_cursor == 3)
+		M_DrawCharacter (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1));
+
+	M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1);
+	M_Print (basex+8, modemConfig_cursor_table[4], "OK");
+
+	M_DrawCharacter (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1));
+}
+
+
+void M_ModemConfig_Key (int key)
+{
+	int		l;
+
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_SerialConfig_f ();
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		modemConfig_cursor--;
+		if (modemConfig_cursor < 0)
+			modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		modemConfig_cursor++;
+		if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS)
+			modemConfig_cursor = 0;
+		break;
+
+	case K_LEFTARROW:
+	case K_RIGHTARROW:
+		if (modemConfig_cursor == 0)
+		{
+			if (modemConfig_dialing == 'P')
+				modemConfig_dialing = 'T';
+			else
+				modemConfig_dialing = 'P';
+			S_LocalSound ("misc/menu1.wav");
+		}
+		break;
+
+	case K_ENTER:
+		if (modemConfig_cursor == 0)
+		{
+			if (modemConfig_dialing == 'P')
+				modemConfig_dialing = 'T';
+			else
+				modemConfig_dialing = 'P';
+			m_entersound = true;
+		}
+
+		if (modemConfig_cursor == 4)
+		{
+			(*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup);
+			m_entersound = true;
+			M_Menu_SerialConfig_f ();
+		}
+		break;
+
+	case K_BACKSPACE:
+		if (modemConfig_cursor == 1)
+		{
+			if (strlen(modemConfig_clear))
+				modemConfig_clear[strlen(modemConfig_clear)-1] = 0;
+		}
+
+		if (modemConfig_cursor == 2)
+		{
+			if (strlen(modemConfig_init))
+				modemConfig_init[strlen(modemConfig_init)-1] = 0;
+		}
+
+		if (modemConfig_cursor == 3)
+		{
+			if (strlen(modemConfig_hangup))
+				modemConfig_hangup[strlen(modemConfig_hangup)-1] = 0;
+		}
+		break;
+
+	default:
+		if (key < 32 || key > 127)
+			break;
+
+		if (modemConfig_cursor == 1)
+		{
+			l = strlen(modemConfig_clear);
+			if (l < 15)
+			{
+				modemConfig_clear[l+1] = 0;
+				modemConfig_clear[l] = key;
+			}
+		}
+
+		if (modemConfig_cursor == 2)
+		{
+			l = strlen(modemConfig_init);
+			if (l < 29)
+			{
+				modemConfig_init[l+1] = 0;
+				modemConfig_init[l] = key;
+			}
+		}
+
+		if (modemConfig_cursor == 3)
+		{
+			l = strlen(modemConfig_hangup);
+			if (l < 15)
+			{
+				modemConfig_hangup[l+1] = 0;
+				modemConfig_hangup[l] = key;
+			}
+		}
+	}
+}
+
+//=============================================================================
+/* LAN CONFIG MENU */
+
+int		lanConfig_cursor = -1;
+int		lanConfig_cursor_table [] = {72, 92, 124};
+#define NUM_LANCONFIG_CMDS	3
+
+int 	lanConfig_port;
+char	lanConfig_portname[6];
+char	lanConfig_joinname[22];
+
+void M_Menu_LanConfig_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_lanconfig;
+	m_entersound = true;
+	if (lanConfig_cursor == -1)
+	{
+		if (JoiningGame && TCPIPConfig)
+			lanConfig_cursor = 2;
+		else
+			lanConfig_cursor = 1;
+	}
+	if (StartingGame && lanConfig_cursor == 2)
+		lanConfig_cursor = 1;
+	lanConfig_port = DEFAULTnet_hostport;
+	sprintf(lanConfig_portname, "%u", lanConfig_port);
+
+	m_return_onerror = false;
+	m_return_reason[0] = 0;
+}
+
+
+void M_LanConfig_Draw (void)
+{
+	qpic_t	*p;
+	int		basex;
+	char	*startJoin;
+	char	*protocol;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	basex = (320-p->width)/2;
+	M_DrawPic (basex, 4, p);
+
+	if (StartingGame)
+		startJoin = "New Game";
+	else
+		startJoin = "Join Game";
+	if (IPXConfig)
+		protocol = "IPX";
+	else
+		protocol = "TCP/IP";
+	M_Print (basex, 32, va ("%s - %s", startJoin, protocol));
+	basex += 8;
+
+	M_Print (basex, 52, "Address:");
+	if (IPXConfig)
+		M_Print (basex+9*8, 52, my_ipx_address);
+	else
+		M_Print (basex+9*8, 52, my_tcpip_address);
+
+	M_Print (basex, lanConfig_cursor_table[0], "Port");
+	M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
+	M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
+
+	if (JoiningGame)
+	{
+		M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
+		M_Print (basex, 108, "Join game at:");
+		M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
+		M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
+	}
+	else
+	{
+		M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
+		M_Print (basex+8, lanConfig_cursor_table[1], "OK");
+	}
+
+	M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
+
+	if (lanConfig_cursor == 0)
+		M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
+
+	if (lanConfig_cursor == 2)
+		M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
+
+	if (*m_return_reason)
+		M_PrintWhite (basex, 148, m_return_reason);
+}
+
+
+void M_LanConfig_Key (int key)
+{
+	int		l;
+
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Net_f ();
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		lanConfig_cursor--;
+		if (lanConfig_cursor < 0)
+			lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		lanConfig_cursor++;
+		if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
+			lanConfig_cursor = 0;
+		break;
+
+	case K_ENTER:
+		if (lanConfig_cursor == 0)
+			break;
+
+		m_entersound = true;
+
+		M_ConfigureNetSubsystem ();
+
+		if (lanConfig_cursor == 1)
+		{
+			if (StartingGame)
+			{
+				M_Menu_GameOptions_f ();
+				break;
+			}
+			M_Menu_Search_f();
+			break;
+		}
+
+		if (lanConfig_cursor == 2)
+		{
+			m_return_state = m_state;
+			m_return_onerror = true;
+			key_dest = key_game;
+			m_state = m_none;
+			Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
+			break;
+		}
+
+		break;
+
+	case K_BACKSPACE:
+		if (lanConfig_cursor == 0)
+		{
+			if (strlen(lanConfig_portname))
+				lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
+		}
+
+		if (lanConfig_cursor == 2)
+		{
+			if (strlen(lanConfig_joinname))
+				lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
+		}
+		break;
+
+	default:
+		if (key < 32 || key > 127)
+			break;
+
+		if (lanConfig_cursor == 2)
+		{
+			l = strlen(lanConfig_joinname);
+			if (l < 21)
+			{
+				lanConfig_joinname[l+1] = 0;
+				lanConfig_joinname[l] = key;
+			}
+		}
+
+		if (key < '0' || key > '9')
+			break;
+		if (lanConfig_cursor == 0)
+		{
+			l = strlen(lanConfig_portname);
+			if (l < 5)
+			{
+				lanConfig_portname[l+1] = 0;
+				lanConfig_portname[l] = key;
+			}
+		}
+	}
+
+	if (StartingGame && lanConfig_cursor == 2)
+		if (key == K_UPARROW)
+			lanConfig_cursor = 1;
+		else
+			lanConfig_cursor = 0;
+
+	l =  Q_atoi(lanConfig_portname);
+	if (l > 65535)
+		l = lanConfig_port;
+	else
+		lanConfig_port = l;
+	sprintf(lanConfig_portname, "%u", lanConfig_port);
+}
+
+//=============================================================================
+/* GAME OPTIONS MENU */
+
+typedef struct
+{
+	char	*name;
+	char	*description;
+} level_t;
+
+level_t		levels[] =
+{
+	{"start", "Entrance"},	// 0
+
+	{"e1m1", "Slipgate Complex"},				// 1
+	{"e1m2", "Castle of the Damned"},
+	{"e1m3", "The Necropolis"},
+	{"e1m4", "The Grisly Grotto"},
+	{"e1m5", "Gloom Keep"},
+	{"e1m6", "The Door To Chthon"},
+	{"e1m7", "The House of Chthon"},
+	{"e1m8", "Ziggurat Vertigo"},
+
+	{"e2m1", "The Installation"},				// 9
+	{"e2m2", "Ogre Citadel"},
+	{"e2m3", "Crypt of Decay"},
+	{"e2m4", "The Ebon Fortress"},
+	{"e2m5", "The Wizard's Manse"},
+	{"e2m6", "The Dismal Oubliette"},
+	{"e2m7", "Underearth"},
+
+	{"e3m1", "Termination Central"},			// 16
+	{"e3m2", "The Vaults of Zin"},
+	{"e3m3", "The Tomb of Terror"},
+	{"e3m4", "Satan's Dark Delight"},
+	{"e3m5", "Wind Tunnels"},
+	{"e3m6", "Chambers of Torment"},
+	{"e3m7", "The Haunted Halls"},
+
+	{"e4m1", "The Sewage System"},				// 23
+	{"e4m2", "The Tower of Despair"},
+	{"e4m3", "The Elder God Shrine"},
+	{"e4m4", "The Palace of Hate"},
+	{"e4m5", "Hell's Atrium"},
+	{"e4m6", "The Pain Maze"},
+	{"e4m7", "Azure Agony"},
+	{"e4m8", "The Nameless City"},
+
+	{"end", "Shub-Niggurath's Pit"},			// 31
+
+	{"dm1", "Place of Two Deaths"},				// 32
+	{"dm2", "Claustrophobopolis"},
+	{"dm3", "The Abandoned Base"},
+	{"dm4", "The Bad Place"},
+	{"dm5", "The Cistern"},
+	{"dm6", "The Dark Zone"}
+};
+
+//MED 01/06/97 added hipnotic levels
+level_t     hipnoticlevels[] =
+{
+   {"start", "Command HQ"},  // 0
+
+   {"hip1m1", "The Pumping Station"},          // 1
+   {"hip1m2", "Storage Facility"},
+   {"hip1m3", "The Lost Mine"},
+   {"hip1m4", "Research Facility"},
+   {"hip1m5", "Military Complex"},
+
+   {"hip2m1", "Ancient Realms"},          // 6
+   {"hip2m2", "The Black Cathedral"},
+   {"hip2m3", "The Catacombs"},
+   {"hip2m4", "The Crypt"},
+   {"hip2m5", "Mortum's Keep"},
+   {"hip2m6", "The Gremlin's Domain"},
+
+   {"hip3m1", "Tur Torment"},       // 12
+   {"hip3m2", "Pandemonium"},
+   {"hip3m3", "Limbo"},
+   {"hip3m4", "The Gauntlet"},
+
+   {"hipend", "Armagon's Lair"},       // 16
+
+   {"hipdm1", "The Edge of Oblivion"}           // 17
+};
+
+//PGM 01/07/97 added rogue levels
+//PGM 03/02/97 added dmatch level
+level_t		roguelevels[] =
+{
+	{"start",	"Split Decision"},
+	{"r1m1",	"Deviant's Domain"},
+	{"r1m2",	"Dread Portal"},
+	{"r1m3",	"Judgement Call"},
+	{"r1m4",	"Cave of Death"},
+	{"r1m5",	"Towers of Wrath"},
+	{"r1m6",	"Temple of Pain"},
+	{"r1m7",	"Tomb of the Overlord"},
+	{"r2m1",	"Tempus Fugit"},
+	{"r2m2",	"Elemental Fury I"},
+	{"r2m3",	"Elemental Fury II"},
+	{"r2m4",	"Curse of Osiris"},
+	{"r2m5",	"Wizard's Keep"},
+	{"r2m6",	"Blood Sacrifice"},
+	{"r2m7",	"Last Bastion"},
+	{"r2m8",	"Source of Evil"},
+	{"ctf1",    "Division of Change"}
+};
+
+typedef struct
+{
+	char	*description;
+	int		firstLevel;
+	int		levels;
+} episode_t;
+
+episode_t	episodes[] =
+{
+	{"Welcome to Quake", 0, 1},
+	{"Doomed Dimension", 1, 8},
+	{"Realm of Black Magic", 9, 7},
+	{"Netherworld", 16, 7},
+	{"The Elder World", 23, 8},
+	{"Final Level", 31, 1},
+	{"Deathmatch Arena", 32, 6}
+};
+
+//MED 01/06/97  added hipnotic episodes
+episode_t   hipnoticepisodes[] =
+{
+   {"Scourge of Armagon", 0, 1},
+   {"Fortress of the Dead", 1, 5},
+   {"Dominion of Darkness", 6, 6},
+   {"The Rift", 12, 4},
+   {"Final Level", 16, 1},
+   {"Deathmatch Arena", 17, 1}
+};
+
+//PGM 01/07/97 added rogue episodes
+//PGM 03/02/97 added dmatch episode
+episode_t	rogueepisodes[] =
+{
+	{"Introduction", 0, 1},
+	{"Hell's Fortress", 1, 7},
+	{"Corridors of Time", 8, 8},
+	{"Deathmatch Arena", 16, 1}
+};
+
+int	startepisode;
+int	startlevel;
+int maxplayers;
+qboolean m_serverInfoMessage = false;
+double m_serverInfoMessageTime;
+
+void M_Menu_GameOptions_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_gameoptions;
+	m_entersound = true;
+	if (maxplayers == 0)
+		maxplayers = svs.maxclients;
+	if (maxplayers < 2)
+		maxplayers = svs.maxclientslimit;
+}
+
+
+int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
+#define	NUM_GAMEOPTIONS	9
+int		gameoptions_cursor;
+
+void M_GameOptions_Draw (void)
+{
+	qpic_t	*p;
+	int		x;
+
+	M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+
+	M_DrawTextBox (152, 32, 10, 1);
+	M_Print (160, 40, "begin game");
+
+	M_Print (0, 56, "      Max players");
+	M_Print (160, 56, va("%i", maxplayers) );
+
+	M_Print (0, 64, "        Game Type");
+	if (coop.value)
+		M_Print (160, 64, "Cooperative");
+	else
+		M_Print (160, 64, "Deathmatch");
+
+	M_Print (0, 72, "        Teamplay");
+	if (rogue)
+	{
+		char *msg;
+
+		switch((int)teamplay.value)
+		{
+			case 1: msg = "No Friendly Fire"; break;
+			case 2: msg = "Friendly Fire"; break;
+			case 3: msg = "Tag"; break;
+			case 4: msg = "Capture the Flag"; break;
+			case 5: msg = "One Flag CTF"; break;
+			case 6: msg = "Three Team CTF"; break;
+			default: msg = "Off"; break;
+		}
+		M_Print (160, 72, msg);
+	}
+	else
+	{
+		char *msg;
+
+		switch((int)teamplay.value)
+		{
+			case 1: msg = "No Friendly Fire"; break;
+			case 2: msg = "Friendly Fire"; break;
+			default: msg = "Off"; break;
+		}
+		M_Print (160, 72, msg);
+	}
+
+	M_Print (0, 80, "            Skill");
+	if (skill.value == 0)
+		M_Print (160, 80, "Easy difficulty");
+	else if (skill.value == 1)
+		M_Print (160, 80, "Normal difficulty");
+	else if (skill.value == 2)
+		M_Print (160, 80, "Hard difficulty");
+	else
+		M_Print (160, 80, "Nightmare difficulty");
+
+	M_Print (0, 88, "       Frag Limit");
+	if (fraglimit.value == 0)
+		M_Print (160, 88, "none");
+	else
+		M_Print (160, 88, va("%i frags", (int)fraglimit.value));
+
+	M_Print (0, 96, "       Time Limit");
+	if (timelimit.value == 0)
+		M_Print (160, 96, "none");
+	else
+		M_Print (160, 96, va("%i minutes", (int)timelimit.value));
+
+	M_Print (0, 112, "         Episode");
+   //MED 01/06/97 added hipnotic episodes
+   if (hipnotic)
+      M_Print (160, 112, hipnoticepisodes[startepisode].description);
+   //PGM 01/07/97 added rogue episodes
+   else if (rogue)
+      M_Print (160, 112, rogueepisodes[startepisode].description);
+   else
+      M_Print (160, 112, episodes[startepisode].description);
+
+	M_Print (0, 120, "           Level");
+   //MED 01/06/97 added hipnotic episodes
+   if (hipnotic)
+   {
+      M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
+   }
+   //PGM 01/07/97 added rogue episodes
+   else if (rogue)
+   {
+      M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
+   }
+   else
+   {
+      M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
+      M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
+   }
+
+// line cursor
+	M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
+
+	if (m_serverInfoMessage)
+	{
+		if ((realtime - m_serverInfoMessageTime) < 5.0)
+		{
+			x = (320-26*8)/2;
+			M_DrawTextBox (x, 138, 24, 4);
+			x += 8;
+			M_Print (x, 146, "  More than 4 players   ");
+			M_Print (x, 154, " requires using command ");
+			M_Print (x, 162, "line parameters; please ");
+			M_Print (x, 170, "   see techinfo.txt.    ");
+		}
+		else
+		{
+			m_serverInfoMessage = false;
+		}
+	}
+}
+
+
+void M_NetStart_Change (int dir)
+{
+	int count;
+
+	switch (gameoptions_cursor)
+	{
+	case 1:
+		maxplayers += dir;
+		if (maxplayers > svs.maxclientslimit)
+		{
+			maxplayers = svs.maxclientslimit;
+			m_serverInfoMessage = true;
+			m_serverInfoMessageTime = realtime;
+		}
+		if (maxplayers < 2)
+			maxplayers = 2;
+		break;
+
+	case 2:
+		Cvar_SetValue ("coop", coop.value ? 0 : 1);
+		break;
+
+	case 3:
+		if (rogue)
+			count = 6;
+		else
+			count = 2;
+
+		Cvar_SetValue ("teamplay", teamplay.value + dir);
+		if (teamplay.value > count)
+			Cvar_SetValue ("teamplay", 0);
+		else if (teamplay.value < 0)
+			Cvar_SetValue ("teamplay", count);
+		break;
+
+	case 4:
+		Cvar_SetValue ("skill", skill.value + dir);
+		if (skill.value > 3)
+			Cvar_SetValue ("skill", 0);
+		if (skill.value < 0)
+			Cvar_SetValue ("skill", 3);
+		break;
+
+	case 5:
+		Cvar_SetValue ("fraglimit", fraglimit.value + dir*10);
+		if (fraglimit.value > 100)
+			Cvar_SetValue ("fraglimit", 0);
+		if (fraglimit.value < 0)
+			Cvar_SetValue ("fraglimit", 100);
+		break;
+
+	case 6:
+		Cvar_SetValue ("timelimit", timelimit.value + dir*5);
+		if (timelimit.value > 60)
+			Cvar_SetValue ("timelimit", 0);
+		if (timelimit.value < 0)
+			Cvar_SetValue ("timelimit", 60);
+		break;
+
+	case 7:
+		startepisode += dir;
+	//MED 01/06/97 added hipnotic count
+		if (hipnotic)
+			count = 6;
+	//PGM 01/07/97 added rogue count
+	//PGM 03/02/97 added 1 for dmatch episode
+		else if (rogue)
+			count = 4;
+		else if (registered.value)
+			count = 7;
+		else
+			count = 2;
+
+		if (startepisode < 0)
+			startepisode = count - 1;
+
+		if (startepisode >= count)
+			startepisode = 0;
+
+		startlevel = 0;
+		break;
+
+	case 8:
+		startlevel += dir;
+    //MED 01/06/97 added hipnotic episodes
+		if (hipnotic)
+			count = hipnoticepisodes[startepisode].levels;
+	//PGM 01/06/97 added hipnotic episodes
+		else if (rogue)
+			count = rogueepisodes[startepisode].levels;
+		else
+			count = episodes[startepisode].levels;
+
+		if (startlevel < 0)
+			startlevel = count - 1;
+
+		if (startlevel >= count)
+			startlevel = 0;
+		break;
+	}
+}
+
+void M_GameOptions_Key (int key)
+{
+	switch (key)
+	{
+	case K_ESCAPE:
+		M_Menu_Net_f ();
+		break;
+
+	case K_UPARROW:
+		S_LocalSound ("misc/menu1.wav");
+		gameoptions_cursor--;
+		if (gameoptions_cursor < 0)
+			gameoptions_cursor = NUM_GAMEOPTIONS-1;
+		break;
+
+	case K_DOWNARROW:
+		S_LocalSound ("misc/menu1.wav");
+		gameoptions_cursor++;
+		if (gameoptions_cursor >= NUM_GAMEOPTIONS)
+			gameoptions_cursor = 0;
+		break;
+
+	case K_LEFTARROW:
+		if (gameoptions_cursor == 0)
+			break;
+		S_LocalSound ("misc/menu3.wav");
+		M_NetStart_Change (-1);
+		break;
+
+	case K_RIGHTARROW:
+		if (gameoptions_cursor == 0)
+			break;
+		S_LocalSound ("misc/menu3.wav");
+		M_NetStart_Change (1);
+		break;
+
+	case K_ENTER:
+		S_LocalSound ("misc/menu2.wav");
+		if (gameoptions_cursor == 0)
+		{
+			if (sv.active)
+				Cbuf_AddText ("disconnect\n");
+			Cbuf_AddText ("listen 0\n");	// so host_netport will be re-examined
+			Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
+			SCR_BeginLoadingPlaque ();
+
+			if (hipnotic)
+				Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) );
+			else if (rogue)
+				Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) );
+			else
+				Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
+
+			return;
+		}
+
+		M_NetStart_Change (1);
+		break;
+	}
+}
+
+//=============================================================================
+/* SEARCH MENU */
+
+qboolean	searchComplete = false;
+double		searchCompleteTime;
+
+void M_Menu_Search_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_search;
+	m_entersound = false;
+	slistSilent = true;
+	slistLocal = false;
+	searchComplete = false;
+	NET_Slist_f();
+
+}
+
+
+void M_Search_Draw (void)
+{
+	qpic_t	*p;
+	int x;
+
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+	x = (320/2) - ((12*8)/2) + 4;
+	M_DrawTextBox (x-8, 32, 12, 1);
+	M_Print (x, 40, "Searching...");
+
+	if(slistInProgress)
+	{
+		NET_Poll();
+		return;
+	}
+
+	if (! searchComplete)
+	{
+		searchComplete = true;
+		searchCompleteTime = realtime;
+	}
+
+	if (hostCacheCount)
+	{
+		M_Menu_ServerList_f ();
+		return;
+	}
+
+	M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
+	if ((realtime - searchCompleteTime) < 3.0)
+		return;
+
+	M_Menu_LanConfig_f ();
+}
+
+
+void M_Search_Key (int key)
+{
+}
+
+//=============================================================================
+/* SLIST MENU */
+
+int		slist_cursor;
+qboolean slist_sorted;
+
+void M_Menu_ServerList_f (void)
+{
+	key_dest = key_menu;
+	m_state = m_slist;
+	m_entersound = true;
+	slist_cursor = 0;
+	m_return_onerror = false;
+	m_return_reason[0] = 0;
+	slist_sorted = false;
+}
+
+
+void M_ServerList_Draw (void)
+{
+	int		n;
+	char	string [64];
+	qpic_t	*p;
+
+	if (!slist_sorted)
+	{
+		if (hostCacheCount > 1)
+		{
+			int	i,j;
+			hostcache_t temp;
+			for (i = 0; i < hostCacheCount; i++)
+				for (j = i+1; j < hostCacheCount; j++)
+					if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
+					{
+						Q_memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
+						Q_memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
+						Q_memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
+					}
+		}
+		slist_sorted = true;
+	}
+
+	p = Draw_CachePic ("gfx/p_multi.lmp");
+	M_DrawPic ( (320-p->width)/2, 4, p);
+	for (n = 0; n < hostCacheCount; n++)
+	{
+		if (hostcache[n].maxusers)
+			sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+		else
+			sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+		M_Print (16, 32 + 8*n, string);
+	}
+	M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
+
+	if (*m_return_reason)
+		M_PrintWhite (16, 148, m_return_reason);
+}
+
+
+void M_ServerList_Key (int k)
+{
+	switch (k)
+	{
+	case K_ESCAPE:
+		M_Menu_LanConfig_f ();
+		break;
+
+	case K_SPACE:
+		M_Menu_Search_f ();
+		break;
+
+	case K_UPARROW:
+	case K_LEFTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		slist_cursor--;
+		if (slist_cursor < 0)
+			slist_cursor = hostCacheCount - 1;
+		break;
+
+	case K_DOWNARROW:
+	case K_RIGHTARROW:
+		S_LocalSound ("misc/menu1.wav");
+		slist_cursor++;
+		if (slist_cursor >= hostCacheCount)
+			slist_cursor = 0;
+		break;
+
+	case K_ENTER:
+		S_LocalSound ("misc/menu2.wav");
+		m_return_state = m_state;
+		m_return_onerror = true;
+		slist_sorted = false;
+		key_dest = key_game;
+		m_state = m_none;
+		Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
+		break;
+
+	default:
+		break;
+	}
+
+}
+
+//=============================================================================
+/* Menu Subsystem */
+
+
+void M_Init (void)
+{
+	Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
+
+	Cmd_AddCommand ("menu_main", M_Menu_Main_f);
+	Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
+	Cmd_AddCommand ("menu_load", M_Menu_Load_f);
+	Cmd_AddCommand ("menu_save", M_Menu_Save_f);
+	Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
+	Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
+	Cmd_AddCommand ("menu_options", M_Menu_Options_f);
+	Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
+	Cmd_AddCommand ("menu_video", M_Menu_Video_f);
+	Cmd_AddCommand ("help", M_Menu_Help_f);
+	Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
+}
+
+
+void M_Draw (void)
+{
+	if (m_state == m_none || key_dest != key_menu)
+		return;
+
+	if (!m_recursiveDraw)
+	{
+		scr_copyeverything = 1;
+
+		if (scr_con_current)
+		{
+			Draw_ConsoleBackground (vid.height);
+			VID_UnlockBuffer ();
+			S_ExtraUpdate ();
+			VID_LockBuffer ();
+		}
+		else
+			Draw_FadeScreen ();
+
+		scr_fullupdate = 0;
+	}
+	else
+	{
+		m_recursiveDraw = false;
+	}
+
+	switch (m_state)
+	{
+	case m_none:
+		break;
+
+	case m_main:
+		M_Main_Draw ();
+		break;
+
+	case m_singleplayer:
+		M_SinglePlayer_Draw ();
+		break;
+
+	case m_load:
+		M_Load_Draw ();
+		break;
+
+	case m_save:
+		M_Save_Draw ();
+		break;
+
+	case m_multiplayer:
+		M_MultiPlayer_Draw ();
+		break;
+
+	case m_setup:
+		M_Setup_Draw ();
+		break;
+
+	case m_net:
+		M_Net_Draw ();
+		break;
+
+	case m_options:
+		M_Options_Draw ();
+		break;
+
+	case m_keys:
+		M_Keys_Draw ();
+		break;
+
+	case m_video:
+		M_Video_Draw ();
+		break;
+
+	case m_help:
+		M_Help_Draw ();
+		break;
+
+	case m_quit:
+		M_Quit_Draw ();
+		break;
+
+	case m_serialconfig:
+		M_SerialConfig_Draw ();
+		break;
+
+	case m_modemconfig:
+		M_ModemConfig_Draw ();
+		break;
+
+	case m_lanconfig:
+		M_LanConfig_Draw ();
+		break;
+
+	case m_gameoptions:
+		M_GameOptions_Draw ();
+		break;
+
+	case m_search:
+		M_Search_Draw ();
+		break;
+
+	case m_slist:
+		M_ServerList_Draw ();
+		break;
+	}
+
+	if (m_entersound)
+	{
+		S_LocalSound ("misc/menu2.wav");
+		m_entersound = false;
+	}
+
+	VID_UnlockBuffer ();
+	S_ExtraUpdate ();
+	VID_LockBuffer ();
+}
+
+
+void M_Keydown (int key)
+{
+	switch (m_state)
+	{
+	case m_none:
+		return;
+
+	case m_main:
+		M_Main_Key (key);
+		return;
+
+	case m_singleplayer:
+		M_SinglePlayer_Key (key);
+		return;
+
+	case m_load:
+		M_Load_Key (key);
+		return;
+
+	case m_save:
+		M_Save_Key (key);
+		return;
+
+	case m_multiplayer:
+		M_MultiPlayer_Key (key);
+		return;
+
+	case m_setup:
+		M_Setup_Key (key);
+		return;
+
+	case m_net:
+		M_Net_Key (key);
+		return;
+
+	case m_options:
+		M_Options_Key (key);
+		return;
+
+	case m_keys:
+		M_Keys_Key (key);
+		return;
+
+	case m_video:
+		M_Video_Key (key);
+		return;
+
+	case m_help:
+		M_Help_Key (key);
+		return;
+
+	case m_quit:
+		M_Quit_Key (key);
+		return;
+
+	case m_serialconfig:
+		M_SerialConfig_Key (key);
+		return;
+
+	case m_modemconfig:
+		M_ModemConfig_Key (key);
+		return;
+
+	case m_lanconfig:
+		M_LanConfig_Key (key);
+		return;
+
+	case m_gameoptions:
+		M_GameOptions_Key (key);
+		return;
+
+	case m_search:
+		M_Search_Key (key);
+		break;
+
+	case m_slist:
+		M_ServerList_Key (key);
+		return;
+	}
+}
+
+
+void M_ConfigureNetSubsystem(void)
+{
+// enable/disable net systems to match desired config
+
+	Cbuf_AddText ("stopdemo\n");
+	if (SerialConfig || DirectConfig)
+	{
+		Cbuf_AddText ("com1 enable\n");
+	}
+
+	if (IPXConfig || TCPIPConfig)
+		net_hostport = lanConfig_port;
+}
diff --git a/apps/plugins/sdl/progs/quake/menu.h b/apps/plugins/sdl/progs/quake/menu.h
new file mode 100644
index 0000000..616de3f
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/menu.h
@@ -0,0 +1,38 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+//
+// the net drivers should just set the apropriate bits in m_activenet,
+// instead of having the menu code look through their internal tables
+//
+#define	MNET_IPX		1
+#define	MNET_TCP		2
+
+extern	int	m_activenet;
+
+//
+// menus
+//
+void M_Init (void);
+void M_Keydown (int key);
+void M_Draw (void);
+void M_ToggleMenu_f (void);
+
+
diff --git a/apps/plugins/sdl/progs/quake/model.c b/apps/plugins/sdl/progs/quake/model.c
new file mode 100644
index 0000000..57fdfec
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/model.c
@@ -0,0 +1,1874 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// models.c -- model loading and caching
+
+// models are the only shared resource between a client and server running
+// on the same machine.
+
+#include "quakedef.h"
+#include "r_local.h"
+
+model_t	*loadmodel;
+char	loadname[32];	// for hunk tags
+
+void Mod_LoadSpriteModel (model_t *mod, void *buffer);
+void Mod_LoadBrushModel (model_t *mod, void *buffer);
+void Mod_LoadAliasModel (model_t *mod, void *buffer);
+model_t *Mod_LoadModel (model_t *mod, qboolean crash);
+
+byte	mod_novis[MAX_MAP_LEAFS/8];
+
+#define	MAX_MOD_KNOWN	256
+model_t	mod_known[MAX_MOD_KNOWN];
+int		mod_numknown;
+
+// values for model_t's needload
+#define NL_PRESENT		0
+#define NL_NEEDS_LOADED	1
+#define NL_UNREFERENCED	2
+
+/*
+===============
+Mod_Init
+===============
+*/
+void Mod_Init (void)
+{
+	memset (mod_novis, 0xff, sizeof(mod_novis));
+}
+
+/*
+===============
+Mod_Extradata
+
+Caches the data if needed
+===============
+*/
+void *Mod_Extradata (model_t *mod)
+{
+	void	*r;
+	
+	r = Cache_Check (&mod->cache);
+	if (r)
+		return r;
+
+	Mod_LoadModel (mod, true);
+	
+	if (!mod->cache.data)
+		Sys_Error ("Mod_Extradata: caching failed");
+	return mod->cache.data;
+}
+
+/*
+===============
+Mod_PointInLeaf
+===============
+*/
+mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
+{
+	mnode_t		*node;
+	float		d;
+	mplane_t	*plane;
+	
+	if (!model || !model->nodes)
+		Sys_Error ("Mod_PointInLeaf: bad model");
+
+	node = model->nodes;
+	while (1)
+	{
+		if (node->contents < 0)
+			return (mleaf_t *)node;
+		plane = node->plane;
+		d = DotProduct (p,plane->normal) - plane->dist;
+		if (d > 0)
+			node = node->children[0];
+		else
+			node = node->children[1];
+	}
+	
+	return NULL;	// never reached
+}
+
+
+/*
+===================
+Mod_DecompressVis
+===================
+*/
+byte *Mod_DecompressVis (byte *in, model_t *model)
+{
+	static byte	decompressed[MAX_MAP_LEAFS/8];
+	int		c;
+	byte	*out;
+	int		row;
+
+	row = (model->numleafs+7)>>3;	
+	out = decompressed;
+
+	if (!in)
+	{	// no vis info, so make all visible
+		while (row)
+		{
+			*out++ = 0xff;
+			row--;
+		}
+		return decompressed;		
+	}
+
+	do
+	{
+		if (*in)
+		{
+			*out++ = *in++;
+			continue;
+		}
+	
+		c = in[1];
+		in += 2;
+		while (c)
+		{
+			*out++ = 0;
+			c--;
+		}
+	} while (out - decompressed < row);
+	
+	return decompressed;
+}
+
+byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
+{
+	if (leaf == model->leafs)
+		return mod_novis;
+	return Mod_DecompressVis (leaf->compressed_vis, model);
+}
+
+/*
+===================
+Mod_ClearAll
+===================
+*/
+void Mod_ClearAll (void)
+{
+	int		i;
+	model_t	*mod;
+
+
+	for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++) {
+		mod->needload = NL_UNREFERENCED;
+//FIX FOR CACHE_ALLOC ERRORS:
+		if (mod->type == mod_sprite) mod->cache.data = NULL;
+	}
+}
+
+/*
+==================
+Mod_FindName
+
+==================
+*/
+model_t *Mod_FindName (char *name)
+{
+	int		i;
+	model_t	*mod;
+	model_t	*avail = NULL;
+
+	if (!name[0])
+		Sys_Error ("Mod_ForName: NULL name");
+		
+//
+// search the currently loaded models
+//
+	for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
+	{
+		if (!strcmp (mod->name, name) )
+			break;
+		if (mod->needload == NL_UNREFERENCED)
+			if (!avail || mod->type != mod_alias)
+				avail = mod;
+	}
+			
+	if (i == mod_numknown)
+	{
+		if (mod_numknown == MAX_MOD_KNOWN)
+		{
+			if (avail)
+			{
+				mod = avail;
+				if (mod->type == mod_alias)
+					if (Cache_Check (&mod->cache))
+						Cache_Free (&mod->cache);
+			}
+			else
+				Sys_Error ("mod_numknown == MAX_MOD_KNOWN");
+		}
+		else
+			mod_numknown++;
+		strcpy (mod->name, name);
+		mod->needload = NL_NEEDS_LOADED;
+	}
+
+	return mod;
+}
+
+/*
+==================
+Mod_TouchModel
+
+==================
+*/
+void Mod_TouchModel (char *name)
+{
+	model_t	*mod;
+	
+	mod = Mod_FindName (name);
+	
+	if (mod->needload == NL_PRESENT)
+	{
+		if (mod->type == mod_alias)
+			Cache_Check (&mod->cache);
+	}
+}
+
+/*
+==================
+Mod_LoadModel
+
+Loads a model into the cache
+==================
+*/
+model_t *Mod_LoadModel (model_t *mod, qboolean crash)
+{
+	unsigned *buf;
+	byte	stackbuf[1024];		// avoid dirtying the cache heap
+
+	if (mod->type == mod_alias)
+	{
+		if (Cache_Check (&mod->cache))
+		{
+			mod->needload = NL_PRESENT;
+			return mod;
+		}
+	}
+	else
+	{
+		if (mod->needload == NL_PRESENT)
+			return mod;
+	}
+
+//
+// because the world is so huge, load it one piece at a time
+//
+	
+//
+// load the file
+//
+	buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf));
+	if (!buf)
+	{
+		if (crash)
+			Sys_Error ("Mod_NumForName: %s not found", mod->name);
+		return NULL;
+	}
+	
+//
+// allocate a new model
+//
+	COM_FileBase (mod->name, loadname);
+	
+	loadmodel = mod;
+
+//
+// fill it in
+//
+
+// call the apropriate loader
+	mod->needload = NL_PRESENT;
+
+	switch (LittleLong(*(unsigned *)buf))
+	{
+	case IDPOLYHEADER:
+		Mod_LoadAliasModel (mod, buf);
+		break;
+		
+	case IDSPRITEHEADER:
+		Mod_LoadSpriteModel (mod, buf);
+		break;
+	
+	default:
+		Mod_LoadBrushModel (mod, buf);
+		break;
+	}
+
+	return mod;
+}
+
+/*
+==================
+Mod_ForName
+
+Loads in a model for the given name
+==================
+*/
+model_t *Mod_ForName (char *name, qboolean crash)
+{
+	model_t	*mod;
+
+	mod = Mod_FindName (name);
+
+	return Mod_LoadModel (mod, crash);
+}
+
+
+/*
+===============================================================================
+
+					BRUSHMODEL LOADING
+
+===============================================================================
+*/
+
+byte	*mod_base;
+
+
+/*
+=================
+Mod_LoadTextures
+=================
+*/
+void Mod_LoadTextures (lump_t *l)
+{
+	int		i, j, pixels, num, max, altmax;
+	miptex_t	*mt;
+	texture_t	*tx, *tx2;
+	texture_t	*anims[10];
+	texture_t	*altanims[10];
+	dmiptexlump_t *m;
+
+	if (!l->filelen)
+	{
+		loadmodel->textures = NULL;
+		return;
+	}
+	m = (dmiptexlump_t *)(mod_base + l->fileofs);
+	
+	m->nummiptex = LittleLong (m->nummiptex);
+	
+	loadmodel->numtextures = m->nummiptex;
+	loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname);
+
+	for (i=0 ; i<m->nummiptex ; i++)
+	{
+		m->dataofs[i] = LittleLong(m->dataofs[i]);
+		if (m->dataofs[i] == -1)
+			continue;
+		mt = (miptex_t *)((byte *)m + m->dataofs[i]);
+		mt->width = LittleLong (mt->width);
+		mt->height = LittleLong (mt->height);
+		for (j=0 ; j<MIPLEVELS ; j++)
+			mt->offsets[j] = LittleLong (mt->offsets[j]);
+		
+		if ( (mt->width & 15) || (mt->height & 15) )
+			Sys_Error ("Texture %s is not 16 aligned", mt->name);
+		pixels = mt->width*mt->height/64*85;
+		tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname );
+		loadmodel->textures[i] = tx;
+
+		memcpy (tx->name, mt->name, sizeof(tx->name));
+		tx->width = mt->width;
+		tx->height = mt->height;
+		for (j=0 ; j<MIPLEVELS ; j++)
+			tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t);
+		// the pixels immediately follow the structures
+		memcpy ( tx+1, mt+1, pixels);
+		
+		if (!Q_strncmp(mt->name,"sky",3))	
+			R_InitSky (tx);
+	}
+
+//
+// sequence the animations
+//
+	for (i=0 ; i<m->nummiptex ; i++)
+	{
+		tx = loadmodel->textures[i];
+		if (!tx || tx->name[0] != '+')
+			continue;
+		if (tx->anim_next)
+			continue;	// allready sequenced
+
+	// find the number of frames in the animation
+		memset (anims, 0, sizeof(anims));
+		memset (altanims, 0, sizeof(altanims));
+
+		max = tx->name[1];
+		altmax = 0;
+		if (max >= 'a' && max <= 'z')
+			max -= 'a' - 'A';
+		if (max >= '0' && max <= '9')
+		{
+			max -= '0';
+			altmax = 0;
+			anims[max] = tx;
+			max++;
+		}
+		else if (max >= 'A' && max <= 'J')
+		{
+			altmax = max - 'A';
+			max = 0;
+			altanims[altmax] = tx;
+			altmax++;
+		}
+		else
+			Sys_Error ("Bad animating texture %s", tx->name);
+
+		for (j=i+1 ; j<m->nummiptex ; j++)
+		{
+			tx2 = loadmodel->textures[j];
+			if (!tx2 || tx2->name[0] != '+')
+				continue;
+			if (strcmp (tx2->name+2, tx->name+2))
+				continue;
+
+			num = tx2->name[1];
+			if (num >= 'a' && num <= 'z')
+				num -= 'a' - 'A';
+			if (num >= '0' && num <= '9')
+			{
+				num -= '0';
+				anims[num] = tx2;
+				if (num+1 > max)
+					max = num + 1;
+			}
+			else if (num >= 'A' && num <= 'J')
+			{
+				num = num - 'A';
+				altanims[num] = tx2;
+				if (num+1 > altmax)
+					altmax = num+1;
+			}
+			else
+				Sys_Error ("Bad animating texture %s", tx->name);
+		}
+		
+#define	ANIM_CYCLE	2
+	// link them all together
+		for (j=0 ; j<max ; j++)
+		{
+			tx2 = anims[j];
+			if (!tx2)
+				Sys_Error ("Missing frame %i of %s",j, tx->name);
+			tx2->anim_total = max * ANIM_CYCLE;
+			tx2->anim_min = j * ANIM_CYCLE;
+			tx2->anim_max = (j+1) * ANIM_CYCLE;
+			tx2->anim_next = anims[ (j+1)%max ];
+			if (altmax)
+				tx2->alternate_anims = altanims[0];
+		}
+		for (j=0 ; j<altmax ; j++)
+		{
+			tx2 = altanims[j];
+			if (!tx2)
+				Sys_Error ("Missing frame %i of %s",j, tx->name);
+			tx2->anim_total = altmax * ANIM_CYCLE;
+			tx2->anim_min = j * ANIM_CYCLE;
+			tx2->anim_max = (j+1) * ANIM_CYCLE;
+			tx2->anim_next = altanims[ (j+1)%altmax ];
+			if (max)
+				tx2->alternate_anims = anims[0];
+		}
+	}
+}
+
+/*
+=================
+Mod_LoadLighting
+=================
+*/
+void Mod_LoadLighting (lump_t *l)
+{
+	if (!l->filelen)
+	{
+		loadmodel->lightdata = NULL;
+		return;
+	}
+	loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname);	
+	memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadVisibility
+=================
+*/
+void Mod_LoadVisibility (lump_t *l)
+{
+	if (!l->filelen)
+	{
+		loadmodel->visdata = NULL;
+		return;
+	}
+	loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname);	
+	memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadEntities
+=================
+*/
+void Mod_LoadEntities (lump_t *l)
+{
+	if (!l->filelen)
+	{
+		loadmodel->entities = NULL;
+		return;
+	}
+	loadmodel->entities = Hunk_AllocName ( l->filelen, loadname);	
+	memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
+}
+
+
+/*
+=================
+Mod_LoadVertexes
+=================
+*/
+void Mod_LoadVertexes (lump_t *l)
+{
+	dvertex_t	*in;
+	mvertex_t	*out;
+	int			i, count;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->vertexes = out;
+	loadmodel->numvertexes = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		out->position[0] = LittleFloat (in->point[0]);
+		out->position[1] = LittleFloat (in->point[1]);
+		out->position[2] = LittleFloat (in->point[2]);
+	}
+}
+
+/*
+=================
+Mod_LoadSubmodels
+=================
+*/
+void Mod_LoadSubmodels (lump_t *l)
+{
+	dmodel_t	*in;
+	dmodel_t	*out;
+	int			i, j, count;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->submodels = out;
+	loadmodel->numsubmodels = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		for (j=0 ; j<3 ; j++)
+		{	// spread the mins / maxs by a pixel
+			out->mins[j] = LittleFloat (in->mins[j]) - 1;
+			out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
+			out->origin[j] = LittleFloat (in->origin[j]);
+		}
+		for (j=0 ; j<MAX_MAP_HULLS ; j++)
+			out->headnode[j] = LittleLong (in->headnode[j]);
+		out->visleafs = LittleLong (in->visleafs);
+		out->firstface = LittleLong (in->firstface);
+		out->numfaces = LittleLong (in->numfaces);
+	}
+}
+
+/*
+=================
+Mod_LoadEdges
+=================
+*/
+void Mod_LoadEdges (lump_t *l)
+{
+	dedge_t *in;
+	medge_t *out;
+	int 	i, count;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname);	
+
+	loadmodel->edges = out;
+	loadmodel->numedges = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		out->v[0] = (unsigned short)LittleShort(in->v[0]);
+		out->v[1] = (unsigned short)LittleShort(in->v[1]);
+	}
+}
+
+/*
+=================
+Mod_LoadTexinfo
+=================
+*/
+void Mod_LoadTexinfo (lump_t *l)
+{
+	texinfo_t *in;
+	mtexinfo_t *out;
+	int 	i, j, count;
+	int		miptex;
+	float	len1, len2;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->texinfo = out;
+	loadmodel->numtexinfo = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		for (j=0 ; j<8 ; j++)
+			out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
+		len1 = Length (out->vecs[0]);
+		len2 = Length (out->vecs[1]);
+		len1 = (len1 + len2)/2;
+		if (len1 < 0.32)
+			out->mipadjust = 4;
+		else if (len1 < 0.49)
+			out->mipadjust = 3;
+		else if (len1 < 0.99)
+			out->mipadjust = 2;
+		else
+			out->mipadjust = 1;
+#if 0
+		if (len1 + len2 < 0.001)
+			out->mipadjust = 1;		// don't crash
+		else
+			out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 );
+#endif
+
+		miptex = LittleLong (in->miptex);
+		out->flags = LittleLong (in->flags);
+	
+		if (!loadmodel->textures)
+		{
+			out->texture = r_notexture_mip;	// checkerboard texture
+			out->flags = 0;
+		}
+		else
+		{
+			if (miptex >= loadmodel->numtextures)
+				Sys_Error ("miptex >= loadmodel->numtextures");
+			out->texture = loadmodel->textures[miptex];
+			if (!out->texture)
+			{
+				out->texture = r_notexture_mip; // texture not found
+				out->flags = 0;
+			}
+		}
+	}
+}
+
+/*
+================
+CalcSurfaceExtents
+
+Fills in s->texturemins[] and s->extents[]
+================
+*/
+void CalcSurfaceExtents (msurface_t *s)
+{
+	float	mins[2], maxs[2], val;
+	int		i,j, e;
+	mvertex_t	*v;
+	mtexinfo_t	*tex;
+	int		bmins[2], bmaxs[2];
+
+	mins[0] = mins[1] = 999999;
+	maxs[0] = maxs[1] = -99999;
+
+	tex = s->texinfo;
+	
+	for (i=0 ; i<s->numedges ; i++)
+	{
+		e = loadmodel->surfedges[s->firstedge+i];
+		if (e >= 0)
+			v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
+		else
+			v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
+		
+		for (j=0 ; j<2 ; j++)
+		{
+			val = v->position[0] * tex->vecs[j][0] + 
+				v->position[1] * tex->vecs[j][1] +
+				v->position[2] * tex->vecs[j][2] +
+				tex->vecs[j][3];
+			if (val < mins[j])
+				mins[j] = val;
+			if (val > maxs[j])
+				maxs[j] = val;
+		}
+	}
+
+	for (i=0 ; i<2 ; i++)
+	{	
+		bmins[i] = floor(mins[i]/16);
+		bmaxs[i] = ceil(maxs[i]/16);
+
+		s->texturemins[i] = bmins[i] * 16;
+		s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
+		if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256)
+			Sys_Error ("Bad surface extents");
+	}
+}
+
+
+/*
+=================
+Mod_LoadFaces
+=================
+*/
+void Mod_LoadFaces (lump_t *l)
+{
+	dface_t		*in;
+	msurface_t 	*out;
+	int			i, count, surfnum;
+	int			planenum, side;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->surfaces = out;
+	loadmodel->numsurfaces = count;
+
+	for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
+	{
+		out->firstedge = LittleLong(in->firstedge);
+		out->numedges = LittleShort(in->numedges);		
+		out->flags = 0;
+
+		planenum = LittleShort(in->planenum);
+		side = LittleShort(in->side);
+		if (side)
+			out->flags |= SURF_PLANEBACK;			
+
+		out->plane = loadmodel->planes + planenum;
+
+		out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
+
+		CalcSurfaceExtents (out);
+				
+	// lighting info
+
+		for (i=0 ; i<MAXLIGHTMAPS ; i++)
+			out->styles[i] = in->styles[i];
+		i = LittleLong(in->lightofs);
+		if (i == -1)
+			out->samples = NULL;
+		else
+			out->samples = loadmodel->lightdata + i;
+		
+	// set the drawing flags flag
+		
+		if (!Q_strncmp(out->texinfo->texture->name,"sky",3))	// sky
+		{
+			out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
+			continue;
+		}
+		
+		if (!Q_strncmp(out->texinfo->texture->name,"*",1))		// turbulent
+		{
+			out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED);
+			for (i=0 ; i<2 ; i++)
+			{
+				out->extents[i] = 16384;
+				out->texturemins[i] = -8192;
+			}
+			continue;
+		}
+	}
+}
+
+
+/*
+=================
+Mod_SetParent
+=================
+*/
+void Mod_SetParent (mnode_t *node, mnode_t *parent)
+{
+	node->parent = parent;
+	if (node->contents < 0)
+		return;
+	Mod_SetParent (node->children[0], node);
+	Mod_SetParent (node->children[1], node);
+}
+
+/*
+=================
+Mod_LoadNodes
+=================
+*/
+void Mod_LoadNodes (lump_t *l)
+{
+	int			i, j, count, p;
+	dnode_t		*in;
+	mnode_t 	*out;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->nodes = out;
+	loadmodel->numnodes = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		for (j=0 ; j<3 ; j++)
+		{
+			out->minmaxs[j] = LittleShort (in->mins[j]);
+			out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+		}
+	
+		p = LittleLong(in->planenum);
+		out->plane = loadmodel->planes + p;
+
+		out->firstsurface = LittleShort (in->firstface);
+		out->numsurfaces = LittleShort (in->numfaces);
+		
+		for (j=0 ; j<2 ; j++)
+		{
+			p = LittleShort (in->children[j]);
+			if (p >= 0)
+				out->children[j] = loadmodel->nodes + p;
+			else
+				out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
+		}
+	}
+	
+	Mod_SetParent (loadmodel->nodes, NULL);	// sets nodes and leafs
+}
+
+/*
+=================
+Mod_LoadLeafs
+=================
+*/
+void Mod_LoadLeafs (lump_t *l)
+{
+	dleaf_t 	*in;
+	mleaf_t 	*out;
+	int			i, j, count, p;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->leafs = out;
+	loadmodel->numleafs = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		for (j=0 ; j<3 ; j++)
+		{
+			out->minmaxs[j] = LittleShort (in->mins[j]);
+			out->minmaxs[3+j] = LittleShort (in->maxs[j]);
+		}
+
+		p = LittleLong(in->contents);
+		out->contents = p;
+
+		out->firstmarksurface = loadmodel->marksurfaces +
+			LittleShort(in->firstmarksurface);
+		out->nummarksurfaces = LittleShort(in->nummarksurfaces);
+		
+		p = LittleLong(in->visofs);
+		if (p == -1)
+			out->compressed_vis = NULL;
+		else
+			out->compressed_vis = loadmodel->visdata + p;
+		out->efrags = NULL;
+		
+		for (j=0 ; j<4 ; j++)
+			out->ambient_sound_level[j] = in->ambient_level[j];
+	}	
+}
+
+/*
+=================
+Mod_LoadClipnodes
+=================
+*/
+void Mod_LoadClipnodes (lump_t *l)
+{
+	dclipnode_t *in, *out;
+	int			i, count;
+	hull_t		*hull;
+
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->clipnodes = out;
+	loadmodel->numclipnodes = count;
+
+	hull = &loadmodel->hulls[1];
+	hull->clipnodes = out;
+	hull->firstclipnode = 0;
+	hull->lastclipnode = count-1;
+	hull->planes = loadmodel->planes;
+	hull->clip_mins[0] = -16;
+	hull->clip_mins[1] = -16;
+	hull->clip_mins[2] = -24;
+	hull->clip_maxs[0] = 16;
+	hull->clip_maxs[1] = 16;
+	hull->clip_maxs[2] = 32;
+
+	hull = &loadmodel->hulls[2];
+	hull->clipnodes = out;
+	hull->firstclipnode = 0;
+	hull->lastclipnode = count-1;
+	hull->planes = loadmodel->planes;
+	hull->clip_mins[0] = -32;
+	hull->clip_mins[1] = -32;
+	hull->clip_mins[2] = -24;
+	hull->clip_maxs[0] = 32;
+	hull->clip_maxs[1] = 32;
+	hull->clip_maxs[2] = 64;
+
+	for (i=0 ; i<count ; i++, out++, in++)
+	{
+		out->planenum = LittleLong(in->planenum);
+		out->children[0] = LittleShort(in->children[0]);
+		out->children[1] = LittleShort(in->children[1]);
+	}
+}
+
+/*
+=================
+Mod_MakeHull0
+
+Deplicate the drawing hull structure as a clipping hull
+=================
+*/
+void Mod_MakeHull0 (void)
+{
+	mnode_t		*in, *child;
+	dclipnode_t *out;
+	int			i, j, count;
+	hull_t		*hull;
+	
+	hull = &loadmodel->hulls[0];	
+	
+	in = loadmodel->nodes;
+	count = loadmodel->numnodes;
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	hull->clipnodes = out;
+	hull->firstclipnode = 0;
+	hull->lastclipnode = count-1;
+	hull->planes = loadmodel->planes;
+
+	for (i=0 ; i<count ; i++, out++, in++)
+	{
+		out->planenum = in->plane - loadmodel->planes;
+		for (j=0 ; j<2 ; j++)
+		{
+			child = in->children[j];
+			if (child->contents < 0)
+				out->children[j] = child->contents;
+			else
+				out->children[j] = child - loadmodel->nodes;
+		}
+	}
+}
+
+/*
+=================
+Mod_LoadMarksurfaces
+=================
+*/
+void Mod_LoadMarksurfaces (lump_t *l)
+{	
+	int		i, j, count;
+	short		*in;
+	msurface_t **out;
+	
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->marksurfaces = out;
+	loadmodel->nummarksurfaces = count;
+
+	for ( i=0 ; i<count ; i++)
+	{
+		j = LittleShort(in[i]);
+		if (j >= loadmodel->numsurfaces)
+			Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
+		out[i] = loadmodel->surfaces + j;
+	}
+}
+
+/*
+=================
+Mod_LoadSurfedges
+=================
+*/
+void Mod_LoadSurfedges (lump_t *l)
+{	
+	int		i, count;
+	int		*in, *out;
+	
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*sizeof(*out), loadname);	
+
+	loadmodel->surfedges = out;
+	loadmodel->numsurfedges = count;
+
+	for ( i=0 ; i<count ; i++)
+		out[i] = LittleLong (in[i]);
+}
+
+/*
+=================
+Mod_LoadPlanes
+=================
+*/
+void Mod_LoadPlanes (lump_t *l)
+{
+	int			i, j;
+	mplane_t	*out;
+	dplane_t 	*in;
+	int			count;
+	int			bits;
+	
+	in = (void *)(mod_base + l->fileofs);
+	if (l->filelen % sizeof(*in))
+		Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
+	count = l->filelen / sizeof(*in);
+	out = Hunk_AllocName ( count*2*sizeof(*out), loadname);	
+	
+	loadmodel->planes = out;
+	loadmodel->numplanes = count;
+
+	for ( i=0 ; i<count ; i++, in++, out++)
+	{
+		bits = 0;
+		for (j=0 ; j<3 ; j++)
+		{
+			out->normal[j] = LittleFloat (in->normal[j]);
+			if (out->normal[j] < 0)
+				bits |= 1<<j;
+		}
+
+		out->dist = LittleFloat (in->dist);
+		out->type = LittleLong (in->type);
+		out->signbits = bits;
+	}
+}
+
+/*
+=================
+RadiusFromBounds
+=================
+*/
+float RadiusFromBounds (vec3_t mins, vec3_t maxs)
+{
+	int		i;
+	vec3_t	corner;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
+	}
+
+	return Length (corner);
+}
+
+/*
+=================
+Mod_LoadBrushModel
+=================
+*/
+void Mod_LoadBrushModel (model_t *mod, void *buffer)
+{
+	int			i, j;
+	dheader_t	*header;
+	dmodel_t 	*bm;
+	
+	loadmodel->type = mod_brush;
+	
+	header = (dheader_t *)buffer;
+
+	i = LittleLong (header->version);
+	if (i != BSPVERSION)
+            rb->splashf (HZ, "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
+
+// swap all the lumps
+	mod_base = (byte *)header;
+
+	for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
+		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+// load into heap
+	
+	Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
+	Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
+	Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
+	Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
+	Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
+	Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
+	Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
+	Mod_LoadFaces (&header->lumps[LUMP_FACES]);
+	Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
+	Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
+	Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
+	Mod_LoadNodes (&header->lumps[LUMP_NODES]);
+	Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
+	Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
+	Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
+
+	Mod_MakeHull0 ();
+	
+	mod->numframes = 2;		// regular and alternate animation
+	mod->flags = 0;
+	
+//
+// set up the submodels (FIXME: this is confusing)
+//
+	for (i=0 ; i<mod->numsubmodels ; i++)
+	{
+		bm = &mod->submodels[i];
+
+		mod->hulls[0].firstclipnode = bm->headnode[0];
+		for (j=1 ; j<MAX_MAP_HULLS ; j++)
+		{
+			mod->hulls[j].firstclipnode = bm->headnode[j];
+			mod->hulls[j].lastclipnode = mod->numclipnodes-1;
+		}
+		
+		mod->firstmodelsurface = bm->firstface;
+		mod->nummodelsurfaces = bm->numfaces;
+		
+		VectorCopy (bm->maxs, mod->maxs);
+		VectorCopy (bm->mins, mod->mins);
+		mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
+		
+		mod->numleafs = bm->visleafs;
+
+		if (i < mod->numsubmodels-1)
+		{	// duplicate the basic information
+			char	name[10];
+
+			sprintf (name, "*%i", i+1);
+			loadmodel = Mod_FindName (name);
+			*loadmodel = *mod;
+			strcpy (loadmodel->name, name);
+			mod = loadmodel;
+		}
+	}
+}
+
+/*
+==============================================================================
+
+ALIAS MODELS
+
+==============================================================================
+*/
+
+/*
+=================
+Mod_LoadAliasFrame
+=================
+*/
+void * Mod_LoadAliasFrame (void * pin, int *pframeindex, int numv,
+	trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
+{
+	trivertx_t		*pframe, *pinframe;
+	int				i, j;
+	daliasframe_t	*pdaliasframe;
+
+	pdaliasframe = (daliasframe_t *)pin;
+
+	strcpy (name, pdaliasframe->name);
+
+	for (i=0 ; i<3 ; i++)
+	{
+	// these are byte values, so we don't have to worry about
+	// endianness
+		pbboxmin->v[i] = pdaliasframe->bboxmin.v[i];
+		pbboxmax->v[i] = pdaliasframe->bboxmax.v[i];
+	}
+
+	pinframe = (trivertx_t *)(pdaliasframe + 1);
+	pframe = Hunk_AllocName (numv * sizeof(*pframe), loadname);
+
+	*pframeindex = (byte *)pframe - (byte *)pheader;
+
+	for (j=0 ; j<numv ; j++)
+	{
+		int		k;
+
+	// these are all byte values, so no need to deal with endianness
+		pframe[j].lightnormalindex = pinframe[j].lightnormalindex;
+
+		for (k=0 ; k<3 ; k++)
+		{
+			pframe[j].v[k] = pinframe[j].v[k];
+		}
+	}
+
+	pinframe += numv;
+
+	return (void *)pinframe;
+}
+
+
+/*
+=================
+Mod_LoadAliasGroup
+=================
+*/
+void * Mod_LoadAliasGroup (void * pin, int *pframeindex, int numv,
+	trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
+{
+	daliasgroup_t		*pingroup;
+	maliasgroup_t		*paliasgroup;
+	int					i, numframes;
+	daliasinterval_t	*pin_intervals;
+	float				*poutintervals;
+	void				*ptemp;
+	
+	pingroup = (daliasgroup_t *)pin;
+
+	numframes = LittleLong (pingroup->numframes);
+
+	paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) +
+			(numframes - 1) * sizeof (paliasgroup->frames[0]), loadname);
+
+	paliasgroup->numframes = numframes;
+
+	for (i=0 ; i<3 ; i++)
+	{
+	// these are byte values, so we don't have to worry about endianness
+		pbboxmin->v[i] = pingroup->bboxmin.v[i];
+		pbboxmax->v[i] = pingroup->bboxmax.v[i];
+	}
+
+	*pframeindex = (byte *)paliasgroup - (byte *)pheader;
+
+	pin_intervals = (daliasinterval_t *)(pingroup + 1);
+
+	poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
+
+	paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader;
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		*poutintervals = LittleFloat (pin_intervals->interval);
+		if (*poutintervals <= 0.0)
+			Sys_Error ("Mod_LoadAliasGroup: interval<=0");
+
+		poutintervals++;
+		pin_intervals++;
+	}
+
+	ptemp = (void *)pin_intervals;
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		ptemp = Mod_LoadAliasFrame (ptemp,
+									&paliasgroup->frames[i].frame,
+									numv,
+									&paliasgroup->frames[i].bboxmin,
+									&paliasgroup->frames[i].bboxmax,
+									pheader, name);
+	}
+
+	return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadAliasSkin
+=================
+*/
+void * Mod_LoadAliasSkin (void * pin, int *pskinindex, int skinsize,
+	aliashdr_t *pheader)
+{
+	int		i;
+	byte	*pskin, *pinskin;
+	unsigned short	*pusskin;
+
+	pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname);
+	pinskin = (byte *)pin;
+	*pskinindex = (byte *)pskin - (byte *)pheader;
+
+	if (r_pixbytes == 1)
+	{
+		Q_memcpy (pskin, pinskin, skinsize);
+	}
+	else if (r_pixbytes == 2)
+	{
+		pusskin = (unsigned short *)pskin;
+
+		for (i=0 ; i<skinsize ; i++)
+			pusskin[i] = d_8to16table[pinskin[i]];
+	}
+	else
+	{
+		Sys_Error ("Mod_LoadAliasSkin: driver set invalid r_pixbytes: %d\n",
+				 r_pixbytes);
+	}
+
+	pinskin += skinsize;
+
+	return ((void *)pinskin);
+}
+
+
+/*
+=================
+Mod_LoadAliasSkinGroup
+=================
+*/
+void * Mod_LoadAliasSkinGroup (void * pin, int *pskinindex, int skinsize,
+	aliashdr_t *pheader)
+{
+	daliasskingroup_t		*pinskingroup;
+	maliasskingroup_t		*paliasskingroup;
+	int						i, numskins;
+	daliasskininterval_t	*pinskinintervals;
+	float					*poutskinintervals;
+	void					*ptemp;
+
+	pinskingroup = (daliasskingroup_t *)pin;
+
+	numskins = LittleLong (pinskingroup->numskins);
+
+	paliasskingroup = Hunk_AllocName (sizeof (maliasskingroup_t) +
+			(numskins - 1) * sizeof (paliasskingroup->skindescs[0]),
+			loadname);
+
+	paliasskingroup->numskins = numskins;
+
+	*pskinindex = (byte *)paliasskingroup - (byte *)pheader;
+
+	pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
+
+	poutskinintervals = Hunk_AllocName (numskins * sizeof (float),loadname);
+
+	paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader;
+
+	for (i=0 ; i<numskins ; i++)
+	{
+		*poutskinintervals = LittleFloat (pinskinintervals->interval);
+		if (*poutskinintervals <= 0)
+			Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0");
+
+		poutskinintervals++;
+		pinskinintervals++;
+	}
+
+	ptemp = (void *)pinskinintervals;
+
+	for (i=0 ; i<numskins ; i++)
+	{
+		ptemp = Mod_LoadAliasSkin (ptemp,
+				&paliasskingroup->skindescs[i].skin, skinsize, pheader);
+	}
+
+	return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadAliasModel
+=================
+*/
+void Mod_LoadAliasModel (model_t *mod, void *buffer)
+{
+	int					i;
+	mdl_t				*pmodel, *pinmodel;
+	stvert_t			*pstverts, *pinstverts;
+	aliashdr_t			*pheader;
+	mtriangle_t			*ptri;
+	dtriangle_t			*pintriangles;
+	int					version, numframes, numskins;
+	int					size;
+	daliasframetype_t	*pframetype;
+	daliasskintype_t	*pskintype;
+	maliasskindesc_t	*pskindesc;
+	int					skinsize;
+	int					start, end, total;
+	
+	start = Hunk_LowMark ();
+
+	pinmodel = (mdl_t *)buffer;
+
+	version = LittleLong (pinmodel->version);
+	if (version != ALIAS_VERSION)
+		Sys_Error ("%s has wrong version number (%i should be %i)",
+				 mod->name, version, ALIAS_VERSION);
+
+//
+// allocate space for a working header, plus all the data except the frames,
+// skin and group info
+//
+	size = 	sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) *
+			 sizeof (pheader->frames[0]) +
+			sizeof (mdl_t) +
+			LittleLong (pinmodel->numverts) * sizeof (stvert_t) +
+			LittleLong (pinmodel->numtris) * sizeof (mtriangle_t);
+
+	pheader = Hunk_AllocName (size, loadname);
+	pmodel = (mdl_t *) ((byte *)&pheader[1] +
+			(LittleLong (pinmodel->numframes) - 1) *
+			 sizeof (pheader->frames[0]));
+	
+//	mod->cache.data = pheader;
+	mod->flags = LittleLong (pinmodel->flags);
+
+//
+// endian-adjust and copy the data, starting with the alias model header
+//
+	pmodel->boundingradius = LittleFloat (pinmodel->boundingradius);
+	pmodel->numskins = LittleLong (pinmodel->numskins);
+	pmodel->skinwidth = LittleLong (pinmodel->skinwidth);
+	pmodel->skinheight = LittleLong (pinmodel->skinheight);
+
+	if (pmodel->skinheight > MAX_LBM_HEIGHT)
+		Sys_Error ("model %s has a skin taller than %d", mod->name,
+				   MAX_LBM_HEIGHT);
+
+	pmodel->numverts = LittleLong (pinmodel->numverts);
+
+	if (pmodel->numverts <= 0)
+		Sys_Error ("model %s has no vertices", mod->name);
+
+	if (pmodel->numverts > MAXALIASVERTS)
+		Sys_Error ("model %s has too many vertices", mod->name);
+
+	pmodel->numtris = LittleLong (pinmodel->numtris);
+
+	if (pmodel->numtris <= 0)
+		Sys_Error ("model %s has no triangles", mod->name);
+
+	pmodel->numframes = LittleLong (pinmodel->numframes);
+	pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
+	mod->synctype = LittleLong (pinmodel->synctype);
+	mod->numframes = pmodel->numframes;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		pmodel->scale[i] = LittleFloat (pinmodel->scale[i]);
+		pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
+		pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
+	}
+
+	numskins = pmodel->numskins;
+	numframes = pmodel->numframes;
+
+	if (pmodel->skinwidth & 0x03)
+		Sys_Error ("Mod_LoadAliasModel: skinwidth not multiple of 4");
+
+	pheader->model = (byte *)pmodel - (byte *)pheader;
+
+//
+// load the skins
+//
+	skinsize = pmodel->skinheight * pmodel->skinwidth;
+
+	if (numskins < 1)
+		Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
+
+	pskintype = (daliasskintype_t *)&pinmodel[1];
+
+	pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t),
+								loadname);
+
+	pheader->skindesc = (byte *)pskindesc - (byte *)pheader;
+
+	for (i=0 ; i<numskins ; i++)
+	{
+		aliasskintype_t	skintype;
+
+		skintype = LittleLong (pskintype->type);
+		pskindesc[i].type = skintype;
+
+		if (skintype == ALIAS_SKIN_SINGLE)
+		{
+			pskintype = (daliasskintype_t *)
+					Mod_LoadAliasSkin (pskintype + 1,
+									   &pskindesc[i].skin,
+									   skinsize, pheader);
+		}
+		else
+		{
+			pskintype = (daliasskintype_t *)
+					Mod_LoadAliasSkinGroup (pskintype + 1,
+											&pskindesc[i].skin,
+											skinsize, pheader);
+		}
+	}
+
+//
+// set base s and t vertices
+//
+	pstverts = (stvert_t *)&pmodel[1];
+	pinstverts = (stvert_t *)pskintype;
+
+	pheader->stverts = (byte *)pstverts - (byte *)pheader;
+
+	for (i=0 ; i<pmodel->numverts ; i++)
+	{
+		pstverts[i].onseam = LittleLong (pinstverts[i].onseam);
+	// put s and t in 16.16 format
+		pstverts[i].s = LittleLong (pinstverts[i].s) << 16;
+		pstverts[i].t = LittleLong (pinstverts[i].t) << 16;
+	}
+
+//
+// set up the triangles
+//
+	ptri = (mtriangle_t *)&pstverts[pmodel->numverts];
+	pintriangles = (dtriangle_t *)&pinstverts[pmodel->numverts];
+
+	pheader->triangles = (byte *)ptri - (byte *)pheader;
+
+	for (i=0 ; i<pmodel->numtris ; i++)
+	{
+		int		j;
+
+		ptri[i].facesfront = LittleLong (pintriangles[i].facesfront);
+
+		for (j=0 ; j<3 ; j++)
+		{
+			ptri[i].vertindex[j] =
+					LittleLong (pintriangles[i].vertindex[j]);
+		}
+	}
+
+//
+// load the frames
+//
+	if (numframes < 1)
+		Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
+
+	pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris];
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		aliasframetype_t	frametype;
+
+		frametype = LittleLong (pframetype->type);
+		pheader->frames[i].type = frametype;
+
+		if (frametype == ALIAS_SINGLE)
+		{
+			pframetype = (daliasframetype_t *)
+					Mod_LoadAliasFrame (pframetype + 1,
+										&pheader->frames[i].frame,
+										pmodel->numverts,
+										&pheader->frames[i].bboxmin,
+										&pheader->frames[i].bboxmax,
+										pheader, pheader->frames[i].name);
+		}
+		else
+		{
+			pframetype = (daliasframetype_t *)
+					Mod_LoadAliasGroup (pframetype + 1,
+										&pheader->frames[i].frame,
+										pmodel->numverts,
+										&pheader->frames[i].bboxmin,
+										&pheader->frames[i].bboxmax,
+										pheader, pheader->frames[i].name);
+		}
+	}
+
+	mod->type = mod_alias;
+
+// FIXME: do this right
+	mod->mins[0] = mod->mins[1] = mod->mins[2] = -16;
+	mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16;
+
+//
+// move the complete, relocatable alias model to the cache
+//	
+	end = Hunk_LowMark ();
+	total = end - start;
+	
+	Cache_Alloc (&mod->cache, total, loadname);
+	if (!mod->cache.data)
+		return;
+	memcpy (mod->cache.data, pheader, total);
+
+	Hunk_FreeToLowMark (start);
+}
+
+//=============================================================================
+
+/*
+=================
+Mod_LoadSpriteFrame
+=================
+*/
+void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe)
+{
+	dspriteframe_t		*pinframe;
+	mspriteframe_t		*pspriteframe;
+	int					i, width, height, size, origin[2];
+	unsigned short		*ppixout;
+	byte				*ppixin;
+
+	pinframe = (dspriteframe_t *)pin;
+
+	width = LittleLong (pinframe->width);
+	height = LittleLong (pinframe->height);
+	size = width * height;
+
+	pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes,
+								   loadname);
+
+	Q_memset (pspriteframe, 0, sizeof (mspriteframe_t) + size);
+	*ppframe = pspriteframe;
+
+	pspriteframe->width = width;
+	pspriteframe->height = height;
+	origin[0] = LittleLong (pinframe->origin[0]);
+	origin[1] = LittleLong (pinframe->origin[1]);
+
+	pspriteframe->up = origin[1];
+	pspriteframe->down = origin[1] - height;
+	pspriteframe->left = origin[0];
+	pspriteframe->right = width + origin[0];
+
+	if (r_pixbytes == 1)
+	{
+		Q_memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size);
+	}
+	else if (r_pixbytes == 2)
+	{
+		ppixin = (byte *)(pinframe + 1);
+		ppixout = (unsigned short *)&pspriteframe->pixels[0];
+
+		for (i=0 ; i<size ; i++)
+			ppixout[i] = d_8to16table[ppixin[i]];
+	}
+	else
+	{
+		Sys_Error ("Mod_LoadSpriteFrame: driver set invalid r_pixbytes: %d\n",
+				 r_pixbytes);
+	}
+
+	return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
+}
+
+
+/*
+=================
+Mod_LoadSpriteGroup
+=================
+*/
+void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe)
+{
+	dspritegroup_t		*pingroup;
+	mspritegroup_t		*pspritegroup;
+	int					i, numframes;
+	dspriteinterval_t	*pin_intervals;
+	float				*poutintervals;
+	void				*ptemp;
+
+	pingroup = (dspritegroup_t *)pin;
+
+	numframes = LittleLong (pingroup->numframes);
+
+	pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) +
+				(numframes - 1) * sizeof (pspritegroup->frames[0]), loadname);
+
+	pspritegroup->numframes = numframes;
+
+	*ppframe = (mspriteframe_t *)pspritegroup;
+
+	pin_intervals = (dspriteinterval_t *)(pingroup + 1);
+
+	poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname);
+
+	pspritegroup->intervals = poutintervals;
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		*poutintervals = LittleFloat (pin_intervals->interval);
+		if (*poutintervals <= 0.0)
+			Sys_Error ("Mod_LoadSpriteGroup: interval<=0");
+
+		poutintervals++;
+		pin_intervals++;
+	}
+
+	ptemp = (void *)pin_intervals;
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i]);
+	}
+
+	return ptemp;
+}
+
+
+/*
+=================
+Mod_LoadSpriteModel
+=================
+*/
+void Mod_LoadSpriteModel (model_t *mod, void *buffer)
+{
+	int					i;
+	int					version;
+	dsprite_t			*pin;
+	msprite_t			*psprite;
+	int					numframes;
+	int					size;
+	dspriteframetype_t	*pframetype;
+	
+	pin = (dsprite_t *)buffer;
+
+	version = LittleLong (pin->version);
+	if (version != SPRITE_VERSION)
+		Sys_Error ("%s has wrong version number "
+				 "(%i should be %i)", mod->name, version, SPRITE_VERSION);
+
+	numframes = LittleLong (pin->numframes);
+
+	size = sizeof (msprite_t) +	(numframes - 1) * sizeof (psprite->frames);
+
+	psprite = Hunk_AllocName (size, loadname);
+
+	mod->cache.data = psprite;
+
+	psprite->type = LittleLong (pin->type);
+	psprite->maxwidth = LittleLong (pin->width);
+	psprite->maxheight = LittleLong (pin->height);
+	psprite->beamlength = LittleFloat (pin->beamlength);
+	mod->synctype = LittleLong (pin->synctype);
+	psprite->numframes = numframes;
+
+	mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;
+	mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;
+	mod->mins[2] = -psprite->maxheight/2;
+	mod->maxs[2] = psprite->maxheight/2;
+	
+//
+// load the frames
+//
+	if (numframes < 1)
+		Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
+
+	mod->numframes = numframes;
+	mod->flags = 0;
+
+	pframetype = (dspriteframetype_t *)(pin + 1);
+
+	for (i=0 ; i<numframes ; i++)
+	{
+		spriteframetype_t	frametype;
+
+		frametype = LittleLong (pframetype->type);
+		psprite->frames[i].type = frametype;
+
+		if (frametype == SPR_SINGLE)
+		{
+			pframetype = (dspriteframetype_t *)
+					Mod_LoadSpriteFrame (pframetype + 1,
+										 &psprite->frames[i].frameptr);
+		}
+		else
+		{
+			pframetype = (dspriteframetype_t *)
+					Mod_LoadSpriteGroup (pframetype + 1,
+										 &psprite->frames[i].frameptr);
+		}
+	}
+
+	mod->type = mod_sprite;
+}
+
+//=============================================================================
+
+/*
+================
+Mod_Print
+================
+*/
+void Mod_Print (void)
+{
+	int		i;
+	model_t	*mod;
+
+	Con_Printf ("Cached models:\n");
+	for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
+	{
+		Con_Printf ("%8p : %s",mod->cache.data, mod->name);
+		if (mod->needload & NL_UNREFERENCED)
+			Con_Printf (" (!R)");
+		if (mod->needload & NL_NEEDS_LOADED)
+			Con_Printf (" (!P)");
+		Con_Printf ("\n");
+	}
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/model.h b/apps/plugins/sdl/progs/quake/model.h
new file mode 100644
index 0000000..899010f
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/model.h
@@ -0,0 +1,383 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 __MODEL__
+#define __MODEL__
+
+#include "modelgen.h"
+#include "spritegn.h"
+
+/*
+
+d*_t structures are on-disk representations
+m*_t structures are in-memory
+
+*/
+
+/*
+==============================================================================
+
+BRUSH MODELS
+
+==============================================================================
+*/
+
+
+//
+// in memory representation
+//
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+	vec3_t		position;
+} mvertex_t;
+
+#define	SIDE_FRONT	0
+#define	SIDE_BACK	1
+#define	SIDE_ON		2
+
+
+// plane_t structure
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct mplane_s
+{
+	vec3_t	normal;
+	float	dist;
+	byte	type;			// for texture axis selection and fast side tests
+	byte	signbits;		// signx + signy<<1 + signz<<1
+	byte	pad[2];
+} mplane_t;
+
+typedef struct texture_s
+{
+	char		name[16];
+	unsigned	width, height;
+	int			anim_total;				// total tenths in sequence ( 0 = no)
+	int			anim_min, anim_max;		// time for this frame min <=time< max
+	struct texture_s *anim_next;		// in the animation sequence
+	struct texture_s *alternate_anims;	// bmodels in frmae 1 use these
+	unsigned	offsets[MIPLEVELS];		// four mip maps stored
+} texture_t;
+
+
+#define	SURF_PLANEBACK		2
+#define	SURF_DRAWSKY		4
+#define SURF_DRAWSPRITE		8
+#define SURF_DRAWTURB		0x10
+#define SURF_DRAWTILED		0x20
+#define SURF_DRAWBACKGROUND	0x40
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+	unsigned short	v[2];
+	unsigned int	cachededgeoffset;
+} medge_t;
+
+typedef struct
+{
+	float		vecs[2][4];
+	float		mipadjust;
+	texture_t	*texture;
+	int			flags;
+} mtexinfo_t;
+
+typedef struct msurface_s
+{
+	int			visframe;		// should be drawn when node is crossed
+
+	int			dlightframe;
+	int			dlightbits;
+
+	mplane_t	*plane;
+	int			flags;
+
+	int			firstedge;	// look up in model->surfedges[], negative numbers
+	int			numedges;	// are backwards edges
+	
+// surface generation data
+	struct surfcache_s	*cachespots[MIPLEVELS];
+
+	short		texturemins[2];
+	short		extents[2];
+
+	mtexinfo_t	*texinfo;
+	
+// lighting info
+	byte		styles[MAXLIGHTMAPS];
+	byte		*samples;		// [numstyles*surfsize]
+} msurface_t;
+
+typedef struct mnode_s
+{
+// common with leaf
+	int			contents;		// 0, to differentiate from leafs
+	int			visframe;		// node needs to be traversed if current
+	
+	short		minmaxs[6];		// for bounding box culling
+
+	struct mnode_s	*parent;
+
+// node specific
+	mplane_t	*plane;
+	struct mnode_s	*children[2];	
+
+	unsigned short		firstsurface;
+	unsigned short		numsurfaces;
+} mnode_t;
+
+
+
+typedef struct mleaf_s
+{
+// common with node
+	int			contents;		// wil be a negative contents number
+	int			visframe;		// node needs to be traversed if current
+
+	short		minmaxs[6];		// for bounding box culling
+
+	struct mnode_s	*parent;
+
+// leaf specific
+	byte		*compressed_vis;
+	efrag_t		*efrags;
+
+	msurface_t	**firstmarksurface;
+	int			nummarksurfaces;
+	int			key;			// BSP sequence number for leaf's contents
+	byte		ambient_sound_level[NUM_AMBIENTS];
+} mleaf_t;
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+	dclipnode_t	*clipnodes;
+	mplane_t	*planes;
+	int			firstclipnode;
+	int			lastclipnode;
+	vec3_t		clip_mins;
+	vec3_t		clip_maxs;
+} hull_t;
+
+/*
+==============================================================================
+
+SPRITE MODELS
+
+==============================================================================
+*/
+
+
+// FIXME: shorten these?
+typedef struct mspriteframe_s
+{
+	int		width;
+	int		height;
+	void	*pcachespot;			// remove?
+	float	up, down, left, right;
+	byte	pixels[4];
+} mspriteframe_t;
+
+typedef struct
+{
+	int				numframes;
+	float			*intervals;
+	mspriteframe_t	*frames[1];
+} mspritegroup_t;
+
+typedef struct
+{
+	spriteframetype_t	type;
+	mspriteframe_t		*frameptr;
+} mspriteframedesc_t;
+
+typedef struct
+{
+	int					type;
+	int					maxwidth;
+	int					maxheight;
+	int					numframes;
+	float				beamlength;		// remove?
+	void				*cachespot;		// remove?
+	mspriteframedesc_t	frames[1];
+} msprite_t;
+
+
+/*
+==============================================================================
+
+ALIAS MODELS
+
+Alias models are position independent, so the cache manager can move them.
+==============================================================================
+*/
+
+typedef struct
+{
+	aliasframetype_t	type;
+	trivertx_t			bboxmin;
+	trivertx_t			bboxmax;
+	int					frame;
+	char				name[16];
+} maliasframedesc_t;
+
+typedef struct
+{
+	aliasskintype_t		type;
+	void				*pcachespot;
+	int					skin;
+} maliasskindesc_t;
+
+typedef struct
+{
+	trivertx_t			bboxmin;
+	trivertx_t			bboxmax;
+	int					frame;
+} maliasgroupframedesc_t;
+
+typedef struct
+{
+	int						numframes;
+	int						intervals;
+	maliasgroupframedesc_t	frames[1];
+} maliasgroup_t;
+
+typedef struct
+{
+	int					numskins;
+	int					intervals;
+	maliasskindesc_t	skindescs[1];
+} maliasskingroup_t;
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct mtriangle_s {
+	int					facesfront;
+	int					vertindex[3];
+} mtriangle_t;
+
+typedef struct {
+	int					model;
+	int					stverts;
+	int					skindesc;
+	int					triangles;
+	maliasframedesc_t	frames[1];
+} aliashdr_t;
+
+//===================================================================
+
+//
+// Whole model
+//
+
+typedef int modtype_t;
+enum {mod_brush, mod_sprite, mod_alias};
+
+#define	EF_ROCKET	1			// leave a trail
+#define	EF_GRENADE	2			// leave a trail
+#define	EF_GIB		4			// leave a trail
+#define	EF_ROTATE	8			// rotate (bonus items)
+#define	EF_TRACER	16			// green split trail
+#define	EF_ZOMGIB	32			// small blood trail
+#define	EF_TRACER2	64			// orange split trail + rotate
+#define	EF_TRACER3	128			// purple trail
+
+typedef struct model_s
+{
+	char		name[MAX_QPATH];
+	qboolean	needload;		// bmodels and sprites don't cache normally
+
+	modtype_t	type;
+	int			numframes;
+	synctype_t	synctype;
+	
+	int			flags;
+
+//
+// volume occupied by the model
+//		
+	vec3_t		mins, maxs;
+	float		radius;
+
+//
+// brush model
+//
+	int			firstmodelsurface, nummodelsurfaces;
+
+	int			numsubmodels;
+	dmodel_t	*submodels;
+
+	int			numplanes;
+	mplane_t	*planes;
+
+	int			numleafs;		// number of visible leafs, not counting 0
+	mleaf_t		*leafs;
+
+	int			numvertexes;
+	mvertex_t	*vertexes;
+
+	int			numedges;
+	medge_t		*edges;
+
+	int			numnodes;
+	mnode_t		*nodes;
+
+	int			numtexinfo;
+	mtexinfo_t	*texinfo;
+
+	int			numsurfaces;
+	msurface_t	*surfaces;
+
+	int			numsurfedges;
+	int			*surfedges;
+
+	int			numclipnodes;
+	dclipnode_t	*clipnodes;
+
+	int			nummarksurfaces;
+	msurface_t	**marksurfaces;
+
+	hull_t		hulls[MAX_MAP_HULLS];
+
+	int			numtextures;
+	texture_t	**textures;
+
+	byte		*visdata;
+	byte		*lightdata;
+	char		*entities;
+
+//
+// additional model data
+//
+	cache_user_t	cache;		// only access through Mod_Extradata
+
+} model_t;
+
+//============================================================================
+
+void	Mod_Init (void);
+void	Mod_ClearAll (void);
+model_t *Mod_ForName (char *name, qboolean crash);
+void	*Mod_Extradata (model_t *mod);	// handles caching
+void	Mod_TouchModel (char *name);
+
+mleaf_t *Mod_PointInLeaf (float *p, model_t *model);
+byte	*Mod_LeafPVS (mleaf_t *leaf, model_t *model);
+
+#endif	// __MODEL__
diff --git a/apps/plugins/sdl/progs/quake/modelgen.h b/apps/plugins/sdl/progs/quake/modelgen.h
new file mode 100644
index 0000000..631232f
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/modelgen.h
@@ -0,0 +1,137 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// modelgen.h: header file for model generation program
+//
+
+// *********************************************************
+// * This file must be identical in the modelgen directory *
+// * and in the Quake directory, because it's used to      *
+// * pass data from one to the other via model files.      *
+// *********************************************************
+
+#ifdef INCLUDELIBS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+#include "mathlib.h"
+
+#endif
+
+#define ALIAS_VERSION	6
+
+#define ALIAS_ONSEAM				0x0020
+
+// must match definition in spritegn.h
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef int synctype_t;
+enum {ST_SYNC=0, ST_RAND };
+#endif
+
+typedef int aliasframetype_t;
+enum { ALIAS_SINGLE=0, ALIAS_GROUP };
+
+typedef int aliasskintype_t;
+enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP };
+
+typedef struct {
+	int			ident;
+	int			version;
+	vec3_t		scale;
+	vec3_t		scale_origin;
+	float		boundingradius;
+	vec3_t		eyeposition;
+	int			numskins;
+	int			skinwidth;
+	int			skinheight;
+	int			numverts;
+	int			numtris;
+	int			numframes;
+	synctype_t	synctype;
+	int			flags;
+	float		size;
+} mdl_t;
+
+// TODO: could be shorts
+
+typedef struct {
+	int		onseam;
+	int		s;
+	int		t;
+} stvert_t;
+
+typedef struct dtriangle_s {
+	int					facesfront;
+	int					vertindex[3];
+} dtriangle_t;
+
+#define DT_FACES_FRONT				0x0010
+
+// This mirrors trivert_t in trilib.h, is present so Quake knows how to
+// load this data
+
+typedef struct {
+	byte	v[3];
+	byte	lightnormalindex;
+} trivertx_t;
+
+typedef struct {
+	trivertx_t	bboxmin;	// lightnormal isn't used
+	trivertx_t	bboxmax;	// lightnormal isn't used
+	char		name[16];	// frame name from grabbing
+} daliasframe_t;
+
+typedef struct {
+	int			numframes;
+	trivertx_t	bboxmin;	// lightnormal isn't used
+	trivertx_t	bboxmax;	// lightnormal isn't used
+} daliasgroup_t;
+
+typedef struct {
+	int			numskins;
+} daliasskingroup_t;
+
+typedef struct {
+	float	interval;
+} daliasinterval_t;
+
+typedef struct {
+	float	interval;
+} daliasskininterval_t;
+
+typedef struct {
+	aliasframetype_t	type;
+} daliasframetype_t;
+
+typedef struct {
+	aliasskintype_t	type;
+} daliasskintype_t;
+
+#define IDPOLYHEADER	(('O'<<24)+('P'<<16)+('D'<<8)+'I')
+														// little-endian "IDPO"
+
diff --git a/apps/plugins/sdl/progs/quake/mpdosock.h b/apps/plugins/sdl/progs/quake/mpdosock.h
new file mode 100644
index 0000000..75171f4
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/mpdosock.h
@@ -0,0 +1,797 @@
+/* WINSOCK.H--definitions to be used with the WINSOCK.DLL
+ * Copyright (c) 1993-1995, Microsoft Corp. All rights reserved.
+ *
+ * This header file corresponds to version 1.1 of the Windows Sockets specification.
+ *
+ * This file includes parts which are Copyright (c) 1982-1986 Regents
+ * of the University of California.  All rights reserved.  The
+ * Berkeley Software License Agreement specifies the terms and
+ * conditions for redistribution.
+ *
+ */
+
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_
+
+#define FAR
+#define PASCAL
+
+/*
+ * Basic system type definitions, taken from the BSD file sys/types.h.
+ */
+typedef unsigned char   u_char;
+typedef unsigned short  u_short;
+typedef unsigned int    u_int;
+typedef unsigned long   u_long;
+
+/*
+ * The new type to be used in all
+ * instances which refer to sockets.
+ */
+typedef u_int           SOCKET;
+
+// FIXME
+#if 0
+/*
+ * Select uses arrays of SOCKETs.  These macros manipulate such
+ * arrays.  FD_SETSIZE may be defined by the user before including
+ * this file, but the default here should be >= 64.
+ *
+ * CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE
+ * INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE.
+ */
+#ifndef FD_SETSIZE
+#define FD_SETSIZE      64
+#endif /* FD_SETSIZE */
+
+typedef struct fd_set {
+        u_int   fd_count;               /* how many are SET? */
+        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
+} fd_set;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#define FD_CLR(fd, set) do { \
+    u_int __i; \
+    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
+        if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \
+            while (__i < ((fd_set FAR *)(set))->fd_count-1) { \
+                ((fd_set FAR *)(set))->fd_array[__i] = \
+                    ((fd_set FAR *)(set))->fd_array[__i+1]; \
+                __i++; \
+            } \
+            ((fd_set FAR *)(set))->fd_count--; \
+            break; \
+        } \
+    } \
+} while(0)
+
+#define FD_SET(fd, set) do { \
+    if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) \
+        ((fd_set FAR *)(set))->fd_array[((fd_set FAR *)(set))->fd_count++]=(fd);\
+} while(0)
+
+#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)
+
+#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set))
+
+/*
+ * Structure used in select() call, taken from the BSD file sys/time.h.
+ */
+struct timeval {
+        long    tv_sec;         /* seconds */
+        long    tv_usec;        /* and microseconds */
+};
+
+/*
+ * Operations on timevals.
+ *
+ * NB: timercmp does not work for >= or <=.
+ */
+#define timerisset(tvp)         ((tvp)->tv_sec || (tvp)->tv_usec)
+#define timercmp(tvp, uvp, cmp) \
+        ((tvp)->tv_sec cmp (uvp)->tv_sec || \
+         (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
+#define timerclear(tvp)         (tvp)->tv_sec = (tvp)->tv_usec = 0
+#endif
+
+/*
+ * Commands for ioctlsocket(),  taken from the BSD file fcntl.h.
+ *
+ *
+ * Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word.  The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+#define IOCPARM_MASK    0x7f            /* parameters must be < 128 bytes */
+#define IOC_VOID        0x20000000      /* no parameters */
+#define IOC_OUT         0x40000000      /* copy out parameters */
+#define IOC_IN          0x80000000      /* copy in parameters */
+#define IOC_INOUT       (IOC_IN|IOC_OUT)
+                                        /* 0x20000000 distinguishes new &
+                                           old ioctl's */
+#define _IO(x,y)        (IOC_VOID|((x)<<8)|(y))
+
+#define _IOR(x,y,t)     (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+
+#define _IOW(x,y,t)     (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+
+#define FIONREAD    _IOR('f', 127, u_long) /* get # bytes to read */
+#define FIONBIO     _IOW('f', 126, u_long) /* set/clear non-blocking i/o */
+#define FIOASYNC    _IOW('f', 125, u_long) /* set/clear async i/o */
+
+/* Socket I/O Controls */
+#define SIOCSHIWAT  _IOW('s',  0, u_long)  /* set high watermark */
+#define SIOCGHIWAT  _IOR('s',  1, u_long)  /* get high watermark */
+#define SIOCSLOWAT  _IOW('s',  2, u_long)  /* set low watermark */
+#define SIOCGLOWAT  _IOR('s',  3, u_long)  /* get low watermark */
+#define SIOCATMARK  _IOR('s',  7, u_long)  /* at oob mark? */
+
+/*
+ * Structures returned by network data base library, taken from the
+ * BSD file netdb.h.  All addresses are supplied in host order, and
+ * returned in network order (suitable for use in system calls).
+ */
+
+struct  hostent {
+        char    FAR * h_name;           /* official name of host */
+        char    FAR * FAR * h_aliases;  /* alias list */
+        short   h_addrtype;             /* host address type */
+        short   h_length;               /* length of address */
+        char    FAR * FAR * h_addr_list; /* list of addresses */
+#define h_addr  h_addr_list[0]          /* address, for backward compat */
+};
+
+/*
+ * It is assumed here that a network number
+ * fits in 32 bits.
+ */
+struct  netent {
+        char    FAR * n_name;           /* official name of net */
+        char    FAR * FAR * n_aliases;  /* alias list */
+        short   n_addrtype;             /* net address type */
+        u_long  n_net;                  /* network # */
+};
+
+struct  servent {
+        char    FAR * s_name;           /* official service name */
+        char    FAR * FAR * s_aliases;  /* alias list */
+        short   s_port;                 /* port # */
+        char    FAR * s_proto;          /* protocol to use */
+};
+
+struct  protoent {
+        char    FAR * p_name;           /* official protocol name */
+        char    FAR * FAR * p_aliases;  /* alias list */
+        short   p_proto;                /* protocol # */
+};
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, taken from the BSD file netinet/in.h.
+ */
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP              0               /* dummy for IP */
+#define IPPROTO_ICMP            1               /* control message protocol */
+#define IPPROTO_GGP             2               /* gateway^2 (deprecated) */
+#define IPPROTO_TCP             6               /* tcp */
+#define IPPROTO_PUP             12              /* pup */
+#define IPPROTO_UDP             17              /* user datagram protocol */
+#define IPPROTO_IDP             22              /* xns idp */
+#define IPPROTO_ND              77              /* UNOFFICIAL net disk proto */
+
+#define IPPROTO_RAW             255             /* raw IP packet */
+#define IPPROTO_MAX             256
+
+/*
+ * Port/socket numbers: network standard functions
+ */
+#define IPPORT_ECHO             7
+#define IPPORT_DISCARD          9
+#define IPPORT_SYSTAT           11
+#define IPPORT_DAYTIME          13
+#define IPPORT_NETSTAT          15
+#define IPPORT_FTP              21
+#define IPPORT_TELNET           23
+#define IPPORT_SMTP             25
+#define IPPORT_TIMESERVER       37
+#define IPPORT_NAMESERVER       42
+#define IPPORT_WHOIS            43
+#define IPPORT_MTP              57
+
+/*
+ * Port/socket numbers: host specific functions
+ */
+#define IPPORT_TFTP             69
+#define IPPORT_RJE              77
+#define IPPORT_FINGER           79
+#define IPPORT_TTYLINK          87
+#define IPPORT_SUPDUP           95
+
+/*
+ * UNIX TCP sockets
+ */
+#define IPPORT_EXECSERVER       512
+#define IPPORT_LOGINSERVER      513
+#define IPPORT_CMDSERVER        514
+#define IPPORT_EFSSERVER        520
+
+/*
+ * UNIX UDP sockets
+ */
+#define IPPORT_BIFFUDP          512
+#define IPPORT_WHOSERVER        513
+#define IPPORT_ROUTESERVER      520
+                                        /* 520+1 also used */
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ */
+#define IPPORT_RESERVED         1024
+
+/*
+ * Link numbers
+ */
+#define IMPLINK_IP              155
+#define IMPLINK_LOWEXPER        156
+#define IMPLINK_HIGHEXPER       158
+
+/*
+ * Internet address (old style... should be updated)
+ */
+struct in_addr {
+        union {
+                struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+                struct { u_short s_w1,s_w2; } S_un_w;
+                u_long S_addr;
+        } S_un;
+#define s_addr  S_un.S_addr
+                                /* can be used for most tcp & ip code */
+#define s_host  S_un.S_un_b.s_b2
+                                /* host on imp */
+#define s_net   S_un.S_un_b.s_b1
+                                /* network */
+#define s_imp   S_un.S_un_w.s_w2
+                                /* imp */
+#define s_impno S_un.S_un_b.s_b4
+                                /* imp # */
+#define s_lh    S_un.S_un_b.s_b3
+                                /* logical host */
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i)            (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET           0xff000000
+#define IN_CLASSA_NSHIFT        24
+#define IN_CLASSA_HOST          0x00ffffff
+#define IN_CLASSA_MAX           128
+
+#define IN_CLASSB(i)            (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET           0xffff0000
+#define IN_CLASSB_NSHIFT        16
+#define IN_CLASSB_HOST          0x0000ffff
+#define IN_CLASSB_MAX           65536
+
+#define IN_CLASSC(i)            (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET           0xffffff00
+#define IN_CLASSC_NSHIFT        8
+#define IN_CLASSC_HOST          0x000000ff
+
+#define INADDR_ANY              (u_long)0x00000000
+#define INADDR_LOOPBACK         0x7f000001
+#define INADDR_BROADCAST        (u_long)0xffffffff
+#define INADDR_NONE             0xffffffff
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+        short   sin_family;
+        u_short sin_port;
+        struct  in_addr sin_addr;
+        char    sin_zero[8];
+};
+
+#define WSADESCRIPTION_LEN      256
+#define WSASYS_STATUS_LEN       128
+
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ */
+#define IP_OPTIONS          1           /* set/get IP per-packet options    */
+#define IP_MULTICAST_IF     2           /* set/get IP multicast interface   */
+#define IP_MULTICAST_TTL    3           /* set/get IP multicast timetolive  */
+#define IP_MULTICAST_LOOP   4           /* set/get IP multicast loopback    */
+#define IP_ADD_MEMBERSHIP   5           /* add  an IP group membership      */
+#define IP_DROP_MEMBERSHIP  6           /* drop an IP group membership      */
+
+#define IP_DEFAULT_MULTICAST_TTL   1    /* normally limit m'casts to 1 hop  */
+#define IP_DEFAULT_MULTICAST_LOOP  1    /* normally hear sends if a member  */
+#define IP_MAX_MEMBERSHIPS         20   /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+        struct in_addr  imr_multiaddr;  /* IP multicast address of group */
+        struct in_addr  imr_interface;  /* local IP address of interface */
+};
+
+/*
+ * Definitions related to sockets: types, address families, options,
+ * taken from the BSD file sys/socket.h.
+ */
+
+/*
+ * This is used instead of -1, since the
+ * SOCKET type is unsigned.
+ */
+#define INVALID_SOCKET  (SOCKET)(~0)
+#define SOCKET_ERROR            (-1)
+
+/*
+ * Types
+ */
+#define SOCK_STREAM     1               /* stream socket */
+#define SOCK_DGRAM      2               /* datagram socket */
+#define SOCK_RAW        3               /* raw-protocol interface */
+#define SOCK_RDM        4               /* reliably-delivered message */
+#define SOCK_SEQPACKET  5               /* sequenced packet stream */
+
+/*
+ * Option flags per-socket.
+ */
+#define SO_DEBUG        0x0001          /* turn on debugging info recording */
+#define SO_ACCEPTCONN   0x0002          /* socket has had listen() */
+#define SO_REUSEADDR    0x0004          /* allow local address reuse */
+#define SO_KEEPALIVE    0x0008          /* keep connections alive */
+#define SO_DONTROUTE    0x0010          /* just use interface addresses */
+#define SO_BROADCAST    0x0020          /* permit sending of broadcast msgs */
+#define SO_USELOOPBACK  0x0040          /* bypass hardware when possible */
+#define SO_LINGER       0x0080          /* linger on close if data present */
+#define SO_OOBINLINE    0x0100          /* leave received OOB data in line */
+
+#define SO_DONTLINGER   (u_int)(~SO_LINGER)
+
+/*
+ * Additional options.
+ */
+#define SO_SNDBUF       0x1001          /* send buffer size */
+#define SO_RCVBUF       0x1002          /* receive buffer size */
+#define SO_SNDLOWAT     0x1003          /* send low-water mark */
+#define SO_RCVLOWAT     0x1004          /* receive low-water mark */
+#define SO_SNDTIMEO     0x1005          /* send timeout */
+#define SO_RCVTIMEO     0x1006          /* receive timeout */
+#define SO_ERROR        0x1007          /* get error status and clear */
+#define SO_TYPE         0x1008          /* get socket type */
+
+/*
+ * Options for connect and disconnect data and options.  Used only by
+ * non-TCP/IP transports such as DECNet, OSI TP4, etc.
+ */
+#define SO_CONNDATA     0x7000
+#define SO_CONNOPT      0x7001
+#define SO_DISCDATA     0x7002
+#define SO_DISCOPT      0x7003
+#define SO_CONNDATALEN  0x7004
+#define SO_CONNOPTLEN   0x7005
+#define SO_DISCDATALEN  0x7006
+#define SO_DISCOPTLEN   0x7007
+
+/*
+ * Option for opening sockets for synchronous access.
+ */
+#define SO_OPENTYPE     0x7008
+
+#define SO_SYNCHRONOUS_ALERT    0x10
+#define SO_SYNCHRONOUS_NONALERT 0x20
+
+/*
+ * Other NT-specific options.
+ */
+#define SO_MAXDG        0x7009
+#define SO_MAXPATHDG    0x700A
+
+/*
+ * TCP options.
+ */
+#define TCP_NODELAY     0x0001
+#define TCP_BSDURGENT   0x7000
+
+/*
+ * Address families.
+ */
+#define AF_UNSPEC       0               /* unspecified */
+#define AF_UNIX         1               /* local to host (pipes, portals) */
+#define AF_INET         2               /* internetwork: UDP, TCP, etc. */
+#define AF_IMPLINK      3               /* arpanet imp addresses */
+#define AF_PUP          4               /* pup protocols: e.g. BSP */
+#define AF_CHAOS        5               /* mit CHAOS protocols */
+#define AF_IPX          6               /* IPX and SPX */
+#define AF_NS           6               /* XEROX NS protocols */
+#define AF_ISO          7               /* ISO protocols */
+#define AF_OSI          AF_ISO          /* OSI is ISO */
+#define AF_ECMA         8               /* european computer manufacturers */
+#define AF_DATAKIT      9               /* datakit protocols */
+#define AF_CCITT        10              /* CCITT protocols, X.25 etc */
+#define AF_SNA          11              /* IBM SNA */
+#define AF_DECnet       12              /* DECnet */
+#define AF_DLI          13              /* Direct data link interface */
+#define AF_LAT          14              /* LAT */
+#define AF_HYLINK       15              /* NSC Hyperchannel */
+#define AF_APPLETALK    16              /* AppleTalk */
+#define AF_NETBIOS      17              /* NetBios-style addresses */
+#define AF_VOICEVIEW    18              /* VoiceView */
+
+#define AF_MAX          19
+
+/*
+ * Structure used by kernel to store most
+ * addresses.
+ */
+struct sockaddr {
+        u_short sa_family;              /* address family */
+        char    sa_data[14];            /* up to 14 bytes of direct address */
+};
+
+/*
+ * Structure used by kernel to pass protocol
+ * information in raw sockets.
+ */
+struct sockproto {
+        u_short sp_family;              /* address family */
+        u_short sp_protocol;            /* protocol */
+};
+
+/*
+ * Protocol families, same as address families for now.
+ */
+#define PF_UNSPEC       AF_UNSPEC
+#define PF_UNIX         AF_UNIX
+#define PF_INET         AF_INET
+#define PF_IMPLINK      AF_IMPLINK
+#define PF_PUP          AF_PUP
+#define PF_CHAOS        AF_CHAOS
+#define PF_NS           AF_NS
+#define PF_IPX          AF_IPX
+#define PF_ISO          AF_ISO
+#define PF_OSI          AF_OSI
+#define PF_ECMA         AF_ECMA
+#define PF_DATAKIT      AF_DATAKIT
+#define PF_CCITT        AF_CCITT
+#define PF_SNA          AF_SNA
+#define PF_DECnet       AF_DECnet
+#define PF_DLI          AF_DLI
+#define PF_LAT          AF_LAT
+#define PF_HYLINK       AF_HYLINK
+#define PF_APPLETALK    AF_APPLETALK
+#define PF_VOICEVIEW    AF_VOICEVIEW
+
+#define PF_MAX          AF_MAX
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct  linger {
+        u_short l_onoff;                /* option on/off */
+        u_short l_linger;               /* linger time */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define SOL_SOCKET      0xffff          /* options for socket level */
+
+/*
+ * Maximum queue length specifiable by listen.
+ */
+#define SOMAXCONN       5
+
+#define MSG_OOB         0x1             /* process out-of-band data */
+#define MSG_PEEK        0x2             /* peek at incoming message */
+#define MSG_DONTROUTE   0x4             /* send without using routing tables */
+
+#define MSG_MAXIOVLEN   16
+
+#define MSG_PARTIAL     0x8000          /* partial send or recv for message xport */
+
+/*
+ * Define constant based on rfc883, used by gethostbyxxxx() calls.
+ */
+#define MAXGETHOSTSTRUCT        1024
+
+/*
+ * Define flags to be used with the WSAAsyncSelect() call.
+ */
+#define FD_READ         0x01
+#define FD_WRITE        0x02
+#define FD_OOB          0x04
+#define FD_ACCEPT       0x08
+#define FD_CONNECT      0x10
+#define FD_CLOSE        0x20
+
+/*
+ * All Windows Sockets error constants are biased by WSABASEERR from
+ * the "normal"
+ */
+#define WSABASEERR              10000
+/*
+ * Windows Sockets definitions of regular Microsoft C error constants
+ */
+#define WSAEINTR                (WSABASEERR+4)
+#define WSAEBADF                (WSABASEERR+9)
+#define WSAEACCES               (WSABASEERR+13)
+#define WSAEFAULT               (WSABASEERR+14)
+#define WSAEINVAL               (WSABASEERR+22)
+#define WSAEMFILE               (WSABASEERR+24)
+
+/*
+ * Windows Sockets definitions of regular Berkeley error constants
+ */
+#define WSAEWOULDBLOCK          (WSABASEERR+35)
+#define WSAEINPROGRESS          (WSABASEERR+36)
+#define WSAEALREADY             (WSABASEERR+37)
+#define WSAENOTSOCK             (WSABASEERR+38)
+#define WSAEDESTADDRREQ         (WSABASEERR+39)
+#define WSAEMSGSIZE             (WSABASEERR+40)
+#define WSAEPROTOTYPE           (WSABASEERR+41)
+#define WSAENOPROTOOPT          (WSABASEERR+42)
+#define WSAEPROTONOSUPPORT      (WSABASEERR+43)
+#define WSAESOCKTNOSUPPORT      (WSABASEERR+44)
+#define WSAEOPNOTSUPP           (WSABASEERR+45)
+#define WSAEPFNOSUPPORT         (WSABASEERR+46)
+#define WSAEAFNOSUPPORT         (WSABASEERR+47)
+#define WSAEADDRINUSE           (WSABASEERR+48)
+#define WSAEADDRNOTAVAIL        (WSABASEERR+49)
+#define WSAENETDOWN             (WSABASEERR+50)
+#define WSAENETUNREACH          (WSABASEERR+51)
+#define WSAENETRESET            (WSABASEERR+52)
+#define WSAECONNABORTED         (WSABASEERR+53)
+#define WSAECONNRESET           (WSABASEERR+54)
+#define WSAENOBUFS              (WSABASEERR+55)
+#define WSAEISCONN              (WSABASEERR+56)
+#define WSAENOTCONN             (WSABASEERR+57)
+#define WSAESHUTDOWN            (WSABASEERR+58)
+#define WSAETOOMANYREFS         (WSABASEERR+59)
+#define WSAETIMEDOUT            (WSABASEERR+60)
+#define WSAECONNREFUSED         (WSABASEERR+61)
+#define WSAELOOP                (WSABASEERR+62)
+#define WSAENAMETOOLONG         (WSABASEERR+63)
+#define WSAEHOSTDOWN            (WSABASEERR+64)
+#define WSAEHOSTUNREACH         (WSABASEERR+65)
+#define WSAENOTEMPTY            (WSABASEERR+66)
+#define WSAEPROCLIM             (WSABASEERR+67)
+#define WSAEUSERS               (WSABASEERR+68)
+#define WSAEDQUOT               (WSABASEERR+69)
+#define WSAESTALE               (WSABASEERR+70)
+#define WSAEREMOTE              (WSABASEERR+71)
+
+#define WSAEDISCON              (WSABASEERR+101)
+
+/*
+ * Extended Windows Sockets error constant definitions
+ */
+#define WSASYSNOTREADY          (WSABASEERR+91)
+#define WSAVERNOTSUPPORTED      (WSABASEERR+92)
+#define WSANOTINITIALISED       (WSABASEERR+93)
+
+/*
+ * Error return codes from gethostbyname() and gethostbyaddr()
+ * (when using the resolver). Note that these errors are
+ * retrieved via WSAGetLastError() and must therefore follow
+ * the rules for avoiding clashes with error numbers from
+ * specific implementations or language run-time systems.
+ * For this reason the codes are based at WSABASEERR+1001.
+ * Note also that [WSA]NO_ADDRESS is defined only for
+ * compatibility purposes.
+ */
+
+#define h_errno         WSAGetLastError()
+
+/* Authoritative Answer: Host not found */
+#define WSAHOST_NOT_FOUND       (WSABASEERR+1001)
+#define HOST_NOT_FOUND          WSAHOST_NOT_FOUND
+
+/* Non-Authoritative: Host not found, or SERVERFAIL */
+#define WSATRY_AGAIN            (WSABASEERR+1002)
+#define TRY_AGAIN               WSATRY_AGAIN
+
+/* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+#define WSANO_RECOVERY          (WSABASEERR+1003)
+#define NO_RECOVERY             WSANO_RECOVERY
+
+/* Valid name, no data record of requested type */
+#define WSANO_DATA              (WSABASEERR+1004)
+#define NO_DATA                 WSANO_DATA
+
+/* no address, look for MX record */
+#define WSANO_ADDRESS           WSANO_DATA
+#define NO_ADDRESS              WSANO_ADDRESS
+
+/*
+ * Windows Sockets errors redefined as regular Berkeley error constants.
+ * These are commented out in Windows NT to avoid conflicts with errno.h.
+ * Use the WSA constants instead.
+ */
+#if 0
+#define EWOULDBLOCK             WSAEWOULDBLOCK
+#define EINPROGRESS             WSAEINPROGRESS
+#define EALREADY                WSAEALREADY
+#define ENOTSOCK                WSAENOTSOCK
+#define EDESTADDRREQ            WSAEDESTADDRREQ
+#define EMSGSIZE                WSAEMSGSIZE
+#define EPROTOTYPE              WSAEPROTOTYPE
+#define ENOPROTOOPT             WSAENOPROTOOPT
+#define EPROTONOSUPPORT         WSAEPROTONOSUPPORT
+#define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT
+#define EOPNOTSUPP              WSAEOPNOTSUPP
+#define EPFNOSUPPORT            WSAEPFNOSUPPORT
+#define EAFNOSUPPORT            WSAEAFNOSUPPORT
+#define EADDRINUSE              WSAEADDRINUSE
+#define EADDRNOTAVAIL           WSAEADDRNOTAVAIL
+#define ENETDOWN                WSAENETDOWN
+#define ENETUNREACH             WSAENETUNREACH
+#define ENETRESET               WSAENETRESET
+#define ECONNABORTED            WSAECONNABORTED
+#define ECONNRESET              WSAECONNRESET
+#define ENOBUFS                 WSAENOBUFS
+#define EISCONN                 WSAEISCONN
+#define ENOTCONN                WSAENOTCONN
+#define ESHUTDOWN               WSAESHUTDOWN
+#define ETOOMANYREFS            WSAETOOMANYREFS
+#define ETIMEDOUT               WSAETIMEDOUT
+#define ECONNREFUSED            WSAECONNREFUSED
+#define ELOOP                   WSAELOOP
+#define ENAMETOOLONG            WSAENAMETOOLONG
+#define EHOSTDOWN               WSAEHOSTDOWN
+#define EHOSTUNREACH            WSAEHOSTUNREACH
+#define ENOTEMPTY               WSAENOTEMPTY
+#define EPROCLIM                WSAEPROCLIM
+#define EUSERS                  WSAEUSERS
+#define EDQUOT                  WSAEDQUOT
+#define ESTALE                  WSAESTALE
+#define EREMOTE                 WSAEREMOTE
+#endif
+
+/* Socket function prototypes */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr,
+                          int FAR *addrlen);
+
+int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen);
+
+int PASCAL FAR closesocket (SOCKET s);
+
+int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen);
+
+int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp);
+
+int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name,
+                            int FAR * namelen);
+
+int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name,
+                            int FAR * namelen);
+
+int PASCAL FAR getsockopt (SOCKET s, int level, int optname,
+                           char FAR * optval, int FAR *optlen);
+
+u_long PASCAL FAR htonl (u_long hostlong);
+
+u_short PASCAL FAR htons (u_short hostshort);
+
+unsigned long PASCAL FAR inet_addr (const char FAR * cp);
+
+char FAR * PASCAL FAR inet_ntoa (struct in_addr in);
+
+int PASCAL FAR listen (SOCKET s, int backlog);
+
+u_long PASCAL FAR ntohl (u_long netlong);
+
+u_short PASCAL FAR ntohs (u_short netshort);
+
+int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags);
+
+int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags,
+                         struct sockaddr FAR *from, int FAR * fromlen);
+
+#if 0
+int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
+                       fd_set FAR *exceptfds, const struct timeval FAR *timeout);
+#endif
+
+int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags);
+
+int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags,
+                       const struct sockaddr FAR *to, int tolen);
+
+int PASCAL FAR setsockopt (SOCKET s, int level, int optname,
+                           const char FAR * optval, int optlen);
+
+int PASCAL FAR shutdown (SOCKET s, int how);
+
+SOCKET PASCAL FAR socket (int af, int type, int protocol);
+
+/* Database function prototypes */
+
+struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr,
+                                              int len, int type);
+
+struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);
+
+int PASCAL FAR gethostname (char FAR * name, int namelen);
+
+struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto);
+
+struct servent FAR * PASCAL FAR getservbyname(const char FAR * name,
+                                              const char FAR * proto);
+
+struct protoent FAR * PASCAL FAR getprotobynumber(int proto);
+
+struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Microsoft Windows Extended data types */
+typedef struct sockaddr SOCKADDR;
+typedef struct sockaddr *PSOCKADDR;
+typedef struct sockaddr FAR *LPSOCKADDR;
+
+typedef struct sockaddr_in SOCKADDR_IN;
+typedef struct sockaddr_in *PSOCKADDR_IN;
+typedef struct sockaddr_in FAR *LPSOCKADDR_IN;
+
+typedef struct linger LINGER;
+typedef struct linger *PLINGER;
+typedef struct linger FAR *LPLINGER;
+
+typedef struct in_addr IN_ADDR;
+typedef struct in_addr *PIN_ADDR;
+typedef struct in_addr FAR *LPIN_ADDR;
+
+typedef struct fd_set FD_SET;
+typedef struct fd_set *PFD_SET;
+typedef struct fd_set FAR *LPFD_SET;
+
+typedef struct hostent HOSTENT;
+typedef struct hostent *PHOSTENT;
+typedef struct hostent FAR *LPHOSTENT;
+
+typedef struct servent SERVENT;
+typedef struct servent *PSERVENT;
+typedef struct servent FAR *LPSERVENT;
+
+typedef struct protoent PROTOENT;
+typedef struct protoent *PPROTOENT;
+typedef struct protoent FAR *LPPROTOENT;
+
+typedef struct timeval TIMEVAL;
+typedef struct timeval *PTIMEVAL;
+typedef struct timeval FAR *LPTIMEVAL;
+
+#endif  /* _WINSOCKAPI_ */
diff --git a/apps/plugins/sdl/progs/quake/net.h b/apps/plugins/sdl/progs/quake/net.h
new file mode 100644
index 0000000..af46125
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net.h
@@ -0,0 +1,337 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net.h -- quake's interface to the networking layer
+
+struct qsockaddr
+{
+	short sa_family;
+	unsigned char sa_data[14];
+};
+
+
+#define	NET_NAMELEN			64
+
+#define NET_MAXMESSAGE		8192
+#define NET_HEADERSIZE		(2 * sizeof(unsigned int))
+#define NET_DATAGRAMSIZE	(MAX_DATAGRAM + NET_HEADERSIZE)
+
+// NetHeader flags
+#define NETFLAG_LENGTH_MASK	0x0000ffff
+#define NETFLAG_DATA		0x00010000
+#define NETFLAG_ACK			0x00020000
+#define NETFLAG_NAK			0x00040000
+#define NETFLAG_EOM			0x00080000
+#define NETFLAG_UNRELIABLE	0x00100000
+#define NETFLAG_CTL			0x80000000
+
+
+#define NET_PROTOCOL_VERSION	3
+
+// This is the network info/connection protocol.  It is used to find Quake
+// servers, get info about them, and connect to them.  Once connected, the
+// Quake game protocol (documented elsewhere) is used.
+//
+//
+// General notes:
+//	game_name is currently always "QUAKE", but is there so this same protocol
+//		can be used for future games as well; can you say Quake2?
+//
+// CCREQ_CONNECT
+//		string	game_name				"QUAKE"
+//		byte	net_protocol_version	NET_PROTOCOL_VERSION
+//
+// CCREQ_SERVER_INFO
+//		string	game_name				"QUAKE"
+//		byte	net_protocol_version	NET_PROTOCOL_VERSION
+//
+// CCREQ_PLAYER_INFO
+//		byte	player_number
+//
+// CCREQ_RULE_INFO
+//		string	rule
+//
+//
+//
+// CCREP_ACCEPT
+//		long	port
+//
+// CCREP_REJECT
+//		string	reason
+//
+// CCREP_SERVER_INFO
+//		string	server_address
+//		string	host_name
+//		string	level_name
+//		byte	current_players
+//		byte	max_players
+//		byte	protocol_version	NET_PROTOCOL_VERSION
+//
+// CCREP_PLAYER_INFO
+//		byte	player_number
+//		string	name
+//		long	colors
+//		long	frags
+//		long	connect_time
+//		string	address
+//
+// CCREP_RULE_INFO
+//		string	rule
+//		string	value
+
+//	note:
+//		There are two address forms used above.  The short form is just a
+//		port number.  The address that goes along with the port is defined as
+//		"whatever address you receive this reponse from".  This lets us use
+//		the host OS to solve the problem of multiple host addresses (possibly
+//		with no routing between them); the host will use the right address
+//		when we reply to the inbound connection request.  The long from is
+//		a full address and port in a string.  It is used for returning the
+//		address of a server that is not running locally.
+
+#define CCREQ_CONNECT		0x01
+#define CCREQ_SERVER_INFO	0x02
+#define CCREQ_PLAYER_INFO	0x03
+#define CCREQ_RULE_INFO		0x04
+
+#define CCREP_ACCEPT		0x81
+#define CCREP_REJECT		0x82
+#define CCREP_SERVER_INFO	0x83
+#define CCREP_PLAYER_INFO	0x84
+#define CCREP_RULE_INFO		0x85
+
+typedef struct qsocket_s
+{
+	struct qsocket_s	*next;
+	double			connecttime;
+	double			lastMessageTime;
+	double			lastSendTime;
+
+	qboolean		disconnected;
+	qboolean		canSend;
+	qboolean		sendNext;
+	
+	int				driver;
+	int				landriver;
+	int				socket;
+	void			*driverdata;
+
+	unsigned int	ackSequence;
+	unsigned int	sendSequence;
+	unsigned int	unreliableSendSequence;
+	int				sendMessageLength;
+	byte			sendMessage [NET_MAXMESSAGE];
+
+	unsigned int	receiveSequence;
+	unsigned int	unreliableReceiveSequence;
+	int				receiveMessageLength;
+	byte			receiveMessage [NET_MAXMESSAGE];
+
+	struct qsockaddr	addr;
+	char				address[NET_NAMELEN];
+
+} qsocket_t;
+
+extern qsocket_t	*net_activeSockets;
+extern qsocket_t	*net_freeSockets;
+extern int			net_numsockets;
+
+typedef struct
+{
+	char		*name;
+	qboolean	initialized;
+	int			controlSock;
+	int			(*Init) (void);
+	void		(*Shutdown) (void);
+	void		(*Listen) (qboolean state);
+	int 		(*OpenSocket) (int port);
+	int 		(*CloseSocket) (int socket);
+	int 		(*Connect) (int socket, struct qsockaddr *addr);
+	int 		(*CheckNewConnections) (void);
+	int 		(*Read) (int socket, byte *buf, int len, struct qsockaddr *addr);
+	int 		(*Write) (int socket, byte *buf, int len, struct qsockaddr *addr);
+	int 		(*Broadcast) (int socket, byte *buf, int len);
+	char *		(*AddrToString) (struct qsockaddr *addr);
+	int 		(*StringToAddr) (char *string, struct qsockaddr *addr);
+	int 		(*GetSocketAddr) (int socket, struct qsockaddr *addr);
+	int 		(*GetNameFromAddr) (struct qsockaddr *addr, char *name);
+	int 		(*GetAddrFromName) (char *name, struct qsockaddr *addr);
+	int			(*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2);
+	int			(*GetSocketPort) (struct qsockaddr *addr);
+	int			(*SetSocketPort) (struct qsockaddr *addr, int port);
+} net_landriver_t;
+
+#define	MAX_NET_DRIVERS		8
+extern int 				net_numlandrivers;
+extern net_landriver_t	net_landrivers[MAX_NET_DRIVERS];
+
+typedef struct
+{
+	char		*name;
+	qboolean	initialized;
+	int			(*Init) (void);
+	void		(*Listen) (qboolean state);
+	void		(*SearchForHosts) (qboolean xmit);
+	qsocket_t	*(*Connect) (char *host);
+	qsocket_t 	*(*CheckNewConnections) (void);
+	int			(*QGetMessage) (qsocket_t *sock);
+	int			(*QSendMessage) (qsocket_t *sock, sizebuf_t *data);
+	int			(*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data);
+	qboolean	(*CanSendMessage) (qsocket_t *sock);
+	qboolean	(*CanSendUnreliableMessage) (qsocket_t *sock);
+	void		(*Close) (qsocket_t *sock);
+	void		(*Shutdown) (void);
+	int			controlSock;
+} net_driver_t;
+
+extern int			net_numdrivers;
+extern net_driver_t	net_drivers[MAX_NET_DRIVERS];
+
+extern int			DEFAULTnet_hostport;
+extern int			net_hostport;
+
+extern int net_driverlevel;
+extern cvar_t		hostname;
+extern char			playername[];
+extern int			playercolor;
+
+extern int		messagesSent;
+extern int		messagesReceived;
+extern int		unreliableMessagesSent;
+extern int		unreliableMessagesReceived;
+
+qsocket_t *NET_NewQSocket (void);
+void NET_FreeQSocket(qsocket_t *);
+double SetNetTime(void);
+
+
+#define HOSTCACHESIZE	8
+
+typedef struct
+{
+	char	name[16];
+	char	map[16];
+	char	cname[32];
+	int		users;
+	int		maxusers;
+	int		driver;
+	int		ldriver;
+	struct qsockaddr addr;
+} hostcache_t;
+
+extern int hostCacheCount;
+extern hostcache_t hostcache[HOSTCACHESIZE];
+
+#if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__)
+#ifndef htonl
+extern unsigned long htonl (unsigned long hostlong);
+#endif
+#ifndef htons
+extern unsigned short htons (unsigned short hostshort);
+#endif
+#ifndef ntohl
+extern unsigned long ntohl (unsigned long netlong);
+#endif
+#ifndef ntohs
+extern unsigned short ntohs (unsigned short netshort);
+#endif
+#endif
+
+#ifdef IDGODS
+qboolean IsID(struct qsockaddr *addr);
+#endif
+
+//============================================================================
+//
+// public network functions
+//
+//============================================================================
+
+extern	double		net_time;
+extern	sizebuf_t	net_message;
+extern	int			net_activeconnections;
+
+void		NET_Init (void);
+void		NET_Shutdown (void);
+
+struct qsocket_s	*NET_CheckNewConnections (void);
+// returns a new connection number if there is one pending, else -1
+
+struct qsocket_s	*NET_Connect (char *host);
+// called by client to connect to a host.  Returns -1 if not able to
+
+qboolean NET_CanSendMessage (qsocket_t *sock);
+// Returns true or false if the given qsocket can currently accept a
+// message to be transmitted.
+
+int			NET_GetMessage (struct qsocket_s *sock);
+// returns data in net_message sizebuf
+// returns 0 if no data is waiting
+// returns 1 if a message was received
+// returns 2 if an unreliable message was received
+// returns -1 if the connection died
+
+int			NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data);
+int			NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data);
+// returns 0 if the message connot be delivered reliably, but the connection
+//		is still considered valid
+// returns 1 if the message was sent properly
+// returns -1 if the connection died
+
+int			NET_SendToAll(sizebuf_t *data, int blocktime);
+// This is a reliable *blocking* send to all attached clients.
+
+
+void		NET_Close (struct qsocket_s *sock);
+// if a dead connection is returned by a get or send function, this function
+// should be called when it is convenient
+
+// Server calls when a client is kicked off for a game related misbehavior
+// like an illegal protocal conversation.  Client calls when disconnecting
+// from a server.
+// A netcon_t number will not be reused until this function is called for it
+
+void NET_Poll(void);
+
+
+typedef struct _PollProcedure
+{
+	struct _PollProcedure	*next;
+	double					nextTime;
+	void					(*procedure)();
+	void					*arg;
+} PollProcedure;
+
+void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
+
+extern	qboolean	serialAvailable;
+extern	qboolean	ipxAvailable;
+extern	qboolean	tcpipAvailable;
+extern	char		my_ipx_address[NET_NAMELEN];
+extern	char		my_tcpip_address[NET_NAMELEN];
+extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+extern	qboolean	slistInProgress;
+extern	qboolean	slistSilent;
+extern	qboolean	slistLocal;
+
+void NET_Slist_f (void);
diff --git a/apps/plugins/sdl/progs/quake/net_bsd.c b/apps/plugins/sdl/progs/quake/net_bsd.c
new file mode 100644
index 0000000..79d62f8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_bsd.c
@@ -0,0 +1,93 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+#include "net_loop.h"
+#include "net_dgrm.h"
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+	{
+	"Loopback",
+	false,
+	Loop_Init,
+	Loop_Listen,
+	Loop_SearchForHosts,
+	Loop_Connect,
+	Loop_CheckNewConnections,
+	Loop_GetMessage,
+	Loop_SendMessage,
+	Loop_SendUnreliableMessage,
+	Loop_CanSendMessage,
+	Loop_CanSendUnreliableMessage,
+	Loop_Close,
+	Loop_Shutdown
+	}
+	,
+	{
+	"Datagram",
+	false,
+	Datagram_Init,
+	Datagram_Listen,
+	Datagram_SearchForHosts,
+	Datagram_Connect,
+	Datagram_CheckNewConnections,
+	Datagram_GetMessage,
+	Datagram_SendMessage,
+	Datagram_SendUnreliableMessage,
+	Datagram_CanSendMessage,
+	Datagram_CanSendUnreliableMessage,
+	Datagram_Close,
+	Datagram_Shutdown
+	}
+};
+
+int net_numdrivers = 2;
+
+#include "net_udp.h"
+
+net_landriver_t	net_landrivers[MAX_NET_DRIVERS] =
+{
+	{
+	"UDP",
+	false,
+	0,
+	UDP_Init,
+	UDP_Shutdown,
+	UDP_Listen,
+	UDP_OpenSocket,
+	UDP_CloseSocket,
+	UDP_Connect,
+	UDP_CheckNewConnections,
+	UDP_Read,
+	UDP_Write,
+	UDP_Broadcast,
+	UDP_AddrToString,
+	UDP_StringToAddr,
+	UDP_GetSocketAddr,
+	UDP_GetNameFromAddr,
+	UDP_GetAddrFromName,
+	UDP_AddrCompare,
+	UDP_GetSocketPort,
+	UDP_SetSocketPort
+	}
+};
+
+int net_numlandrivers = 1;
diff --git a/apps/plugins/sdl/progs/quake/net_bw.h b/apps/plugins/sdl/progs/quake/net_bw.h
new file mode 100644
index 0000000..341c49a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_bw.h
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_bw.h
+
+int  BW_Init (void);
+void BW_Shutdown (void);
+void BW_Listen (qboolean state);
+int  BW_OpenSocket (int port);
+int  BW_CloseSocket (int socket);
+int  BW_Connect (int socket, struct qsockaddr *addr);
+int  BW_CheckNewConnections (void);
+int  BW_Read (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  BW_Write (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  BW_Broadcast (int socket, byte *buf, int len);
+char *BW_AddrToString (struct qsockaddr *addr);
+int  BW_StringToAddr (char *string, struct qsockaddr *addr);
+int  BW_GetSocketAddr (int socket, struct qsockaddr *addr);
+int  BW_GetNameFromAddr (struct qsockaddr *addr, char *name);
+int  BW_GetAddrFromName (char *name, struct qsockaddr *addr);
+int  BW_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+int  BW_GetSocketPort (struct qsockaddr *addr);
+int  BW_SetSocketPort (struct qsockaddr *addr, int port);
diff --git a/apps/plugins/sdl/progs/quake/net_dgrm.c b/apps/plugins/sdl/progs/quake/net_dgrm.c
new file mode 100644
index 0000000..a293b77
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_dgrm.c
@@ -0,0 +1,1390 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_dgrm.c
+
+// This is enables a simple IP banning mechanism
+#define BAN_TEST
+
+#ifdef BAN_TEST
+#if defined(_WIN32)
+#include <windows.h>
+#elif defined (NeXT)
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#else
+#define AF_INET 		2	/* internet */
+struct in_addr
+{
+	union
+	{
+		struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+		struct { unsigned short s_w1,s_w2; } S_un_w;
+		unsigned long S_addr;
+	} S_un;
+};
+#define	s_addr	S_un.S_addr	/* can be used for most tcp & ip code */
+struct sockaddr_in
+{
+    short			sin_family;
+    unsigned short	sin_port;
+	struct in_addr	sin_addr;
+    char			sin_zero[8];
+};
+char *inet_ntoa(struct in_addr in);
+unsigned long inet_addr(const char *cp);
+#endif
+#endif	// BAN_TEST
+
+#include "quakedef.h"
+#include "net_dgrm.h"
+
+// these two macros are to make the code more readable
+#define sfunc	net_landrivers[sock->landriver]
+#define dfunc	net_landrivers[net_landriverlevel]
+
+static int net_landriverlevel;
+
+/* statistic counters */
+int	packetsSent = 0;
+int	packetsReSent = 0;
+int packetsReceived = 0;
+int receivedDuplicateCount = 0;
+int shortPacketCount = 0;
+int droppedDatagrams;
+
+static int myDriverLevel;
+
+struct
+{
+	unsigned int	length;
+	unsigned int	sequence;
+	byte			data[MAX_DATAGRAM];
+} packetBuffer;
+
+extern int m_return_state;
+extern int m_state;
+extern qboolean m_return_onerror;
+extern char m_return_reason[32];
+
+
+#ifdef DEBUG
+char *StrAddr (struct qsockaddr *addr)
+{
+	static char buf[34];
+	byte *p = (byte *)addr;
+	int n;
+
+	for (n = 0; n < 16; n++)
+		sprintf (buf + n * 2, "%02x", *p++);
+	return buf;
+}
+#endif
+
+
+#ifdef BAN_TEST
+unsigned long banAddr = 0x00000000;
+unsigned long banMask = 0xffffffff;
+
+void NET_Ban_f (void)
+{
+	char	addrStr [32];
+	char	maskStr [32];
+	void	(*print) (char *fmt, ...);
+
+	if (cmd_source == src_command)
+	{
+		if (!sv.active)
+		{
+			Cmd_ForwardToServer ();
+			return;
+		}
+		print = Con_Printf;
+	}
+	else
+	{
+		if (pr_global_struct->deathmatch && !host_client->privileged)
+			return;
+		print = SV_ClientPrintf;
+	}
+
+	switch (Cmd_Argc ())
+	{
+		case 1:
+			if (((struct in_addr *)&banAddr)->s_addr)
+			{
+				Q_strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr));
+				Q_strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask));
+				print("Banning %s [%s]\n", addrStr, maskStr);
+			}
+			else
+				print("Banning not active\n");
+			break;
+
+		case 2:
+			if (Q_strcasecmp(Cmd_Argv(1), "off") == 0)
+				banAddr = 0x00000000;
+			else
+				banAddr = inet_addr(Cmd_Argv(1));
+			banMask = 0xffffffff;
+			break;
+
+		case 3:
+			banAddr = inet_addr(Cmd_Argv(1));
+			banMask = inet_addr(Cmd_Argv(2));
+			break;
+
+		default:
+			print("BAN ip_address [mask]\n");
+			break;
+	}
+}
+#endif
+
+
+int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	unsigned int	packetLen;
+	unsigned int	dataLen;
+	unsigned int	eom;
+
+#ifdef DEBUG
+	if (data->cursize == 0)
+		Sys_Error("Datagram_SendMessage: zero length message\n");
+
+	if (data->cursize > NET_MAXMESSAGE)
+		Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize);
+
+	if (sock->canSend == false)
+		Sys_Error("SendMessage: called with canSend == false\n");
+#endif
+
+	Q_memcpy(sock->sendMessage, data->data, data->cursize);
+	sock->sendMessageLength = data->cursize;
+
+	if (data->cursize <= MAX_DATAGRAM)
+	{
+		dataLen = data->cursize;
+		eom = NETFLAG_EOM;
+	}
+	else
+	{
+		dataLen = MAX_DATAGRAM;
+		eom = 0;
+	}
+	packetLen = NET_HEADERSIZE + dataLen;
+
+	packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+	packetBuffer.sequence = BigLong(sock->sendSequence++);
+	Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+	sock->canSend = false;
+
+	if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+		return -1;
+
+	sock->lastSendTime = net_time;
+	packetsSent++;
+	return 1;
+}
+
+
+int SendMessageNext (qsocket_t *sock)
+{
+	unsigned int	packetLen;
+	unsigned int	dataLen;
+	unsigned int	eom;
+
+	if (sock->sendMessageLength <= MAX_DATAGRAM)
+	{
+		dataLen = sock->sendMessageLength;
+		eom = NETFLAG_EOM;
+	}
+	else
+	{
+		dataLen = MAX_DATAGRAM;
+		eom = 0;
+	}
+	packetLen = NET_HEADERSIZE + dataLen;
+
+	packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+	packetBuffer.sequence = BigLong(sock->sendSequence++);
+	Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+	sock->sendNext = false;
+
+	if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+		return -1;
+
+	sock->lastSendTime = net_time;
+	packetsSent++;
+	return 1;
+}
+
+
+int ReSendMessage (qsocket_t *sock)
+{
+	unsigned int	packetLen;
+	unsigned int	dataLen;
+	unsigned int	eom;
+
+	if (sock->sendMessageLength <= MAX_DATAGRAM)
+	{
+		dataLen = sock->sendMessageLength;
+		eom = NETFLAG_EOM;
+	}
+	else
+	{
+		dataLen = MAX_DATAGRAM;
+		eom = 0;
+	}
+	packetLen = NET_HEADERSIZE + dataLen;
+
+	packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom));
+	packetBuffer.sequence = BigLong(sock->sendSequence - 1);
+	Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen);
+
+	sock->sendNext = false;
+
+	if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+		return -1;
+
+	sock->lastSendTime = net_time;
+	packetsReSent++;
+	return 1;
+}
+
+
+qboolean Datagram_CanSendMessage (qsocket_t *sock)
+{
+	if (sock->sendNext)
+		SendMessageNext (sock);
+
+	return sock->canSend;
+}
+
+
+qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock)
+{
+	return true;
+}
+
+
+int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	int 	packetLen;
+
+#ifdef DEBUG
+	if (data->cursize == 0)
+		Sys_Error("Datagram_SendUnreliableMessage: zero length message\n");
+
+	if (data->cursize > MAX_DATAGRAM)
+		Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize);
+#endif
+
+	packetLen = NET_HEADERSIZE + data->cursize;
+
+	packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE);
+	packetBuffer.sequence = BigLong(sock->unreliableSendSequence++);
+	Q_memcpy (packetBuffer.data, data->data, data->cursize);
+
+	if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1)
+		return -1;
+
+	packetsSent++;
+	return 1;
+}
+
+
+int	Datagram_GetMessage (qsocket_t *sock)
+{
+	unsigned int	length;
+	unsigned int	flags;
+	int				ret = 0;
+	struct qsockaddr readaddr;
+	unsigned int	sequence;
+	unsigned int	count;
+
+	if (!sock->canSend)
+		if ((net_time - sock->lastSendTime) > 1.0)
+			ReSendMessage (sock);
+
+	while(1)
+	{	
+		length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr);
+
+//	if ((rand() & 255) > 220)
+//		continue;
+
+		if (length == 0)
+			break;
+
+		if (length == -1)
+		{
+			Con_Printf("Read error\n");
+			return -1;
+		}
+
+		if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0)
+		{
+#ifdef DEBUG
+			Con_DPrintf("Forged packet received\n");
+			Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr));
+			Con_DPrintf("Received: %s\n", StrAddr (&readaddr));
+#endif
+			continue;
+		}
+
+		if (length < NET_HEADERSIZE)
+		{
+			shortPacketCount++;
+			continue;
+		}
+
+		length = BigLong(packetBuffer.length);
+		flags = length & (~NETFLAG_LENGTH_MASK);
+		length &= NETFLAG_LENGTH_MASK;
+
+		if (flags & NETFLAG_CTL)
+			continue;
+
+		sequence = BigLong(packetBuffer.sequence);
+		packetsReceived++;
+
+		if (flags & NETFLAG_UNRELIABLE)
+		{
+			if (sequence < sock->unreliableReceiveSequence)
+			{
+				Con_DPrintf("Got a stale datagram\n");
+				ret = 0;
+				break;
+			}
+			if (sequence != sock->unreliableReceiveSequence)
+			{
+				count = sequence - sock->unreliableReceiveSequence;
+				droppedDatagrams += count;
+				Con_DPrintf("Dropped %u datagram(s)\n", count);
+			}
+			sock->unreliableReceiveSequence = sequence + 1;
+
+			length -= NET_HEADERSIZE;
+
+			SZ_Clear (&net_message);
+			SZ_Write (&net_message, packetBuffer.data, length);
+
+			ret = 2;
+			break;
+		}
+
+		if (flags & NETFLAG_ACK)
+		{
+			if (sequence != (sock->sendSequence - 1))
+			{
+				Con_DPrintf("Stale ACK received\n");
+				continue;
+			}
+			if (sequence == sock->ackSequence)
+			{
+				sock->ackSequence++;
+				if (sock->ackSequence != sock->sendSequence)
+					Con_DPrintf("ack sequencing error\n");
+			}
+			else
+			{
+				Con_DPrintf("Duplicate ACK received\n");
+				continue;
+			}
+			sock->sendMessageLength -= MAX_DATAGRAM;
+			if (sock->sendMessageLength > 0)
+			{
+				Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength);
+				sock->sendNext = true;
+			}
+			else
+			{
+				sock->sendMessageLength = 0;
+				sock->canSend = true;
+			}
+			continue;
+		}
+
+		if (flags & NETFLAG_DATA)
+		{
+			packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK);
+			packetBuffer.sequence = BigLong(sequence);
+			sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr);
+
+			if (sequence != sock->receiveSequence)
+			{
+				receivedDuplicateCount++;
+				continue;
+			}
+			sock->receiveSequence++;
+
+			length -= NET_HEADERSIZE;
+
+			if (flags & NETFLAG_EOM)
+			{
+				SZ_Clear(&net_message);
+				SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength);
+				SZ_Write(&net_message, packetBuffer.data, length);
+				sock->receiveMessageLength = 0;
+
+				ret = 1;
+				break;
+			}
+
+			Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
+			sock->receiveMessageLength += length;
+			continue;
+		}
+	}
+
+	if (sock->sendNext)
+		SendMessageNext (sock);
+
+	return ret;
+}
+
+
+void PrintStats(qsocket_t *s)
+{
+	Con_Printf("canSend = %4u   \n", s->canSend);
+	Con_Printf("sendSeq = %4u   ", s->sendSequence);
+	Con_Printf("recvSeq = %4u   \n", s->receiveSequence);
+	Con_Printf("\n");
+}
+
+void NET_Stats_f (void)
+{
+	qsocket_t	*s;
+
+	if (Cmd_Argc () == 1)
+	{
+		Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
+		Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
+		Con_Printf("reliable messages sent     = %i\n", messagesSent);
+		Con_Printf("reliable messages received = %i\n", messagesReceived);
+		Con_Printf("packetsSent                = %i\n", packetsSent);
+		Con_Printf("packetsReSent              = %i\n", packetsReSent);
+		Con_Printf("packetsReceived            = %i\n", packetsReceived);
+		Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
+		Con_Printf("shortPacketCount           = %i\n", shortPacketCount);
+		Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
+	}
+	else if (Q_strcmp(Cmd_Argv(1), "*") == 0)
+	{
+		for (s = net_activeSockets; s; s = s->next)
+			PrintStats(s);
+		for (s = net_freeSockets; s; s = s->next)
+			PrintStats(s);
+	}
+	else
+	{
+		for (s = net_activeSockets; s; s = s->next)
+			if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+				break;
+		if (s == NULL)
+			for (s = net_freeSockets; s; s = s->next)
+				if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0)
+					break;
+		if (s == NULL)
+			return;
+		PrintStats(s);
+	}
+}
+
+
+static qboolean testInProgress = false;
+static int		testPollCount;
+static int		testDriver;
+static int		testSocket;
+
+static void Test_Poll(void);
+PollProcedure	testPollProcedure = {NULL, 0.0, Test_Poll};
+
+static void Test_Poll(void)
+{
+	struct qsockaddr clientaddr;
+	int		control;
+	int		len;
+	char	name[32];
+	char	address[64];
+	int		colors;
+	int		frags;
+	int		connectTime;
+	byte	playerNumber;
+
+	net_landriverlevel = testDriver;
+
+	while (1)
+	{
+		len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr);
+		if (len < sizeof(int))
+			break;
+
+		net_message.cursize = len;
+
+		MSG_BeginReading ();
+		control = BigLong(*((int *)net_message.data));
+		MSG_ReadLong();
+		if (control == -1)
+			break;
+		if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+			break;
+		if ((control & NETFLAG_LENGTH_MASK) != len)
+			break;
+
+		if (MSG_ReadByte() != CCREP_PLAYER_INFO)
+			Sys_Error("Unexpected repsonse to Player Info request\n");
+
+		playerNumber = MSG_ReadByte();
+		Q_strcpy(name, MSG_ReadString());
+		colors = MSG_ReadLong();
+		frags = MSG_ReadLong();
+		connectTime = MSG_ReadLong();
+		Q_strcpy(address, MSG_ReadString());
+
+		Con_Printf("%s\n  frags:%3i  colors:%u %u  time:%u\n  %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address);
+	}
+
+	testPollCount--;
+	if (testPollCount)
+	{
+		SchedulePollProcedure(&testPollProcedure, 0.1);
+	}
+	else
+	{
+		dfunc.CloseSocket(testSocket);
+		testInProgress = false;
+	}
+}
+
+static void Test_f (void)
+{
+	char	*host;
+	int		n;
+	int		max = MAX_SCOREBOARD;
+	struct qsockaddr sendaddr;
+
+	if (testInProgress)
+		return;
+
+	host = Cmd_Argv (1);
+
+	if (host && hostCacheCount)
+	{
+		for (n = 0; n < hostCacheCount; n++)
+			if (Q_strcasecmp (host, hostcache[n].name) == 0)
+			{
+				if (hostcache[n].driver != myDriverLevel)
+					continue;
+				net_landriverlevel = hostcache[n].ldriver;
+				max = hostcache[n].maxusers;
+				Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+				break;
+			}
+		if (n < hostCacheCount)
+			goto JustDoIt;
+	}
+
+	for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+	{
+		if (!net_landrivers[net_landriverlevel].initialized)
+			continue;
+
+		// see if we can resolve the host name
+		if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+			break;
+	}
+	if (net_landriverlevel == net_numlandrivers)
+		return;
+
+JustDoIt:
+	testSocket = dfunc.OpenSocket(0);
+	if (testSocket == -1)
+		return;
+
+	testInProgress = true;
+	testPollCount = 20;
+	testDriver = net_landriverlevel;
+
+	for (n = 0; n < max; n++)
+	{
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);
+		MSG_WriteByte(&net_message, n);
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | 	(net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr);
+	}
+	SZ_Clear(&net_message);
+	SchedulePollProcedure(&testPollProcedure, 0.1);
+}
+
+
+static qboolean test2InProgress = false;
+static int		test2Driver;
+static int		test2Socket;
+
+static void Test2_Poll(void);
+PollProcedure	test2PollProcedure = {NULL, 0.0, Test2_Poll};
+
+static void Test2_Poll(void)
+{
+	struct qsockaddr clientaddr;
+	int		control;
+	int		len;
+	char	name[256];
+	char	value[256];
+
+	net_landriverlevel = test2Driver;
+	name[0] = 0;
+
+	len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr);
+	if (len < sizeof(int))
+		goto Reschedule;
+
+	net_message.cursize = len;
+
+	MSG_BeginReading ();
+	control = BigLong(*((int *)net_message.data));
+	MSG_ReadLong();
+	if (control == -1)
+		goto Error;
+	if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+		goto Error;
+	if ((control & NETFLAG_LENGTH_MASK) != len)
+		goto Error;
+
+	if (MSG_ReadByte() != CCREP_RULE_INFO)
+		goto Error;
+
+	Q_strcpy(name, MSG_ReadString());
+	if (name[0] == 0)
+		goto Done;
+	Q_strcpy(value, MSG_ReadString());
+
+	Con_Printf("%-16.16s  %-16.16s\n", name, value);
+
+	SZ_Clear(&net_message);
+	// save space for the header, filled in later
+	MSG_WriteLong(&net_message, 0);
+	MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+	MSG_WriteString(&net_message, name);
+	*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+	dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr);
+	SZ_Clear(&net_message);
+
+Reschedule:
+	SchedulePollProcedure(&test2PollProcedure, 0.05);
+	return;
+
+Error:
+	Con_Printf("Unexpected repsonse to Rule Info request\n");
+Done:
+	dfunc.CloseSocket(test2Socket);
+	test2InProgress = false;
+	return;
+}
+
+static void Test2_f (void)
+{
+	char	*host;
+	int		n;
+	struct qsockaddr sendaddr;
+
+	if (test2InProgress)
+		return;
+
+	host = Cmd_Argv (1);
+
+	if (host && hostCacheCount)
+	{
+		for (n = 0; n < hostCacheCount; n++)
+			if (Q_strcasecmp (host, hostcache[n].name) == 0)
+			{
+				if (hostcache[n].driver != myDriverLevel)
+					continue;
+				net_landriverlevel = hostcache[n].ldriver;
+				Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
+				break;
+			}
+		if (n < hostCacheCount)
+			goto JustDoIt;
+	}
+
+	for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+	{
+		if (!net_landrivers[net_landriverlevel].initialized)
+			continue;
+
+		// see if we can resolve the host name
+		if (dfunc.GetAddrFromName(host, &sendaddr) != -1)
+			break;
+	}
+	if (net_landriverlevel == net_numlandrivers)
+		return;
+
+JustDoIt:
+	test2Socket = dfunc.OpenSocket(0);
+	if (test2Socket == -1)
+		return;
+
+	test2InProgress = true;
+	test2Driver = net_landriverlevel;
+
+	SZ_Clear(&net_message);
+	// save space for the header, filled in later
+	MSG_WriteLong(&net_message, 0);
+	MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
+	MSG_WriteString(&net_message, "");
+	*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+	dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr);
+	SZ_Clear(&net_message);
+	SchedulePollProcedure(&test2PollProcedure, 0.05);
+}
+
+
+int Datagram_Init (void)
+{
+	int i;
+	int csock;
+
+	myDriverLevel = net_driverlevel;
+	Cmd_AddCommand ("net_stats", NET_Stats_f);
+
+	if (COM_CheckParm("-nolan"))
+		return -1;
+
+	for (i = 0; i < net_numlandrivers; i++)
+		{
+		csock = net_landrivers[i].Init ();
+		if (csock == -1)
+			continue;
+		net_landrivers[i].initialized = true;
+		net_landrivers[i].controlSock = csock;
+		}
+
+#ifdef BAN_TEST
+	Cmd_AddCommand ("ban", NET_Ban_f);
+#endif
+	Cmd_AddCommand ("test", Test_f);
+	Cmd_AddCommand ("test2", Test2_f);
+
+	return 0;
+}
+
+
+void Datagram_Shutdown (void)
+{
+	int i;
+
+//
+// shutdown the lan drivers
+//
+	for (i = 0; i < net_numlandrivers; i++)
+	{
+		if (net_landrivers[i].initialized)
+		{
+			net_landrivers[i].Shutdown ();
+			net_landrivers[i].initialized = false;
+		}
+	}
+}
+
+
+void Datagram_Close (qsocket_t *sock)
+{
+	sfunc.CloseSocket(sock->socket);
+}
+
+
+void Datagram_Listen (qboolean state)
+{
+	int i;
+
+	for (i = 0; i < net_numlandrivers; i++)
+		if (net_landrivers[i].initialized)
+			net_landrivers[i].Listen (state);
+}
+
+
+static qsocket_t *_Datagram_CheckNewConnections (void)
+{
+	struct qsockaddr clientaddr;
+	struct qsockaddr newaddr;
+	int			newsock;
+	int			acceptsock;
+	qsocket_t	*sock;
+	qsocket_t	*s;
+	int			len;
+	int			command;
+	int			control;
+	int			ret;
+
+	acceptsock = dfunc.CheckNewConnections();
+	if (acceptsock == -1)
+		return NULL;
+
+	SZ_Clear(&net_message);
+
+	len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr);
+	if (len < sizeof(int))
+		return NULL;
+	net_message.cursize = len;
+
+	MSG_BeginReading ();
+	control = BigLong(*((int *)net_message.data));
+	MSG_ReadLong();
+	if (control == -1)
+		return NULL;
+	if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+		return NULL;
+	if ((control & NETFLAG_LENGTH_MASK) != len)
+		return NULL;
+
+	command = MSG_ReadByte();
+	if (command == CCREQ_SERVER_INFO)
+	{
+		if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+			return NULL;
+
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+		dfunc.GetSocketAddr(acceptsock, &newaddr);
+		MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+		MSG_WriteString(&net_message, hostname.string);
+		MSG_WriteString(&net_message, sv.name);
+		MSG_WriteByte(&net_message, net_activeconnections);
+		MSG_WriteByte(&net_message, svs.maxclients);
+		MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+		SZ_Clear(&net_message);
+		return NULL;
+	}
+
+	if (command == CCREQ_PLAYER_INFO)
+	{
+		int			playerNumber;
+		int			activeNumber;
+		int			clientNumber;
+		client_t	*client;
+		
+		playerNumber = MSG_ReadByte();
+		activeNumber = -1;
+		for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
+		{
+			if (client->active)
+			{
+				activeNumber++;
+				if (activeNumber == playerNumber)
+					break;
+			}
+		}
+		if (clientNumber == svs.maxclients)
+			return NULL;
+
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
+		MSG_WriteByte(&net_message, playerNumber);
+		MSG_WriteString(&net_message, client->name);
+		MSG_WriteLong(&net_message, client->colors);
+		MSG_WriteLong(&net_message, (int)client->edict->v.frags);
+		MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime));
+		MSG_WriteString(&net_message, client->netconnection->address);
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+		SZ_Clear(&net_message);
+
+		return NULL;
+	}
+
+	if (command == CCREQ_RULE_INFO)
+	{
+		char	*prevCvarName;
+		cvar_t	*var;
+
+		// find the search start location
+		prevCvarName = MSG_ReadString();
+		if (*prevCvarName)
+		{
+			var = Cvar_FindVar (prevCvarName);
+			if (!var)
+				return NULL;
+			var = var->next;
+		}
+		else
+			var = cvar_vars;
+
+		// search for the next server cvar
+		while (var)
+		{
+			if (var->server)
+				break;
+			var = var->next;
+		}
+
+		// send the response
+
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+		if (var)
+		{
+			MSG_WriteString(&net_message, var->name);
+			MSG_WriteString(&net_message, var->string);
+		}
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+		SZ_Clear(&net_message);
+
+		return NULL;
+	}
+
+	if (command != CCREQ_CONNECT)
+		return NULL;
+
+	if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
+		return NULL;
+
+	if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+	{
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREP_REJECT);
+		MSG_WriteString(&net_message, "Incompatible version.\n");
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+		SZ_Clear(&net_message);
+		return NULL;
+	}
+
+#ifdef BAN_TEST
+	// check for a ban
+	if (clientaddr.sa_family == AF_INET)
+	{
+		unsigned long testAddr;
+		testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr;
+		if ((testAddr & banMask) == banAddr)
+		{
+			SZ_Clear(&net_message);
+			// save space for the header, filled in later
+			MSG_WriteLong(&net_message, 0);
+			MSG_WriteByte(&net_message, CCREP_REJECT);
+			MSG_WriteString(&net_message, "You have been banned.\n");
+			*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+			dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+			SZ_Clear(&net_message);
+			return NULL;
+		}
+	}
+#endif
+
+	// see if this guy is already connected
+	for (s = net_activeSockets; s; s = s->next)
+	{
+		if (s->driver != net_driverlevel)
+			continue;
+		ret = dfunc.AddrCompare(&clientaddr, &s->addr);
+		if (ret >= 0)
+		{
+			// is this a duplicate connection reqeust?
+			if (ret == 0 && net_time - s->connecttime < 2.0)
+			{
+				// yes, so send a duplicate reply
+				SZ_Clear(&net_message);
+				// save space for the header, filled in later
+				MSG_WriteLong(&net_message, 0);
+				MSG_WriteByte(&net_message, CCREP_ACCEPT);
+				dfunc.GetSocketAddr(s->socket, &newaddr);
+				MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+				*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+				dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+				SZ_Clear(&net_message);
+				return NULL;
+			}
+			// it's somebody coming back in from a crash/disconnect
+			// so close the old qsocket and let their retry get them back in
+			NET_Close(s);
+			return NULL;
+		}
+	}
+
+	// allocate a QSocket
+	sock = NET_NewQSocket ();
+	if (sock == NULL)
+	{
+		// no room; try to let him know
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREP_REJECT);
+		MSG_WriteString(&net_message, "Server is full.\n");
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+		SZ_Clear(&net_message);
+		return NULL;
+	}
+
+	// allocate a network socket
+	newsock = dfunc.OpenSocket(0);
+	if (newsock == -1)
+	{
+		NET_FreeQSocket(sock);
+		return NULL;
+	}
+
+	// connect to the client
+	if (dfunc.Connect (newsock, &clientaddr) == -1)
+	{
+		dfunc.CloseSocket(newsock);
+		NET_FreeQSocket(sock);
+		return NULL;
+	}
+
+	// everything is allocated, just fill in the details	
+	sock->socket = newsock;
+	sock->landriver = net_landriverlevel;
+	sock->addr = clientaddr;
+	Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr));
+
+	// send him back the info about the server connection he has been allocated
+	SZ_Clear(&net_message);
+	// save space for the header, filled in later
+	MSG_WriteLong(&net_message, 0);
+	MSG_WriteByte(&net_message, CCREP_ACCEPT);
+	dfunc.GetSocketAddr(newsock, &newaddr);
+	MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
+//	MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
+	*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+	dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr);
+	SZ_Clear(&net_message);
+
+	return sock;
+}
+
+qsocket_t *Datagram_CheckNewConnections (void)
+{
+	qsocket_t *ret = NULL;
+
+	for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+		if (net_landrivers[net_landriverlevel].initialized)
+			if ((ret = _Datagram_CheckNewConnections ()) != NULL)
+				break;
+	return ret;
+}
+
+
+static void _Datagram_SearchForHosts (qboolean xmit)
+{
+	int		ret;
+	int		n;
+	int		i;
+	struct qsockaddr readaddr;
+	struct qsockaddr myaddr;
+	int		control;
+
+	dfunc.GetSocketAddr (dfunc.controlSock, &myaddr);
+	if (xmit)
+	{
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
+		MSG_WriteString(&net_message, "QUAKE");
+		MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize);
+		SZ_Clear(&net_message);
+	}
+
+	while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
+	{
+		if (ret < sizeof(int))
+			continue;
+		net_message.cursize = ret;
+
+		// don't answer our own query
+		if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
+			continue;
+
+		// is the cache full?
+		if (hostCacheCount == HOSTCACHESIZE)
+			continue;
+
+		MSG_BeginReading ();
+		control = BigLong(*((int *)net_message.data));
+		MSG_ReadLong();
+		if (control == -1)
+			continue;
+		if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+			continue;
+		if ((control & NETFLAG_LENGTH_MASK) != ret)
+			continue;
+
+		if (MSG_ReadByte() != CCREP_SERVER_INFO)
+			continue;
+
+		dfunc.GetAddrFromName(MSG_ReadString(), &readaddr);
+		// search the cache for this server
+		for (n = 0; n < hostCacheCount; n++)
+			if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
+				break;
+
+		// is it already there?
+		if (n < hostCacheCount)
+			continue;
+
+		// add it
+		hostCacheCount++;
+		Q_strcpy(hostcache[n].name, MSG_ReadString());
+		Q_strcpy(hostcache[n].map, MSG_ReadString());
+		hostcache[n].users = MSG_ReadByte();
+		hostcache[n].maxusers = MSG_ReadByte();
+		if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
+		{
+			Q_strcpy(hostcache[n].cname, hostcache[n].name);
+			hostcache[n].cname[14] = 0;
+			Q_strcpy(hostcache[n].name, "*");
+			Q_strcat(hostcache[n].name, hostcache[n].cname);
+		}
+		Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
+		hostcache[n].driver = net_driverlevel;
+		hostcache[n].ldriver = net_landriverlevel;
+		Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
+
+		// check for a name conflict
+		for (i = 0; i < hostCacheCount; i++)
+		{
+			if (i == n)
+				continue;
+			if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0)
+			{
+				i = Q_strlen(hostcache[n].name);
+				if (i < 15 && hostcache[n].name[i-1] > '8')
+				{
+					hostcache[n].name[i] = '0';
+					hostcache[n].name[i+1] = 0;
+				}
+				else
+					hostcache[n].name[i-1]++;
+				i = -1;
+			}
+		}
+	}
+}
+
+void Datagram_SearchForHosts (qboolean xmit)
+{
+	for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+	{
+		if (hostCacheCount == HOSTCACHESIZE)
+			break;
+		if (net_landrivers[net_landriverlevel].initialized)
+			_Datagram_SearchForHosts (xmit);
+	}
+}
+
+
+static qsocket_t *_Datagram_Connect (char *host)
+{
+	struct qsockaddr sendaddr;
+	struct qsockaddr readaddr;
+	qsocket_t	*sock;
+	int			newsock;
+	int			ret;
+	int			reps;
+	double		start_time;
+	int			control;
+	char		*reason;
+
+	// see if we can resolve the host name
+	if (dfunc.GetAddrFromName(host, &sendaddr) == -1)
+		return NULL;
+
+	newsock = dfunc.OpenSocket (0);
+	if (newsock == -1)
+		return NULL;
+
+	sock = NET_NewQSocket ();
+	if (sock == NULL)
+		goto ErrorReturn2;
+	sock->socket = newsock;
+	sock->landriver = net_landriverlevel;
+
+	// connect to the host
+	if (dfunc.Connect (newsock, &sendaddr) == -1)
+		goto ErrorReturn;
+
+	// send the connection request
+	Con_Printf("trying...\n"); SCR_UpdateScreen ();
+	start_time = net_time;
+
+	for (reps = 0; reps < 3; reps++)
+	{
+		SZ_Clear(&net_message);
+		// save space for the header, filled in later
+		MSG_WriteLong(&net_message, 0);
+		MSG_WriteByte(&net_message, CCREQ_CONNECT);
+		MSG_WriteString(&net_message, "QUAKE");
+		MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
+		*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+		dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr);
+		SZ_Clear(&net_message);
+		do
+		{
+			ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr);
+			// if we got something, validate it
+			if (ret > 0)
+			{
+				// is it from the right place?
+				if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0)
+				{
+#ifdef DEBUG
+					Con_Printf("wrong reply address\n");
+					Con_Printf("Expected: %s\n", StrAddr (&sendaddr));
+					Con_Printf("Received: %s\n", StrAddr (&readaddr));
+					SCR_UpdateScreen ();
+#endif
+					ret = 0;
+					continue;
+				}
+
+				if (ret < sizeof(int))
+				{
+					ret = 0;
+					continue;
+				}
+
+				net_message.cursize = ret;
+				MSG_BeginReading ();
+
+				control = BigLong(*((int *)net_message.data));
+				MSG_ReadLong();
+				if (control == -1)
+				{
+					ret = 0;
+					continue;
+				}
+				if ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)
+				{
+					ret = 0;
+					continue;
+				}
+				if ((control & NETFLAG_LENGTH_MASK) != ret)
+				{
+					ret = 0;
+					continue;
+				}
+			}
+		}
+		while (ret == 0 && (SetNetTime() - start_time) < 2.5);
+		if (ret)
+			break;
+		Con_Printf("still trying...\n"); SCR_UpdateScreen ();
+		start_time = SetNetTime();
+	}
+
+	if (ret == 0)
+	{
+		reason = "No Response";
+		Con_Printf("%s\n", reason);
+		Q_strcpy(m_return_reason, reason);
+		goto ErrorReturn;
+	}
+
+	if (ret == -1)
+	{
+		reason = "Network Error";
+		Con_Printf("%s\n", reason);
+		Q_strcpy(m_return_reason, reason);
+		goto ErrorReturn;
+	}
+
+	ret = MSG_ReadByte();
+	if (ret == CCREP_REJECT)
+	{
+		reason = MSG_ReadString();
+		Con_Printf(reason);
+		Q_strncpy(m_return_reason, reason, 31);
+		goto ErrorReturn;
+	}
+
+	if (ret == CCREP_ACCEPT)
+	{
+		Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
+		dfunc.SetSocketPort (&sock->addr, MSG_ReadLong());
+	}
+	else
+	{
+		reason = "Bad Response";
+		Con_Printf("%s\n", reason);
+		Q_strcpy(m_return_reason, reason);
+		goto ErrorReturn;
+	}
+
+	dfunc.GetNameFromAddr (&sendaddr, sock->address);
+
+	Con_Printf ("Connection accepted\n");
+	sock->lastMessageTime = SetNetTime();
+
+	// switch the connection to the specified address
+	if (dfunc.Connect (newsock, &sock->addr) == -1)
+	{
+		reason = "Connect to Game failed";
+		Con_Printf("%s\n", reason);
+		Q_strcpy(m_return_reason, reason);
+		goto ErrorReturn;
+	}
+
+	m_return_onerror = false;
+	return sock;
+
+ErrorReturn:
+	NET_FreeQSocket(sock);
+ErrorReturn2:
+	dfunc.CloseSocket(newsock);
+	if (m_return_onerror)
+	{
+		key_dest = key_menu;
+		m_state = m_return_state;
+		m_return_onerror = false;
+	}
+	return NULL;
+}
+
+qsocket_t *Datagram_Connect (char *host)
+{
+	qsocket_t *ret = NULL;
+
+	for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
+		if (net_landrivers[net_landriverlevel].initialized)
+			if ((ret = _Datagram_Connect (host)) != NULL)
+				break;
+	return ret;
+}
diff --git a/apps/plugins/sdl/progs/quake/net_dgrm.h b/apps/plugins/sdl/progs/quake/net_dgrm.h
new file mode 100644
index 0000000..da052e7
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_dgrm.h
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_dgrm.h
+
+
+int			Datagram_Init (void);
+void		Datagram_Listen (qboolean state);
+void		Datagram_SearchForHosts (qboolean xmit);
+qsocket_t	*Datagram_Connect (char *host);
+qsocket_t 	*Datagram_CheckNewConnections (void);
+int			Datagram_GetMessage (qsocket_t *sock);
+int			Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int			Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean	Datagram_CanSendMessage (qsocket_t *sock);
+qboolean	Datagram_CanSendUnreliableMessage (qsocket_t *sock);
+void		Datagram_Close (qsocket_t *sock);
+void		Datagram_Shutdown (void);
diff --git a/apps/plugins/sdl/progs/quake/net_loop.c b/apps/plugins/sdl/progs/quake/net_loop.c
new file mode 100644
index 0000000..35aa370
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_loop.c
@@ -0,0 +1,245 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_loop.c
+
+#include "quakedef.h"
+#include "net_loop.h"
+
+qboolean	localconnectpending = false;
+qsocket_t	*loop_client = NULL;
+qsocket_t	*loop_server = NULL;
+
+int Loop_Init (void)
+{
+	if (cls.state == ca_dedicated)
+		return -1;
+	return 0;
+}
+
+
+void Loop_Shutdown (void)
+{
+}
+
+
+void Loop_Listen (qboolean state)
+{
+}
+
+
+void Loop_SearchForHosts (qboolean xmit)
+{
+	if (!sv.active)
+		return;
+
+	hostCacheCount = 1;
+	if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+		Q_strcpy(hostcache[0].name, "local");
+	else
+		Q_strcpy(hostcache[0].name, hostname.string);
+	Q_strcpy(hostcache[0].map, sv.name);
+	hostcache[0].users = net_activeconnections;
+	hostcache[0].maxusers = svs.maxclients;
+	hostcache[0].driver = net_driverlevel;
+	Q_strcpy(hostcache[0].cname, "local");
+}
+
+
+qsocket_t *Loop_Connect (char *host)
+{
+	if (Q_strcmp(host,"local") != 0)
+		return NULL;
+	
+	localconnectpending = true;
+
+	if (!loop_client)
+	{
+		if ((loop_client = NET_NewQSocket ()) == NULL)
+		{
+			Con_Printf("Loop_Connect: no qsocket available\n");
+			return NULL;
+		}
+		Q_strcpy (loop_client->address, "localhost");
+	}
+	loop_client->receiveMessageLength = 0;
+	loop_client->sendMessageLength = 0;
+	loop_client->canSend = true;
+
+	if (!loop_server)
+	{
+		if ((loop_server = NET_NewQSocket ()) == NULL)
+		{
+			Con_Printf("Loop_Connect: no qsocket available\n");
+			return NULL;
+		}
+		Q_strcpy (loop_server->address, "LOCAL");
+	}
+	loop_server->receiveMessageLength = 0;
+	loop_server->sendMessageLength = 0;
+	loop_server->canSend = true;
+
+	loop_client->driverdata = (void *)loop_server;
+	loop_server->driverdata = (void *)loop_client;
+	
+	return loop_client;	
+}
+
+
+qsocket_t *Loop_CheckNewConnections (void)
+{
+	if (!localconnectpending)
+		return NULL;
+
+	localconnectpending = false;
+	loop_server->sendMessageLength = 0;
+	loop_server->receiveMessageLength = 0;
+	loop_server->canSend = true;
+	loop_client->sendMessageLength = 0;
+	loop_client->receiveMessageLength = 0;
+	loop_client->canSend = true;
+	return loop_server;
+}
+
+
+static int IntAlign(int value)
+{
+	return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
+}
+
+
+int Loop_GetMessage (qsocket_t *sock)
+{
+	int		ret;
+	int		length;
+
+	if (sock->receiveMessageLength == 0)
+		return 0;
+
+	ret = sock->receiveMessage[0];
+	length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
+	// alignment byte skipped here
+	SZ_Clear (&net_message);
+	SZ_Write (&net_message, &sock->receiveMessage[4], length);
+
+	length = IntAlign(length + 4);
+	sock->receiveMessageLength -= length;
+
+	if (sock->receiveMessageLength)
+		Q_memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength);
+
+	if (sock->driverdata && ret == 1)
+		((qsocket_t *)sock->driverdata)->canSend = true;
+
+	return ret;
+}
+
+
+int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	byte *buffer;
+	int  *bufferLength;
+
+	if (!sock->driverdata)
+		return -1;
+
+	bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+	if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE)
+		Sys_Error("Loop_SendMessage: overflow\n");
+
+	buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+	// message type
+	*buffer++ = 1;
+
+	// length
+	*buffer++ = data->cursize & 0xff;
+	*buffer++ = data->cursize >> 8;
+
+	// align
+	buffer++;
+
+	// message
+	Q_memcpy(buffer, data->data, data->cursize);
+	*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+
+	sock->canSend = false;
+	return 1;
+}
+
+
+int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	byte *buffer;
+	int  *bufferLength;
+
+	if (!sock->driverdata)
+		return -1;
+
+	bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
+
+	if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE)
+		return 0;
+
+	buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
+
+	// message type
+	*buffer++ = 2;
+
+	// length
+	*buffer++ = data->cursize & 0xff;
+	*buffer++ = data->cursize >> 8;
+
+	// align
+	buffer++;
+
+	// message
+	Q_memcpy(buffer, data->data, data->cursize);
+	*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
+	return 1;
+}
+
+
+qboolean Loop_CanSendMessage (qsocket_t *sock)
+{
+	if (!sock->driverdata)
+		return false;
+	return sock->canSend;
+}
+
+
+qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock)
+{
+	return true;
+}
+
+
+void Loop_Close (qsocket_t *sock)
+{
+	if (sock->driverdata)
+		((qsocket_t *)sock->driverdata)->driverdata = NULL;
+	sock->receiveMessageLength = 0;
+	sock->sendMessageLength = 0;
+	sock->canSend = true;
+	if (sock == loop_client)
+		loop_client = NULL;
+	else
+		loop_server = NULL;
+}
diff --git a/apps/plugins/sdl/progs/quake/net_loop.h b/apps/plugins/sdl/progs/quake/net_loop.h
new file mode 100644
index 0000000..90cdb2c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_loop.h
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_loop.h
+
+int			Loop_Init (void);
+void		Loop_Listen (qboolean state);
+void		Loop_SearchForHosts (qboolean xmit);
+qsocket_t 	*Loop_Connect (char *host);
+qsocket_t 	*Loop_CheckNewConnections (void);
+int			Loop_GetMessage (qsocket_t *sock);
+int			Loop_SendMessage (qsocket_t *sock, sizebuf_t *data);
+int			Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean	Loop_CanSendMessage (qsocket_t *sock);
+qboolean	Loop_CanSendUnreliableMessage (qsocket_t *sock);
+void		Loop_Close (qsocket_t *sock);
+void		Loop_Shutdown (void);
diff --git a/apps/plugins/sdl/progs/quake/net_main.c b/apps/plugins/sdl/progs/quake/net_main.c
new file mode 100644
index 0000000..5f5d2cf
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_main.c
@@ -0,0 +1,997 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_main.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+qsocket_t	*net_activeSockets = NULL;
+qsocket_t	*net_freeSockets = NULL;
+int			net_numsockets = 0;
+
+qboolean	serialAvailable = false;
+qboolean	ipxAvailable = false;
+qboolean	tcpipAvailable = false;
+
+int			net_hostport;
+int			DEFAULTnet_hostport = 26000;
+
+char		my_ipx_address[NET_NAMELEN];
+char		my_tcpip_address[NET_NAMELEN];
+
+void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem);
+void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
+void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
+
+static qboolean	listening = false;
+
+qboolean	slistInProgress = false;
+qboolean	slistSilent = false;
+qboolean	slistLocal = true;
+static double	slistStartTime;
+static int		slistLastShown;
+
+static void Slist_Send(void);
+static void Slist_Poll(void);
+PollProcedure	slistSendProcedure = {NULL, 0.0, Slist_Send};
+PollProcedure	slistPollProcedure = {NULL, 0.0, Slist_Poll};
+
+
+sizebuf_t		net_message;
+int				net_activeconnections = 0;
+
+int messagesSent = 0;
+int messagesReceived = 0;
+int unreliableMessagesSent = 0;
+int unreliableMessagesReceived = 0;
+
+cvar_t	net_messagetimeout = {"net_messagetimeout","300"};
+cvar_t	hostname = {"hostname", "UNNAMED"};
+
+qboolean	configRestored = false;
+cvar_t	config_com_port = {"_config_com_port", "0x3f8", true};
+cvar_t	config_com_irq = {"_config_com_irq", "4", true};
+cvar_t	config_com_baud = {"_config_com_baud", "57600", true};
+cvar_t	config_com_modem = {"_config_com_modem", "1", true};
+cvar_t	config_modem_dialtype = {"_config_modem_dialtype", "T", true};
+cvar_t	config_modem_clear = {"_config_modem_clear", "ATZ", true};
+cvar_t	config_modem_init = {"_config_modem_init", "", true};
+cvar_t	config_modem_hangup = {"_config_modem_hangup", "AT H", true};
+
+#ifdef IDGODS
+cvar_t	idgods = {"idgods", "0"};
+#endif
+
+int	vcrFile = -1;
+qboolean recording = false;
+
+// these two macros are to make the code more readable
+#define sfunc	net_drivers[sock->driver]
+#define dfunc	net_drivers[net_driverlevel]
+
+int	net_driverlevel;
+
+
+double			net_time;
+
+double SetNetTime(void)
+{
+	net_time = Sys_FloatTime();
+	return net_time;
+}
+
+
+/*
+===================
+NET_NewQSocket
+
+Called by drivers when a new communications endpoint is required
+The sequence and buffer fields will be filled in properly
+===================
+*/
+qsocket_t *NET_NewQSocket (void)
+{
+	qsocket_t	*sock;
+
+	if (net_freeSockets == NULL)
+		return NULL;
+
+	if (net_activeconnections >= svs.maxclients)
+		return NULL;
+
+	// get one from free list
+	sock = net_freeSockets;
+	net_freeSockets = sock->next;
+
+	// add it to active list
+	sock->next = net_activeSockets;
+	net_activeSockets = sock;
+
+	sock->disconnected = false;
+	sock->connecttime = net_time;
+	Q_strcpy (sock->address,"UNSET ADDRESS");
+	sock->driver = net_driverlevel;
+	sock->socket = 0;
+	sock->driverdata = NULL;
+	sock->canSend = true;
+	sock->sendNext = false;
+	sock->lastMessageTime = net_time;
+	sock->ackSequence = 0;
+	sock->sendSequence = 0;
+	sock->unreliableSendSequence = 0;
+	sock->sendMessageLength = 0;
+	sock->receiveSequence = 0;
+	sock->unreliableReceiveSequence = 0;
+	sock->receiveMessageLength = 0;
+
+	return sock;
+}
+
+
+void NET_FreeQSocket(qsocket_t *sock)
+{
+	qsocket_t	*s;
+
+	// remove it from active list
+	if (sock == net_activeSockets)
+		net_activeSockets = net_activeSockets->next;
+	else
+	{
+		for (s = net_activeSockets; s; s = s->next)
+			if (s->next == sock)
+			{
+				s->next = sock->next;
+				break;
+			}
+		if (!s)
+			Sys_Error ("NET_FreeQSocket: not active\n");
+	}
+
+	// add it to free list
+	sock->next = net_freeSockets;
+	net_freeSockets = sock;
+	sock->disconnected = true;
+}
+
+
+static void NET_Listen_f (void)
+{
+	if (Cmd_Argc () != 2)
+	{
+		Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
+		return;
+	}
+
+	listening = Q_atoi(Cmd_Argv(1)) ? true : false;
+
+	for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
+	{
+		if (net_drivers[net_driverlevel].initialized == false)
+			continue;
+		dfunc.Listen (listening);
+	}
+}
+
+
+static void MaxPlayers_f (void)
+{
+	int 	n;
+
+	if (Cmd_Argc () != 2)
+	{
+		Con_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients);
+		return;
+	}
+
+	if (sv.active)
+	{
+		Con_Printf ("maxplayers can not be changed while a server is running.\n");
+		return;
+	}
+
+	n = Q_atoi(Cmd_Argv(1));
+	if (n < 1)
+		n = 1;
+	if (n > svs.maxclientslimit)
+	{
+		n = svs.maxclientslimit;
+		Con_Printf ("\"maxplayers\" set to \"%u\"\n", n);
+	}
+
+	if ((n == 1) && listening)
+		Cbuf_AddText ("listen 0\n");
+
+	if ((n > 1) && (!listening))
+		Cbuf_AddText ("listen 1\n");
+
+	svs.maxclients = n;
+	if (n == 1)
+		Cvar_Set ("deathmatch", "0");
+	else
+		Cvar_Set ("deathmatch", "1");
+}
+
+
+static void NET_Port_f (void)
+{
+	int 	n;
+
+	if (Cmd_Argc () != 2)
+	{
+		Con_Printf ("\"port\" is \"%u\"\n", net_hostport);
+		return;
+	}
+
+	n = Q_atoi(Cmd_Argv(1));
+	if (n < 1 || n > 65534)
+	{
+		Con_Printf ("Bad value, must be between 1 and 65534\n");
+		return;
+	}
+
+	DEFAULTnet_hostport = n;
+	net_hostport = n;
+
+	if (listening)
+	{
+		// force a change to the new port
+		Cbuf_AddText ("listen 0\n");
+		Cbuf_AddText ("listen 1\n");
+	}
+}
+
+
+static void PrintSlistHeader(void)
+{
+	Con_Printf("Server          Map             Users\n");
+	Con_Printf("--------------- --------------- -----\n");
+	slistLastShown = 0;
+}
+
+
+static void PrintSlist(void)
+{
+	int n;
+
+	for (n = slistLastShown; n < hostCacheCount; n++)
+	{
+		if (hostcache[n].maxusers)
+			Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
+		else
+			Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
+	}
+	slistLastShown = n;
+}
+
+
+static void PrintSlistTrailer(void)
+{
+	if (hostCacheCount)
+		Con_Printf("== end list ==\n\n");
+	else
+		Con_Printf("No Quake servers found.\n\n");
+}
+
+
+void NET_Slist_f (void)
+{
+	if (slistInProgress)
+		return;
+
+	if (! slistSilent)
+	{
+		Con_Printf("Looking for Quake servers...\n");
+		PrintSlistHeader();
+	}
+
+	slistInProgress = true;
+	slistStartTime = Sys_FloatTime();
+
+	SchedulePollProcedure(&slistSendProcedure, 0.0);
+	SchedulePollProcedure(&slistPollProcedure, 0.1);
+
+	hostCacheCount = 0;
+}
+
+
+static void Slist_Send(void)
+{
+	for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+	{
+		if (!slistLocal && net_driverlevel == 0)
+			continue;
+		if (net_drivers[net_driverlevel].initialized == false)
+			continue;
+		dfunc.SearchForHosts (true);
+	}
+
+	if ((Sys_FloatTime() - slistStartTime) < 0.5)
+		SchedulePollProcedure(&slistSendProcedure, 0.75);
+}
+
+
+static void Slist_Poll(void)
+{
+	for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
+	{
+		if (!slistLocal && net_driverlevel == 0)
+			continue;
+		if (net_drivers[net_driverlevel].initialized == false)
+			continue;
+		dfunc.SearchForHosts (false);
+	}
+
+	if (! slistSilent)
+		PrintSlist();
+
+	if ((Sys_FloatTime() - slistStartTime) < 1.5)
+	{
+		SchedulePollProcedure(&slistPollProcedure, 0.1);
+		return;
+	}
+
+	if (! slistSilent)
+		PrintSlistTrailer();
+	slistInProgress = false;
+	slistSilent = false;
+	slistLocal = true;
+}
+
+
+/*
+===================
+NET_Connect
+===================
+*/
+
+int hostCacheCount = 0;
+hostcache_t hostcache[HOSTCACHESIZE];
+
+qsocket_t *NET_Connect (char *host)
+{
+	qsocket_t		*ret;
+	int				n;
+	int				numdrivers = net_numdrivers;
+
+	SetNetTime();
+
+	if (host && *host == 0)
+		host = NULL;
+
+	if (host)
+	{
+		if (Q_strcasecmp (host, "local") == 0)
+		{
+			numdrivers = 1;
+			goto JustDoIt;
+		}
+
+		if (hostCacheCount)
+		{
+			for (n = 0; n < hostCacheCount; n++)
+				if (Q_strcasecmp (host, hostcache[n].name) == 0)
+				{
+					host = hostcache[n].cname;
+					break;
+				}
+			if (n < hostCacheCount)
+				goto JustDoIt;
+		}
+	}
+
+	slistSilent = host ? true : false;
+	NET_Slist_f ();
+
+	while(slistInProgress)
+		NET_Poll();
+
+	if (host == NULL)
+	{
+		if (hostCacheCount != 1)
+			return NULL;
+		host = hostcache[0].cname;
+		Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
+	}
+
+	if (hostCacheCount)
+		for (n = 0; n < hostCacheCount; n++)
+			if (Q_strcasecmp (host, hostcache[n].name) == 0)
+			{
+				host = hostcache[n].cname;
+				break;
+			}
+
+JustDoIt:
+	for (net_driverlevel=0 ; net_driverlevel<numdrivers; net_driverlevel++)
+	{
+		if (net_drivers[net_driverlevel].initialized == false)
+			continue;
+		ret = dfunc.Connect (host);
+		if (ret)
+			return ret;
+	}
+
+	if (host)
+	{
+		Con_Printf("\n");
+		PrintSlistHeader();
+		PrintSlist();
+		PrintSlistTrailer();
+	}
+	
+	return NULL;
+}
+
+
+/*
+===================
+NET_CheckNewConnections
+===================
+*/
+
+struct
+{
+	double	time;
+	int		op;
+	long	session;
+} vcrConnect;
+
+qsocket_t *NET_CheckNewConnections (void)
+{
+	qsocket_t	*ret;
+
+	SetNetTime();
+
+	for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
+	{
+		if (net_drivers[net_driverlevel].initialized == false)
+			continue;
+		if (net_driverlevel && listening == false)
+			continue;
+		ret = dfunc.CheckNewConnections ();
+		if (ret)
+		{
+			if (recording)
+			{
+				vcrConnect.time = host_time;
+				vcrConnect.op = VCR_OP_CONNECT;
+				vcrConnect.session = (long)ret;
+				Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect));
+				Sys_FileWrite (vcrFile, ret->address, NET_NAMELEN);
+			}
+			return ret;
+		}
+	}
+	
+	if (recording)
+	{
+		vcrConnect.time = host_time;
+		vcrConnect.op = VCR_OP_CONNECT;
+		vcrConnect.session = 0;
+		Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect));
+	}
+
+	return NULL;
+}
+
+/*
+===================
+NET_Close
+===================
+*/
+void NET_Close (qsocket_t *sock)
+{
+	if (!sock)
+		return;
+
+	if (sock->disconnected)
+		return;
+
+	SetNetTime();
+
+	// call the driver_Close function
+	sfunc.Close (sock);
+
+	NET_FreeQSocket(sock);
+}
+
+
+/*
+=================
+NET_GetMessage
+
+If there is a complete message, return it in net_message
+
+returns 0 if no data is waiting
+returns 1 if a message was received
+returns -1 if connection is invalid
+=================
+*/
+
+struct
+{
+	double	time;
+	int		op;
+	long	session;
+	int		ret;
+	int		len;
+} vcrGetMessage;
+
+extern void PrintStats(qsocket_t *s);
+
+int	NET_GetMessage (qsocket_t *sock)
+{
+	int ret;
+
+	if (!sock)
+		return -1;
+
+	if (sock->disconnected)
+	{
+		Con_Printf("NET_GetMessage: disconnected socket\n");
+		return -1;
+	}
+
+	SetNetTime();
+
+	ret = sfunc.QGetMessage(sock);
+
+	// see if this connection has timed out
+	if (ret == 0 && sock->driver)
+	{
+		if (net_time - sock->lastMessageTime > net_messagetimeout.value)
+		{
+			NET_Close(sock);
+			return -1;
+		}
+	}
+
+
+	if (ret > 0)
+	{
+		if (sock->driver)
+		{
+			sock->lastMessageTime = net_time;
+			if (ret == 1)
+				messagesReceived++;
+			else if (ret == 2)
+				unreliableMessagesReceived++;
+		}
+
+		if (recording)
+		{
+			vcrGetMessage.time = host_time;
+			vcrGetMessage.op = VCR_OP_GETMESSAGE;
+			vcrGetMessage.session = (long)sock;
+			vcrGetMessage.ret = ret;
+			vcrGetMessage.len = net_message.cursize;
+			Sys_FileWrite (vcrFile, &vcrGetMessage, 24);
+			Sys_FileWrite (vcrFile, net_message.data, net_message.cursize);
+		}
+	}
+	else
+	{
+		if (recording)
+		{
+			vcrGetMessage.time = host_time;
+			vcrGetMessage.op = VCR_OP_GETMESSAGE;
+			vcrGetMessage.session = (long)sock;
+			vcrGetMessage.ret = ret;
+			Sys_FileWrite (vcrFile, &vcrGetMessage, 20);
+		}
+	}
+
+	return ret;
+}
+
+
+/*
+==================
+NET_SendMessage
+
+Try to send a complete length+message unit over the reliable stream.
+returns 0 if the message cannot be delivered reliably, but the connection
+		is still considered valid
+returns 1 if the message was sent properly
+returns -1 if the connection died
+==================
+*/
+struct
+{
+	double	time;
+	int		op;
+	long	session;
+	int		r;
+} vcrSendMessage;
+
+int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	int		r;
+	
+	if (!sock)
+		return -1;
+
+	if (sock->disconnected)
+	{
+		Con_Printf("NET_SendMessage: disconnected socket\n");
+		return -1;
+	}
+
+	SetNetTime();
+	r = sfunc.QSendMessage(sock, data);
+	if (r == 1 && sock->driver)
+		messagesSent++;
+
+	if (recording)
+	{
+		vcrSendMessage.time = host_time;
+		vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+		vcrSendMessage.session = (long)sock;
+		vcrSendMessage.r = r;
+		Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+	}
+	
+	return r;
+}
+
+
+int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	int		r;
+	
+	if (!sock)
+		return -1;
+
+	if (sock->disconnected)
+	{
+		Con_Printf("NET_SendMessage: disconnected socket\n");
+		return -1;
+	}
+
+	SetNetTime();
+	r = sfunc.SendUnreliableMessage(sock, data);
+	if (r == 1 && sock->driver)
+		unreliableMessagesSent++;
+
+	if (recording)
+	{
+		vcrSendMessage.time = host_time;
+		vcrSendMessage.op = VCR_OP_SENDMESSAGE;
+		vcrSendMessage.session = (long)sock;
+		vcrSendMessage.r = r;
+		Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+	}
+	
+	return r;
+}
+
+
+/*
+==================
+NET_CanSendMessage
+
+Returns true or false if the given qsocket can currently accept a
+message to be transmitted.
+==================
+*/
+qboolean NET_CanSendMessage (qsocket_t *sock)
+{
+	int		r;
+	
+	if (!sock)
+		return false;
+
+	if (sock->disconnected)
+		return false;
+
+	SetNetTime();
+
+	r = sfunc.CanSendMessage(sock);
+	
+	if (recording)
+	{
+		vcrSendMessage.time = host_time;
+		vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
+		vcrSendMessage.session = (long)sock;
+		vcrSendMessage.r = r;
+		Sys_FileWrite (vcrFile, &vcrSendMessage, 20);
+	}
+	
+	return r;
+}
+
+
+int NET_SendToAll(sizebuf_t *data, int blocktime)
+{
+	double		start;
+	int			i;
+	int			count = 0;
+	qboolean	state1 [MAX_SCOREBOARD];
+	qboolean	state2 [MAX_SCOREBOARD];
+
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+	{
+		if (!host_client->netconnection)
+			continue;
+		if (host_client->active)
+		{
+			if (host_client->netconnection->driver == 0)
+			{
+				NET_SendMessage(host_client->netconnection, data);
+				state1[i] = true;
+				state2[i] = true;
+				continue;
+			}
+			count++;
+			state1[i] = false;
+			state2[i] = false;
+		}
+		else
+		{
+			state1[i] = true;
+			state2[i] = true;
+		}
+	}
+
+	start = Sys_FloatTime();
+	while (count)
+	{
+		count = 0;
+		for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+		{
+			if (! state1[i])
+			{
+				if (NET_CanSendMessage (host_client->netconnection))
+				{
+					state1[i] = true;
+					NET_SendMessage(host_client->netconnection, data);
+				}
+				else
+				{
+					NET_GetMessage (host_client->netconnection);
+				}
+				count++;
+				continue;
+			}
+
+			if (! state2[i])
+			{
+				if (NET_CanSendMessage (host_client->netconnection))
+				{
+					state2[i] = true;
+				}
+				else
+				{
+					NET_GetMessage (host_client->netconnection);
+				}
+				count++;
+				continue;
+			}
+		}
+		if ((Sys_FloatTime() - start) > blocktime)
+			break;
+	}
+	return count;
+}
+
+
+//=============================================================================
+
+/*
+====================
+NET_Init
+====================
+*/
+
+void NET_Init (void)
+{
+	int			i;
+	int			controlSocket;
+	qsocket_t	*s;
+
+	if (COM_CheckParm("-playback"))
+	{
+		net_numdrivers = 1;
+		net_drivers[0].Init = VCR_Init;
+	}
+
+	if (COM_CheckParm("-record"))
+		recording = true;
+
+	i = COM_CheckParm ("-port");
+	if (!i)
+		i = COM_CheckParm ("-udpport");
+	if (!i)
+		i = COM_CheckParm ("-ipxport");
+
+	if (i)
+	{
+		if (i < com_argc-1)
+			DEFAULTnet_hostport = Q_atoi (com_argv[i+1]);
+		else
+			Sys_Error ("NET_Init: you must specify a number after -port");
+	}
+	net_hostport = DEFAULTnet_hostport;
+
+	if (COM_CheckParm("-listen") || cls.state == ca_dedicated)
+		listening = true;
+	net_numsockets = svs.maxclientslimit;
+	if (cls.state != ca_dedicated)
+		net_numsockets++;
+
+	SetNetTime();
+
+	for (i = 0; i < net_numsockets; i++)
+	{
+		s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket");
+		s->next = net_freeSockets;
+		net_freeSockets = s;
+		s->disconnected = true;
+	}
+
+	// allocate space for network message buffer
+	SZ_Alloc (&net_message, NET_MAXMESSAGE);
+
+	Cvar_RegisterVariable (&net_messagetimeout);
+	Cvar_RegisterVariable (&hostname);
+	Cvar_RegisterVariable (&config_com_port);
+	Cvar_RegisterVariable (&config_com_irq);
+	Cvar_RegisterVariable (&config_com_baud);
+	Cvar_RegisterVariable (&config_com_modem);
+	Cvar_RegisterVariable (&config_modem_dialtype);
+	Cvar_RegisterVariable (&config_modem_clear);
+	Cvar_RegisterVariable (&config_modem_init);
+	Cvar_RegisterVariable (&config_modem_hangup);
+#ifdef IDGODS
+	Cvar_RegisterVariable (&idgods);
+#endif
+
+	Cmd_AddCommand ("slist", NET_Slist_f);
+	Cmd_AddCommand ("listen", NET_Listen_f);
+	Cmd_AddCommand ("maxplayers", MaxPlayers_f);
+	Cmd_AddCommand ("port", NET_Port_f);
+
+	// initialize all the drivers
+	for (net_driverlevel=0 ; net_driverlevel<net_numdrivers ; net_driverlevel++)
+		{
+		controlSocket = net_drivers[net_driverlevel].Init();
+		if (controlSocket == -1)
+			continue;
+		net_drivers[net_driverlevel].initialized = true;
+		net_drivers[net_driverlevel].controlSock = controlSocket;
+		if (listening)
+			net_drivers[net_driverlevel].Listen (true);
+		}
+
+	if (*my_ipx_address)
+		Con_DPrintf("IPX address %s\n", my_ipx_address);
+	if (*my_tcpip_address)
+		Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
+}
+
+/*
+====================
+NET_Shutdown
+====================
+*/
+
+void		NET_Shutdown (void)
+{
+	qsocket_t	*sock;
+
+	SetNetTime();
+
+	for (sock = net_activeSockets; sock; sock = sock->next)
+		NET_Close(sock);
+
+//
+// shutdown the drivers
+//
+	for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
+	{
+		if (net_drivers[net_driverlevel].initialized == true)
+		{
+			net_drivers[net_driverlevel].Shutdown ();
+			net_drivers[net_driverlevel].initialized = false;
+		}
+	}
+
+	if (vcrFile != -1)
+	{
+		Con_Printf ("Closing vcrfile.\n");
+		Sys_FileClose(vcrFile);
+	}
+}
+
+
+static PollProcedure *pollProcedureList = NULL;
+
+void NET_Poll(void)
+{
+	PollProcedure *pp;
+	qboolean	useModem;
+
+	if (!configRestored)
+	{
+		if (serialAvailable)
+		{
+			if (config_com_modem.value == 1.0)
+				useModem = true;
+			else
+				useModem = false;
+			SetComPortConfig (0, (int)config_com_port.value, (int)config_com_irq.value, (int)config_com_baud.value, useModem);
+			SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string);
+		}
+		configRestored = true;
+	}
+
+	SetNetTime();
+
+	for (pp = pollProcedureList; pp; pp = pp->next)
+	{
+		if (pp->nextTime > net_time)
+			break;
+		pollProcedureList = pp->next;
+		pp->procedure(pp->arg);
+	}
+}
+
+
+void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
+{
+	PollProcedure *pp, *prev;
+
+	proc->nextTime = Sys_FloatTime() + timeOffset;
+	for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
+	{
+		if (pp->nextTime >= proc->nextTime)
+			break;
+		prev = pp;
+	}
+
+	if (prev == NULL)
+	{
+		proc->next = pollProcedureList;
+		pollProcedureList = proc;
+		return;
+	}
+
+	proc->next = pp;
+	prev->next = proc;
+}
+
+
+#ifdef IDGODS
+#define IDNET	0xc0f62800
+
+qboolean IsID(struct qsockaddr *addr)
+{
+	if (idgods.value == 0.0)
+		return false;
+
+	if (addr->sa_family != 2)
+		return false;
+
+	if ((BigLong(*(int *)&addr->sa_data[2]) & 0xffffff00) == IDNET)
+		return true;
+	return false;
+}
+#endif
diff --git a/apps/plugins/sdl/progs/quake/net_none.c b/apps/plugins/sdl/progs/quake/net_none.c
new file mode 100644
index 0000000..c00c975
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_none.c
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+#include "net_loop.h"
+
+net_driver_t net_drivers[MAX_NET_DRIVERS] =
+{
+	{
+	"Loopback",
+	false,
+	Loop_Init,
+	Loop_Listen,
+	Loop_SearchForHosts,
+	Loop_Connect,
+	Loop_CheckNewConnections,
+	Loop_GetMessage,
+	Loop_SendMessage,
+	Loop_SendUnreliableMessage,
+	Loop_CanSendMessage,
+	Loop_CanSendUnreliableMessage,
+	Loop_Close,
+	Loop_Shutdown
+	}
+};
+int net_numdrivers = 1;
+
+net_landriver_t	net_landrivers[MAX_NET_DRIVERS];
+int net_numlandrivers = 0;
diff --git a/apps/plugins/sdl/progs/quake/net_udp.c b/apps/plugins/sdl/progs/quake/net_udp.c
new file mode 100644
index 0000000..c7e8256
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_udp.c
@@ -0,0 +1,415 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_udp.c
+
+#include "quakedef.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+extern int gethostname (char *, int);
+extern int close (int);
+
+extern cvar_t hostname;
+
+static int net_acceptsocket = -1;		// socket for fielding new connections
+static int net_controlsocket;
+static int net_broadcastsocket = 0;
+static struct qsockaddr broadcastaddr;
+
+static unsigned long myAddr;
+
+#include "net_udp.h"
+
+//=============================================================================
+
+int UDP_Init (void)
+{
+	struct hostent *local;
+	char	buff[MAXHOSTNAMELEN];
+	struct qsockaddr addr;
+	char *colon;
+	
+	if (COM_CheckParm ("-noudp"))
+		return -1;
+
+	// determine my name & address
+	gethostname(buff, MAXHOSTNAMELEN);
+	local = gethostbyname(buff);
+	myAddr = *(int *)local->h_addr_list[0];
+
+	// if the quake hostname isn't set, set it to the machine name
+	if (Q_strcmp(hostname.string, "UNNAMED") == 0)
+	{
+		buff[15] = 0;
+		Cvar_Set ("hostname", buff);
+	}
+
+	if ((net_controlsocket = UDP_OpenSocket (0)) == -1)
+		Sys_Error("UDP_Init: Unable to open control socket\n");
+
+	((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET;
+	((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST;
+	((struct sockaddr_in *)&broadcastaddr)->sin_port = htons(net_hostport);
+
+	UDP_GetSocketAddr (net_controlsocket, &addr);
+	Q_strcpy(my_tcpip_address,  UDP_AddrToString (&addr));
+	colon = Q_strrchr (my_tcpip_address, ':');
+	if (colon)
+		*colon = 0;
+
+	Con_Printf("UDP Initialized\n");
+	tcpipAvailable = true;
+
+	return net_controlsocket;
+}
+
+//=============================================================================
+
+void UDP_Shutdown (void)
+{
+	UDP_Listen (false);
+	UDP_CloseSocket (net_controlsocket);
+}
+
+//=============================================================================
+
+void UDP_Listen (qboolean state)
+{
+	// enable listening
+	if (state)
+	{
+		if (net_acceptsocket != -1)
+			return;
+		if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == -1)
+			Sys_Error ("UDP_Listen: Unable to open accept socket\n");
+		return;
+	}
+
+	// disable listening
+	if (net_acceptsocket == -1)
+		return;
+	UDP_CloseSocket (net_acceptsocket);
+	net_acceptsocket = -1;
+}
+
+//=============================================================================
+
+int UDP_OpenSocket (int port)
+{
+	int newsocket;
+	struct sockaddr_in address;
+	qboolean _true = true;
+
+	if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+		return -1;
+
+#if 0
+	if (ioctl (newsocket, FIONBIO, (char *)&_true) == -1)
+		goto ErrorReturn;
+#endif
+	address.sin_family = AF_INET;
+	address.sin_addr.s_addr = INADDR_ANY;
+	address.sin_port = htons(port);
+	if( bind (newsocket, (void *)&address, sizeof(address)) == -1)
+		goto ErrorReturn;
+
+	return newsocket;
+
+ErrorReturn:
+	close (newsocket);
+	return -1;
+}
+
+//=============================================================================
+
+int UDP_CloseSocket (int socket)
+{
+	if (socket == net_broadcastsocket)
+		net_broadcastsocket = 0;
+	return close (socket);
+}
+
+
+//=============================================================================
+/*
+============
+PartialIPAddress
+
+this lets you type only as much of the net address as required, using
+the local network components to fill in the rest
+============
+*/
+static int PartialIPAddress (char *in, struct qsockaddr *hostaddr)
+{
+	char buff[256];
+	char *b;
+	int addr;
+	int num;
+	int mask;
+	int run;
+	int port;
+	
+	buff[0] = '.';
+	b = buff;
+	strcpy(buff+1, in);
+	if (buff[1] == '.')
+		b++;
+
+	addr = 0;
+	mask=-1;
+	while (*b == '.')
+	{
+		b++;
+		num = 0;
+		run = 0;
+		while (!( *b < '0' || *b > '9'))
+		{
+		  num = num*10 + *b++ - '0';
+		  if (++run > 3)
+		  	return -1;
+		}
+		if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
+			return -1;
+		if (num < 0 || num > 255)
+			return -1;
+		mask<<=8;
+		addr = (addr<<8) + num;
+	}
+	
+	if (*b++ == ':')
+		port = Q_atoi(b);
+	else
+		port = net_hostport;
+
+	hostaddr->sa_family = AF_INET;
+	((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port);	
+	((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr);
+	
+	return 0;
+}
+//=============================================================================
+
+int UDP_Connect (int socket, struct qsockaddr *addr)
+{
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_CheckNewConnections (void)
+{
+	unsigned long	available;
+
+	if (net_acceptsocket == -1)
+		return -1;
+
+	if (ioctl (net_acceptsocket, FIONREAD, &available) == -1)
+		Sys_Error ("UDP: ioctlsocket (FIONREAD) failed\n");
+	if (available)
+		return net_acceptsocket;
+	return -1;
+}
+
+//=============================================================================
+
+int UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+	int addrlen = sizeof (struct qsockaddr);
+	int ret;
+
+	ret = recvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen);
+	if (ret == -1 && (errno == EWOULDBLOCK || errno == ECONNREFUSED))
+		return 0;
+	return ret;
+}
+
+//=============================================================================
+
+int UDP_MakeSocketBroadcastCapable (int socket)
+{
+	int				i = 1;
+
+	// make this socket broadcast capable
+	if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0)
+		return -1;
+	net_broadcastsocket = socket;
+
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_Broadcast (int socket, byte *buf, int len)
+{
+	int ret;
+
+	if (socket != net_broadcastsocket)
+	{
+		if (net_broadcastsocket != 0)
+			Sys_Error("Attempted to use multiple broadcasts sockets\n");
+		ret = UDP_MakeSocketBroadcastCapable (socket);
+		if (ret == -1)
+		{
+			Con_Printf("Unable to make socket broadcast capable\n");
+			return ret;
+		}
+	}
+
+	return UDP_Write (socket, buf, len, &broadcastaddr);
+}
+
+//=============================================================================
+
+int UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr)
+{
+	int ret;
+
+	ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
+	if (ret == -1 && errno == EWOULDBLOCK)
+		return 0;
+	return ret;
+}
+
+//=============================================================================
+
+char *UDP_AddrToString (struct qsockaddr *addr)
+{
+	static char buffer[22];
+	int haddr;
+
+	haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
+	sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port));
+	return buffer;
+}
+
+//=============================================================================
+
+int UDP_StringToAddr (char *string, struct qsockaddr *addr)
+{
+	int ha1, ha2, ha3, ha4, hp;
+	int ipaddr;
+
+	sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
+	ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
+
+	addr->sa_family = AF_INET;
+	((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
+	((struct sockaddr_in *)addr)->sin_port = htons(hp);
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_GetSocketAddr (int socket, struct qsockaddr *addr)
+{
+	int addrlen = sizeof(struct qsockaddr);
+	unsigned int a;
+
+	Q_memset(addr, 0, sizeof(struct qsockaddr));
+	getsockname(socket, (struct sockaddr *)addr, &addrlen);
+	a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
+	if (a == 0 || a == inet_addr("127.0.0.1"))
+		((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
+
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name)
+{
+	struct hostent *hostentry;
+
+	hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET);
+	if (hostentry)
+	{
+		Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
+		return 0;
+	}
+
+	Q_strcpy (name, UDP_AddrToString (addr));
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_GetAddrFromName(char *name, struct qsockaddr *addr)
+{
+	struct hostent *hostentry;
+
+	if (name[0] >= '0' && name[0] <= '9')
+		return PartialIPAddress (name, addr);
+	
+	hostentry = gethostbyname (name);
+	if (!hostentry)
+		return -1;
+
+	addr->sa_family = AF_INET;
+	((struct sockaddr_in *)addr)->sin_port = htons(net_hostport);	
+	((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0];
+
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
+{
+	if (addr1->sa_family != addr2->sa_family)
+		return -1;
+
+	if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr)
+		return -1;
+
+	if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port)
+		return 1;
+
+	return 0;
+}
+
+//=============================================================================
+
+int UDP_GetSocketPort (struct qsockaddr *addr)
+{
+	return ntohs(((struct sockaddr_in *)addr)->sin_port);
+}
+
+
+int UDP_SetSocketPort (struct qsockaddr *addr, int port)
+{
+	((struct sockaddr_in *)addr)->sin_port = htons(port);
+	return 0;
+}
+
+//=============================================================================
diff --git a/apps/plugins/sdl/progs/quake/net_udp.h b/apps/plugins/sdl/progs/quake/net_udp.h
new file mode 100644
index 0000000..7530301
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_udp.h
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_udp.h
+
+int  UDP_Init (void);
+void UDP_Shutdown (void);
+void UDP_Listen (qboolean state);
+int  UDP_OpenSocket (int port);
+int  UDP_CloseSocket (int socket);
+int  UDP_Connect (int socket, struct qsockaddr *addr);
+int  UDP_CheckNewConnections (void);
+int  UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr);
+int  UDP_Broadcast (int socket, byte *buf, int len);
+char *UDP_AddrToString (struct qsockaddr *addr);
+int  UDP_StringToAddr (char *string, struct qsockaddr *addr);
+int  UDP_GetSocketAddr (int socket, struct qsockaddr *addr);
+int  UDP_GetNameFromAddr (struct qsockaddr *addr, char *name);
+int  UDP_GetAddrFromName (char *name, struct qsockaddr *addr);
+int  UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
+int  UDP_GetSocketPort (struct qsockaddr *addr);
+int  UDP_SetSocketPort (struct qsockaddr *addr, int port);
diff --git a/apps/plugins/sdl/progs/quake/net_vcr.c b/apps/plugins/sdl/progs/quake/net_vcr.c
new file mode 100644
index 0000000..ba8f40d
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_vcr.c
@@ -0,0 +1,167 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_vcr.c
+
+#include "quakedef.h"
+#include "net_vcr.h"
+
+extern int vcrFile;
+
+// This is the playback portion of the VCR.  It reads the file produced
+// by the recorder and plays it back to the host.  The recording contains
+// everything necessary (events, timestamps, and data) to duplicate the game
+// from the viewpoint of everything above the network layer.
+
+static struct
+{
+	double	time;
+	int		op;
+	long	session;
+}	next;
+
+int VCR_Init (void)
+{
+	net_drivers[0].Init = VCR_Init;
+
+	net_drivers[0].SearchForHosts = VCR_SearchForHosts;
+	net_drivers[0].Connect = VCR_Connect;
+	net_drivers[0].CheckNewConnections = VCR_CheckNewConnections;
+	net_drivers[0].QGetMessage = VCR_GetMessage;
+	net_drivers[0].QSendMessage = VCR_SendMessage;
+	net_drivers[0].CanSendMessage = VCR_CanSendMessage;
+	net_drivers[0].Close = VCR_Close;
+	net_drivers[0].Shutdown = VCR_Shutdown;
+
+	Sys_FileRead(vcrFile, &next, sizeof(next));
+	return 0;
+}
+
+void VCR_ReadNext (void)
+{
+	if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0)
+	{
+		next.op = 255;
+		Sys_Error ("=== END OF PLAYBACK===\n");
+	}
+	if (next.op < 1 || next.op > VCR_MAX_MESSAGE)
+		Sys_Error ("VCR_ReadNext: bad op");
+}
+
+
+void VCR_Listen (qboolean state)
+{
+}
+
+
+void VCR_Shutdown (void)
+{
+}
+
+
+int VCR_GetMessage (qsocket_t *sock)
+{
+	int	ret;
+	
+	if (host_time != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata))
+		Sys_Error ("VCR missmatch");
+
+	Sys_FileRead(vcrFile, &ret, sizeof(int));
+	if (ret != 1)
+	{
+		VCR_ReadNext ();
+		return ret;
+	}
+
+	Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int));
+	Sys_FileRead(vcrFile, net_message.data, net_message.cursize);
+
+	VCR_ReadNext ();
+
+	return 1;
+}
+
+
+int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data)
+{
+	int	ret;
+
+	if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+		Sys_Error ("VCR missmatch");
+
+	Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+	VCR_ReadNext ();
+
+	return ret;
+}
+
+
+qboolean VCR_CanSendMessage (qsocket_t *sock)
+{
+	qboolean	ret;
+
+	if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata))
+		Sys_Error ("VCR missmatch");
+
+	Sys_FileRead(vcrFile, &ret, sizeof(int));
+
+	VCR_ReadNext ();
+
+	return ret;
+}
+
+
+void VCR_Close (qsocket_t *sock)
+{
+}
+
+
+void VCR_SearchForHosts (qboolean xmit)
+{
+}
+
+
+qsocket_t *VCR_Connect (char *host)
+{
+	return NULL;
+}
+
+
+qsocket_t *VCR_CheckNewConnections (void)
+{
+	qsocket_t	*sock;
+
+	if (host_time != next.time || next.op != VCR_OP_CONNECT)
+		Sys_Error ("VCR missmatch");
+
+	if (!next.session)
+	{
+		VCR_ReadNext ();
+		return NULL;
+	}
+
+	sock = NET_NewQSocket ();
+	*(long *)(&sock->driverdata) = next.session;
+
+	Sys_FileRead (vcrFile, sock->address, NET_NAMELEN);
+	VCR_ReadNext ();
+
+	return sock;
+}
diff --git a/apps/plugins/sdl/progs/quake/net_vcr.h b/apps/plugins/sdl/progs/quake/net_vcr.h
new file mode 100644
index 0000000..95c2f34
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_vcr.h
@@ -0,0 +1,37 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// net_vcr.h
+
+#define VCR_OP_CONNECT					1
+#define VCR_OP_GETMESSAGE				2
+#define VCR_OP_SENDMESSAGE				3
+#define VCR_OP_CANSENDMESSAGE			4
+#define VCR_MAX_MESSAGE					4
+
+int			VCR_Init (void);
+void		VCR_Listen (qboolean state);
+void		VCR_SearchForHosts (qboolean xmit);
+qsocket_t 	*VCR_Connect (char *host);
+qsocket_t 	*VCR_CheckNewConnections (void);
+int			VCR_GetMessage (qsocket_t *sock);
+int			VCR_SendMessage (qsocket_t *sock, sizebuf_t *data);
+qboolean	VCR_CanSendMessage (qsocket_t *sock);
+void		VCR_Close (qsocket_t *sock);
+void		VCR_Shutdown (void);
diff --git a/apps/plugins/sdl/progs/quake/net_wso.c b/apps/plugins/sdl/progs/quake/net_wso.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/net_wso.c
diff --git a/apps/plugins/sdl/progs/quake/nonintel.c b/apps/plugins/sdl/progs/quake/nonintel.c
new file mode 100644
index 0000000..9e59039
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/nonintel.c
@@ -0,0 +1,64 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// nonintel.c: code for non-Intel processors only
+//
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+#if	!id386
+
+/*
+================
+R_Surf8Patch
+================
+*/
+void R_Surf8Patch ()
+{
+	// we only patch code on Intel
+}
+
+
+/*
+================
+R_Surf16Patch
+================
+*/
+void R_Surf16Patch ()
+{
+	// we only patch code on Intel
+}
+
+
+/*
+================
+R_SurfacePatch
+================
+*/
+void R_SurfacePatch (void)
+{
+	// we only patch code on Intel
+}
+
+
+#endif	// !id386
+
diff --git a/apps/plugins/sdl/progs/quake/pr_cmds.c b/apps/plugins/sdl/progs/quake/pr_cmds.c
new file mode 100644
index 0000000..9036afa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/pr_cmds.c
@@ -0,0 +1,1936 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+#define	RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
+
+/*
+===============================================================================
+
+						BUILT-IN FUNCTIONS
+
+===============================================================================
+*/
+
+char *PF_VarString (int	first)
+{
+	int		i;
+	static char out[256];
+	
+	out[0] = 0;
+	for (i=first ; i<pr_argc ; i++)
+	{
+		strcat (out, G_STRING((OFS_PARM0+i*3)));
+	}
+	return out;
+}
+
+
+/*
+=================
+PF_errror
+
+This is a TERMINAL error, which will kill off the entire server.
+Dumps self.
+
+error(value)
+=================
+*/
+void PF_error (void)
+{
+	char	*s;
+	edict_t	*ed;
+	
+	s = PF_VarString(0);
+	Con_Printf ("======SERVER ERROR in %s:\n%s\n"
+	,pr_strings + pr_xfunction->s_name,s);
+	ed = PROG_TO_EDICT(pr_global_struct->self);
+	ED_Print (ed);
+
+	Host_Error ("Program error");
+}
+
+/*
+=================
+PF_objerror
+
+Dumps out self, then an error message.  The program is aborted and self is
+removed, but the level can continue.
+
+objerror(value)
+=================
+*/
+void PF_objerror (void)
+{
+	char	*s;
+	edict_t	*ed;
+	
+	s = PF_VarString(0);
+	Con_Printf ("======OBJECT ERROR in %s:\n%s\n"
+	,pr_strings + pr_xfunction->s_name,s);
+	ed = PROG_TO_EDICT(pr_global_struct->self);
+	ED_Print (ed);
+	ED_Free (ed);
+	
+	Host_Error ("Program error");
+}
+
+
+
+/*
+==============
+PF_makevectors
+
+Writes new values for v_forward, v_up, and v_right based on angles
+makevectors(vector)
+==============
+*/
+void PF_makevectors (void)
+{
+	AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
+}
+
+/*
+=================
+PF_setorigin
+
+This is the only valid way to move an object without using the physics of the world (setting velocity and waiting).  Directly changing origin will not set internal links correctly, so clipping would be messed up.  This should be called when an object is spawned, and then only if it is teleported.
+
+setorigin (entity, origin)
+=================
+*/
+void PF_setorigin (void)
+{
+    
+	edict_t	*e;
+	float	*org;
+	
+	e = G_EDICT(OFS_PARM0);
+	org = G_VECTOR(OFS_PARM1);
+        //printf("SetOrigin %f %f %f", org[0], org[1], org[2]);
+	VectorCopy (org, e->v.origin);
+	SV_LinkEdict (e, false);
+}
+
+
+void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
+{
+	float	*angles;
+	vec3_t	rmin, rmax;
+	float	bounds[2][3];
+	float	xvector[2], yvector[2];
+	float	a;
+	vec3_t	base, transformed;
+	int		i, j, k, l;
+	
+	for (i=0 ; i<3 ; i++)
+		if (min[i] > max[i])
+			PR_RunError ("backwards mins/maxs");
+
+	rotate = false;		// FIXME: implement rotation properly again
+
+	if (!rotate)
+	{
+		VectorCopy (min, rmin);
+		VectorCopy (max, rmax);
+	}
+	else
+	{
+	// find min / max for rotations
+		angles = e->v.angles;
+		
+		a = angles[1]/180 * M_PI;
+		
+		xvector[0] = cos(a);
+		xvector[1] = sin(a);
+		yvector[0] = -sin(a);
+		yvector[1] = cos(a);
+		
+		VectorCopy (min, bounds[0]);
+		VectorCopy (max, bounds[1]);
+		
+		rmin[0] = rmin[1] = rmin[2] = 9999;
+		rmax[0] = rmax[1] = rmax[2] = -9999;
+		
+		for (i=0 ; i<= 1 ; i++)
+		{
+			base[0] = bounds[i][0];
+			for (j=0 ; j<= 1 ; j++)
+			{
+				base[1] = bounds[j][1];
+				for (k=0 ; k<= 1 ; k++)
+				{
+					base[2] = bounds[k][2];
+					
+				// transform the point
+					transformed[0] = xvector[0]*base[0] + yvector[0]*base[1];
+					transformed[1] = xvector[1]*base[0] + yvector[1]*base[1];
+					transformed[2] = base[2];
+					
+					for (l=0 ; l<3 ; l++)
+					{
+						if (transformed[l] < rmin[l])
+							rmin[l] = transformed[l];
+						if (transformed[l] > rmax[l])
+							rmax[l] = transformed[l];
+					}
+				}
+			}
+		}
+	}
+	
+// set derived values
+	VectorCopy (rmin, e->v.mins);
+	VectorCopy (rmax, e->v.maxs);
+	VectorSubtract (max, min, e->v.size);
+	
+	SV_LinkEdict (e, false);
+}
+
+/*
+=================
+PF_setsize
+
+the size box is rotated by the current angle
+
+setsize (entity, minvector, maxvector)
+=================
+*/
+void PF_setsize (void)
+{
+	edict_t	*e;
+	float	*min, *max;
+	
+	e = G_EDICT(OFS_PARM0);
+	min = G_VECTOR(OFS_PARM1);
+	max = G_VECTOR(OFS_PARM2);
+	SetMinMaxSize (e, min, max, false);
+}
+
+
+/*
+=================
+PF_setmodel
+
+setmodel(entity, model)
+=================
+*/
+void PF_setmodel (void)
+{
+	edict_t	*e;
+	char	*m, **check;
+	model_t	*mod;
+	int		i;
+
+	e = G_EDICT(OFS_PARM0);
+	m = G_STRING(OFS_PARM1);
+
+// check to see if model was properly precached
+	for (i=0, check = sv.model_precache ; *check ; i++, check++)
+		if (!strcmp(*check, m))
+			break;
+			
+	if (!*check)
+		PR_RunError ("no precache: %s\n", m);
+		
+
+	e->v.model = m - pr_strings;
+	e->v.modelindex = i; //SV_ModelIndex (m);
+
+	mod = sv.models[ (int)e->v.modelindex];  // Mod_ForName (m, true);
+	
+	if (mod)
+		SetMinMaxSize (e, mod->mins, mod->maxs, true);
+	else
+		SetMinMaxSize (e, vec3_origin, vec3_origin, true);
+}
+
+/*
+=================
+PF_bprint
+
+broadcast print to everyone on server
+
+bprint(value)
+=================
+*/
+void PF_bprint (void)
+{
+	char		*s;
+
+	s = PF_VarString(0);
+	SV_BroadcastPrintf ("%s", s);
+}
+
+/*
+=================
+PF_sprint
+
+single print to a specific client
+
+sprint(clientent, value)
+=================
+*/
+void PF_sprint (void)
+{
+	char		*s;
+	client_t	*client;
+	int			entnum;
+	
+	entnum = G_EDICTNUM(OFS_PARM0);
+	s = PF_VarString(1);
+	
+	if (entnum < 1 || entnum > svs.maxclients)
+	{
+		Con_Printf ("tried to sprint to a non-client\n");
+		return;
+	}
+		
+	client = &svs.clients[entnum-1];
+		
+	MSG_WriteChar (&client->message,svc_print);
+	MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_centerprint
+
+single print to a specific client
+
+centerprint(clientent, value)
+=================
+*/
+void PF_centerprint (void)
+{
+	char		*s;
+	client_t	*client;
+	int			entnum;
+	
+	entnum = G_EDICTNUM(OFS_PARM0);
+	s = PF_VarString(1);
+	
+	if (entnum < 1 || entnum > svs.maxclients)
+	{
+		Con_Printf ("tried to sprint to a non-client\n");
+		return;
+	}
+		
+	client = &svs.clients[entnum-1];
+		
+	MSG_WriteChar (&client->message,svc_centerprint);
+	MSG_WriteString (&client->message, s );
+}
+
+
+/*
+=================
+PF_normalize
+
+vector normalize(vector)
+=================
+*/
+void PF_normalize (void)
+{
+	float	*value1;
+	vec3_t	newvalue;
+	float	new;
+	
+	value1 = G_VECTOR(OFS_PARM0);
+
+	new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+	new = sqrt(new);
+	
+	if (new == 0)
+		newvalue[0] = newvalue[1] = newvalue[2] = 0;
+	else
+	{
+		new = 1/new;
+		newvalue[0] = value1[0] * new;
+		newvalue[1] = value1[1] * new;
+		newvalue[2] = value1[2] * new;
+	}
+	
+	VectorCopy (newvalue, G_VECTOR(OFS_RETURN));	
+}
+
+/*
+=================
+PF_vlen
+
+scalar vlen(vector)
+=================
+*/
+void PF_vlen (void)
+{
+	float	*value1;
+	float	new;
+	
+	value1 = G_VECTOR(OFS_PARM0);
+
+	new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
+	new = sqrt(new);
+	
+	G_FLOAT(OFS_RETURN) = new;
+}
+
+/*
+=================
+PF_vectoyaw
+
+float vectoyaw(vector)
+=================
+*/
+void PF_vectoyaw (void)
+{
+	float	*value1;
+	float	yaw;
+	
+	value1 = G_VECTOR(OFS_PARM0);
+
+	if (value1[1] == 0 && value1[0] == 0)
+		yaw = 0;
+	else
+	{
+		yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
+		if (yaw < 0)
+			yaw += 360;
+	}
+
+	G_FLOAT(OFS_RETURN) = yaw;
+}
+
+
+/*
+=================
+PF_vectoangles
+
+vector vectoangles(vector)
+=================
+*/
+void PF_vectoangles (void)
+{
+	float	*value1;
+	float	forward;
+	float	yaw, pitch;
+	
+	value1 = G_VECTOR(OFS_PARM0);
+
+	if (value1[1] == 0 && value1[0] == 0)
+	{
+		yaw = 0;
+		if (value1[2] > 0)
+			pitch = 90;
+		else
+			pitch = 270;
+	}
+	else
+	{
+		yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
+		if (yaw < 0)
+			yaw += 360;
+
+		forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
+		pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
+		if (pitch < 0)
+			pitch += 360;
+	}
+
+	G_FLOAT(OFS_RETURN+0) = pitch;
+	G_FLOAT(OFS_RETURN+1) = yaw;
+	G_FLOAT(OFS_RETURN+2) = 0;
+}
+
+/*
+=================
+PF_Random
+
+Returns a number from 0<= num < 1
+
+random()
+=================
+*/
+void PF_random (void)
+{
+	float		num;
+		
+	num = (rand ()&0x7fff) / ((float)0x7fff);
+	
+	G_FLOAT(OFS_RETURN) = num;
+}
+
+/*
+=================
+PF_particle
+
+particle(origin, color, count)
+=================
+*/
+void PF_particle (void)
+{
+	float		*org, *dir;
+	float		color;
+	float		count;
+			
+	org = G_VECTOR(OFS_PARM0);
+	dir = G_VECTOR(OFS_PARM1);
+	color = G_FLOAT(OFS_PARM2);
+	count = G_FLOAT(OFS_PARM3);
+	SV_StartParticle (org, dir, color, count);
+}
+
+
+/*
+=================
+PF_ambientsound
+
+=================
+*/
+void PF_ambientsound (void)
+{
+	char		**check;
+	char		*samp;
+	float		*pos;
+	float 		vol, attenuation;
+	int			i, soundnum;
+
+	pos = G_VECTOR (OFS_PARM0);			
+	samp = G_STRING(OFS_PARM1);
+	vol = G_FLOAT(OFS_PARM2);
+	attenuation = G_FLOAT(OFS_PARM3);
+	
+// check to see if samp was properly precached
+	for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
+		if (!strcmp(*check,samp))
+			break;
+			
+	if (!*check)
+	{
+		Con_Printf ("no precache: %s\n", samp);
+		return;
+	}
+
+// add an svc_spawnambient command to the level signon packet
+
+	MSG_WriteByte (&sv.signon,svc_spawnstaticsound);
+	for (i=0 ; i<3 ; i++)
+		MSG_WriteCoord(&sv.signon, pos[i]);
+
+	MSG_WriteByte (&sv.signon, soundnum);
+
+	MSG_WriteByte (&sv.signon, vol*255);
+	MSG_WriteByte (&sv.signon, attenuation*64);
+
+}
+
+/*
+=================
+PF_sound
+
+Each entity can have eight independant sound sources, like voice,
+weapon, feet, etc.
+
+Channel 0 is an auto-allocate channel, the others override anything
+allready running on that entity/channel pair.
+
+An attenuation of 0 will play full volume everywhere in the level.
+Larger attenuations will drop off.
+
+=================
+*/
+void PF_sound (void)
+{
+	char		*sample;
+	int			channel;
+	edict_t		*entity;
+	int 		volume;
+	float attenuation;
+		
+	entity = G_EDICT(OFS_PARM0);
+	channel = G_FLOAT(OFS_PARM1);
+	sample = G_STRING(OFS_PARM2);
+	volume = G_FLOAT(OFS_PARM3) * 255;
+	attenuation = G_FLOAT(OFS_PARM4);
+	
+	if (volume < 0 || volume > 255)
+		Sys_Error ("SV_StartSound: volume = %i", volume);
+
+	if (attenuation < 0 || attenuation > 4)
+		Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
+
+	if (channel < 0 || channel > 7)
+		Sys_Error ("SV_StartSound: channel = %i", channel);
+
+	SV_StartSound (entity, channel, sample, volume, attenuation);
+}
+
+/*
+=================
+PF_break
+
+break()
+=================
+*/
+void PF_break (void)
+{
+Con_Printf ("break statement\n");
+*(int *)-4 = 0;	// dump to debugger
+//	PR_RunError ("break statement");
+}
+
+/*
+=================
+PF_traceline
+
+Used for use tracing and shot targeting
+Traces are blocked by bbox and exact bsp entityes, and also slide box entities
+if the tryents flag is set.
+
+traceline (vector1, vector2, tryents)
+=================
+*/
+void PF_traceline (void)
+{
+	float	*v1, *v2;
+	trace_t	trace;
+	int		nomonsters;
+	edict_t	*ent;
+
+	v1 = G_VECTOR(OFS_PARM0);
+	v2 = G_VECTOR(OFS_PARM1);
+	nomonsters = G_FLOAT(OFS_PARM2);
+	ent = G_EDICT(OFS_PARM3);
+
+	trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
+
+	pr_global_struct->trace_allsolid = trace.allsolid;
+	pr_global_struct->trace_startsolid = trace.startsolid;
+	pr_global_struct->trace_fraction = trace.fraction;
+	pr_global_struct->trace_inwater = trace.inwater;
+	pr_global_struct->trace_inopen = trace.inopen;
+	VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+	VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+	pr_global_struct->trace_plane_dist =  trace.plane.dist;	
+	if (trace.ent)
+		pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+	else
+		pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+
+
+#ifdef QUAKE2
+extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore);
+
+void PF_TraceToss (void)
+{
+	trace_t	trace;
+	edict_t	*ent;
+	edict_t	*ignore;
+
+	ent = G_EDICT(OFS_PARM0);
+	ignore = G_EDICT(OFS_PARM1);
+
+	trace = SV_Trace_Toss (ent, ignore);
+
+	pr_global_struct->trace_allsolid = trace.allsolid;
+	pr_global_struct->trace_startsolid = trace.startsolid;
+	pr_global_struct->trace_fraction = trace.fraction;
+	pr_global_struct->trace_inwater = trace.inwater;
+	pr_global_struct->trace_inopen = trace.inopen;
+	VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
+	VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
+	pr_global_struct->trace_plane_dist =  trace.plane.dist;	
+	if (trace.ent)
+		pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
+	else
+		pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
+}
+#endif
+
+
+/*
+=================
+PF_checkpos
+
+Returns true if the given entity can move to the given position from it's
+current position by walking or rolling.
+FIXME: make work...
+scalar checkpos (entity, vector)
+=================
+*/
+void PF_checkpos (void)
+{
+}
+
+//============================================================================
+
+byte	checkpvs[MAX_MAP_LEAFS/8];
+
+int PF_newcheckclient (int check)
+{
+	int		i;
+	byte	*pvs;
+	edict_t	*ent;
+	mleaf_t	*leaf;
+	vec3_t	org;
+
+// cycle to the next one
+
+	if (check < 1)
+		check = 1;
+	if (check > svs.maxclients)
+		check = svs.maxclients;
+
+	if (check == svs.maxclients)
+		i = 1;
+	else
+		i = check + 1;
+
+	for ( ;  ; i++)
+	{
+		if (i == svs.maxclients+1)
+			i = 1;
+
+		ent = EDICT_NUM(i);
+
+		if (i == check)
+			break;	// didn't find anything else
+
+		if (ent->free)
+			continue;
+		if (ent->v.health <= 0)
+			continue;
+		if ((int)ent->v.flags & FL_NOTARGET)
+			continue;
+
+	// anything that is a client, or has a client as an enemy
+		break;
+	}
+
+// get the PVS for the entity
+	VectorAdd (ent->v.origin, ent->v.view_ofs, org);
+	leaf = Mod_PointInLeaf (org, sv.worldmodel);
+	pvs = Mod_LeafPVS (leaf, sv.worldmodel);
+	memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 );
+
+	return i;
+}
+
+/*
+=================
+PF_checkclient
+
+Returns a client (or object that has a client enemy) that would be a
+valid target.
+
+If there are more than one valid options, they are cycled each frame
+
+If (self.origin + self.viewofs) is not in the PVS of the current target,
+it is not returned at all.
+
+name checkclient ()
+=================
+*/
+#define	MAX_CHECK	16
+int c_invis, c_notvis;
+void PF_checkclient (void)
+{
+	edict_t	*ent, *self;
+	mleaf_t	*leaf;
+	int		l;
+	vec3_t	view;
+	
+// find a new check if on a new frame
+	if (sv.time - sv.lastchecktime >= 0.1)
+	{
+		sv.lastcheck = PF_newcheckclient (sv.lastcheck);
+		sv.lastchecktime = sv.time;
+	}
+
+// return check if it might be visible	
+	ent = EDICT_NUM(sv.lastcheck);
+	if (ent->free || ent->v.health <= 0)
+	{
+		RETURN_EDICT(sv.edicts);
+		return;
+	}
+
+// if current entity can't possibly see the check entity, return 0
+	self = PROG_TO_EDICT(pr_global_struct->self);
+	VectorAdd (self->v.origin, self->v.view_ofs, view);
+	leaf = Mod_PointInLeaf (view, sv.worldmodel);
+	l = (leaf - sv.worldmodel->leafs) - 1;
+	if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
+	{
+c_notvis++;
+		RETURN_EDICT(sv.edicts);
+		return;
+	}
+
+// might be able to see it
+c_invis++;
+	RETURN_EDICT(ent);
+}
+
+//============================================================================
+
+
+/*
+=================
+PF_stuffcmd
+
+Sends text over to the client's execution buffer
+
+stuffcmd (clientent, value)
+=================
+*/
+void PF_stuffcmd (void)
+{
+	int		entnum;
+	char	*str;
+	client_t	*old;
+	
+	entnum = G_EDICTNUM(OFS_PARM0);
+	if (entnum < 1 || entnum > svs.maxclients)
+		PR_RunError ("Parm 0 not a client");
+	str = G_STRING(OFS_PARM1);	
+	
+	old = host_client;
+	host_client = &svs.clients[entnum-1];
+	Host_ClientCommands ("%s", str);
+	host_client = old;
+}
+
+/*
+=================
+PF_localcmd
+
+Sends text over to the client's execution buffer
+
+localcmd (string)
+=================
+*/
+void PF_localcmd (void)
+{
+	char	*str;
+	
+	str = G_STRING(OFS_PARM0);	
+	Cbuf_AddText (str);
+}
+
+/*
+=================
+PF_cvar
+
+float cvar (string)
+=================
+*/
+void PF_cvar (void)
+{
+	char	*str;
+	
+	str = G_STRING(OFS_PARM0);
+	
+	G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
+}
+
+/*
+=================
+PF_cvar_set
+
+float cvar (string)
+=================
+*/
+void PF_cvar_set (void)
+{
+	char	*var, *val;
+	
+	var = G_STRING(OFS_PARM0);
+	val = G_STRING(OFS_PARM1);
+	
+	Cvar_Set (var, val);
+}
+
+/*
+=================
+PF_findradius
+
+Returns a chain of entities that have origins within a spherical area
+
+findradius (origin, radius)
+=================
+*/
+void PF_findradius (void)
+{
+	edict_t	*ent, *chain;
+	float	rad;
+	float	*org;
+	vec3_t	eorg;
+	int		i, j;
+
+	chain = (edict_t *)sv.edicts;
+	
+	org = G_VECTOR(OFS_PARM0);
+	rad = G_FLOAT(OFS_PARM1);
+
+	ent = NEXT_EDICT(sv.edicts);
+	for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+	{
+		if (ent->free)
+			continue;
+		if (ent->v.solid == SOLID_NOT)
+			continue;
+		for (j=0 ; j<3 ; j++)
+			eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);			
+		if (Length(eorg) > rad)
+			continue;
+			
+		ent->v.chain = EDICT_TO_PROG(chain);
+		chain = ent;
+	}
+
+	RETURN_EDICT(chain);
+}
+
+
+/*
+=========
+PF_dprint
+=========
+*/
+void PF_dprint (void)
+{
+	Con_DPrintf ("%s",PF_VarString(0));
+}
+
+char	pr_string_temp[128];
+
+void PF_ftos (void)
+{
+	float	v;
+	v = G_FLOAT(OFS_PARM0);
+	
+	if (v == (int)v)
+		sprintf (pr_string_temp, "%d",(int)v);
+	else
+		sprintf (pr_string_temp, "%5.1f",v);
+	G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+void PF_fabs (void)
+{
+	float	v;
+	v = G_FLOAT(OFS_PARM0);
+	G_FLOAT(OFS_RETURN) = fabs(v);
+}
+
+void PF_vtos (void)
+{
+	sprintf (pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
+	G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+
+#ifdef QUAKE2
+void PF_etos (void)
+{
+	sprintf (pr_string_temp, "entity %i", G_EDICTNUM(OFS_PARM0));
+	G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
+}
+#endif
+
+void PF_Spawn (void)
+{
+	edict_t	*ed;
+	ed = ED_Alloc();
+	RETURN_EDICT(ed);
+}
+
+void PF_Remove (void)
+{
+	edict_t	*ed;
+	
+	ed = G_EDICT(OFS_PARM0);
+	ED_Free (ed);
+}
+
+
+// entity (entity start, .string field, string match) find = #5;
+void PF_Find (void)
+#ifdef QUAKE2
+{
+	int		e;	
+	int		f;
+	char	*s, *t;
+	edict_t	*ed;
+	edict_t	*first;
+	edict_t	*second;
+	edict_t	*last;
+
+	first = second = last = (edict_t *)sv.edicts;
+	e = G_EDICTNUM(OFS_PARM0);
+	f = G_INT(OFS_PARM1);
+	s = G_STRING(OFS_PARM2);
+	if (!s)
+		PR_RunError ("PF_Find: bad search string");
+		
+	for (e++ ; e < sv.num_edicts ; e++)
+	{
+		ed = EDICT_NUM(e);
+		if (ed->free)
+			continue;
+		t = E_STRING(ed,f);
+		if (!t)
+			continue;
+		if (!strcmp(t,s))
+		{
+			if (first == (edict_t *)sv.edicts)
+				first = ed;
+			else if (second == (edict_t *)sv.edicts)
+				second = ed;
+			ed->v.chain = EDICT_TO_PROG(last);
+			last = ed;
+		}
+	}
+
+	if (first != last)
+	{
+		if (last != second)
+			first->v.chain = last->v.chain;
+		else
+			first->v.chain = EDICT_TO_PROG(last);
+		last->v.chain = EDICT_TO_PROG((edict_t *)sv.edicts);
+		if (second && second != last)
+			second->v.chain = EDICT_TO_PROG(last);
+	}
+	RETURN_EDICT(first);
+}
+#else
+{
+	int		e;	
+	int		f;
+	char	*s, *t;
+	edict_t	*ed;
+
+	e = G_EDICTNUM(OFS_PARM0);
+	f = G_INT(OFS_PARM1);
+	s = G_STRING(OFS_PARM2);
+	if (!s)
+		PR_RunError ("PF_Find: bad search string");
+		
+	for (e++ ; e < sv.num_edicts ; e++)
+	{
+		ed = EDICT_NUM(e);
+		if (ed->free)
+			continue;
+		t = E_STRING(ed,f);
+		if (!t)
+			continue;
+		if (!strcmp(t,s))
+		{
+			RETURN_EDICT(ed);
+			return;
+		}
+	}
+
+	RETURN_EDICT(sv.edicts);
+}
+#endif
+
+void PR_CheckEmptyString (char *s)
+{
+	if (s[0] <= ' ')
+		PR_RunError ("Bad string");
+}
+
+void PF_precache_file (void)
+{	// precache_file is only used to copy files with qcc, it does nothing
+	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+}
+
+void PF_precache_sound (void)
+{
+	char	*s;
+	int		i;
+	
+	if (sv.state != ss_loading)
+		PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+		
+	s = G_STRING(OFS_PARM0);
+	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+	PR_CheckEmptyString (s);
+	
+	for (i=0 ; i<MAX_SOUNDS ; i++)
+	{
+		if (!sv.sound_precache[i])
+		{
+			sv.sound_precache[i] = s;
+			return;
+		}
+		if (!strcmp(sv.sound_precache[i], s))
+			return;
+	}
+	PR_RunError ("PF_precache_sound: overflow");
+}
+
+void PF_precache_model (void)
+{
+	char	*s;
+	int		i;
+	
+	if (sv.state != ss_loading)
+		PR_RunError ("PF_Precache_*: Precache can only be done in spawn functions");
+		
+	s = G_STRING(OFS_PARM0);
+	G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
+	PR_CheckEmptyString (s);
+
+	for (i=0 ; i<MAX_MODELS ; i++)
+	{
+		if (!sv.model_precache[i])
+		{
+			sv.model_precache[i] = s;
+			sv.models[i] = Mod_ForName (s, true);
+			return;
+		}
+		if (!strcmp(sv.model_precache[i], s))
+			return;
+	}
+	PR_RunError ("PF_precache_model: overflow");
+}
+
+
+void PF_coredump (void)
+{
+	ED_PrintEdicts ();
+}
+
+void PF_traceon (void)
+{
+	pr_trace = true;
+}
+
+void PF_traceoff (void)
+{
+	pr_trace = false;
+}
+
+void PF_eprint (void)
+{
+	ED_PrintNum (G_EDICTNUM(OFS_PARM0));
+}
+
+/*
+===============
+PF_walkmove
+
+float(float yaw, float dist) walkmove
+===============
+*/
+void PF_walkmove (void)
+{
+	edict_t	*ent;
+	float	yaw, dist;
+	vec3_t	move;
+	dfunction_t	*oldf;
+	int 	oldself;
+	
+	ent = PROG_TO_EDICT(pr_global_struct->self);
+	yaw = G_FLOAT(OFS_PARM0);
+	dist = G_FLOAT(OFS_PARM1);
+	
+	if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+	{
+		G_FLOAT(OFS_RETURN) = 0;
+		return;
+	}
+
+	yaw = yaw*M_PI*2 / 360;
+	
+	move[0] = cos(yaw)*dist;
+	move[1] = sin(yaw)*dist;
+	move[2] = 0;
+
+// save program state, because SV_movestep may call other progs
+	oldf = pr_xfunction;
+	oldself = pr_global_struct->self;
+	
+	G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true);
+	
+	
+// restore program state
+	pr_xfunction = oldf;
+	pr_global_struct->self = oldself;
+}
+
+/*
+===============
+PF_droptofloor
+
+void() droptofloor
+===============
+*/
+void PF_droptofloor (void)
+{
+	edict_t		*ent;
+	vec3_t		end;
+	trace_t		trace;
+	
+	ent = PROG_TO_EDICT(pr_global_struct->self);
+
+	VectorCopy (ent->v.origin, end);
+	end[2] -= 256;
+	
+	trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+	if (trace.fraction == 1 || trace.allsolid)
+		G_FLOAT(OFS_RETURN) = 0;
+	else
+	{
+		VectorCopy (trace.endpos, ent->v.origin);
+		SV_LinkEdict (ent, false);
+		ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+		ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+		G_FLOAT(OFS_RETURN) = 1;
+	}
+}
+
+/*
+===============
+PF_lightstyle
+
+void(float style, string value) lightstyle
+===============
+*/
+void PF_lightstyle (void)
+{
+	int		style;
+	char	*val;
+	client_t	*client;
+	int			j;
+	
+	style = G_FLOAT(OFS_PARM0);
+	val = G_STRING(OFS_PARM1);
+
+// change the string in sv
+	sv.lightstyles[style] = val;
+	
+// send message to all clients on this server
+	if (sv.state != ss_active)
+		return;
+	
+	for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+		if (client->active || client->spawned)
+		{
+			MSG_WriteChar (&client->message, svc_lightstyle);
+			MSG_WriteChar (&client->message,style);
+			MSG_WriteString (&client->message, val);
+		}
+}
+
+void PF_rint (void)
+{
+	float	f;
+	f = G_FLOAT(OFS_PARM0);
+	if (f > 0)
+		G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
+	else
+		G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
+}
+void PF_floor (void)
+{
+	G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));
+}
+void PF_ceil (void)
+{
+	G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));
+}
+
+
+/*
+=============
+PF_checkbottom
+=============
+*/
+void PF_checkbottom (void)
+{
+	edict_t	*ent;
+	
+	ent = G_EDICT(OFS_PARM0);
+
+	G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent);
+}
+
+/*
+=============
+PF_pointcontents
+=============
+*/
+void PF_pointcontents (void)
+{
+	float	*v;
+	
+	v = G_VECTOR(OFS_PARM0);
+
+	G_FLOAT(OFS_RETURN) = SV_PointContents (v);	
+}
+
+/*
+=============
+PF_nextent
+
+entity nextent(entity)
+=============
+*/
+void PF_nextent (void)
+{
+	int		i;
+	edict_t	*ent;
+	
+	i = G_EDICTNUM(OFS_PARM0);
+	while (1)
+	{
+		i++;
+		if (i == sv.num_edicts)
+		{
+			RETURN_EDICT(sv.edicts);
+			return;
+		}
+		ent = EDICT_NUM(i);
+		if (!ent->free)
+		{
+			RETURN_EDICT(ent);
+			return;
+		}
+	}
+}
+
+/*
+=============
+PF_aim
+
+Pick a vector for the player to shoot along
+vector aim(entity, missilespeed)
+=============
+*/
+cvar_t	sv_aim = {"sv_aim", "0.93"};
+void PF_aim (void)
+{
+	edict_t	*ent, *check, *bestent;
+	vec3_t	start, dir, end, bestdir;
+	int		i, j;
+	trace_t	tr;
+	float	dist, bestdist;
+	float	speed;
+	
+	ent = G_EDICT(OFS_PARM0);
+	speed = G_FLOAT(OFS_PARM1);
+
+	VectorCopy (ent->v.origin, start);
+	start[2] += 20;
+
+// try sending a trace straight
+	VectorCopy (pr_global_struct->v_forward, dir);
+	VectorMA (start, 2048, dir, end);
+	tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+	if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM
+	&& (!teamplay.value || ent->v.team <=0 || ent->v.team != tr.ent->v.team) )
+	{
+		VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
+		return;
+	}
+
+
+// try all possible entities
+	VectorCopy (dir, bestdir);
+	bestdist = sv_aim.value;
+	bestent = NULL;
+	
+	check = NEXT_EDICT(sv.edicts);
+	for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) )
+	{
+		if (check->v.takedamage != DAMAGE_AIM)
+			continue;
+		if (check == ent)
+			continue;
+		if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team)
+			continue;	// don't aim at teammate
+		for (j=0 ; j<3 ; j++)
+			end[j] = check->v.origin[j]
+			+ 0.5*(check->v.mins[j] + check->v.maxs[j]);
+		VectorSubtract (end, start, dir);
+		VectorNormalizeNoRet (dir);
+		dist = DotProduct (dir, pr_global_struct->v_forward);
+		if (dist < bestdist)
+			continue;	// to far to turn
+		tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent);
+		if (tr.ent == check)
+		{	// can shoot at this one
+			bestdist = dist;
+			bestent = check;
+		}
+	}
+	
+	if (bestent)
+	{
+		VectorSubtract (bestent->v.origin, ent->v.origin, dir);
+		dist = DotProduct (dir, pr_global_struct->v_forward);
+		VectorScale (pr_global_struct->v_forward, dist, end);
+		end[2] = dir[2];
+		VectorNormalizeNoRet (end);
+		VectorCopy (end, G_VECTOR(OFS_RETURN));	
+	}
+	else
+	{
+		VectorCopy (bestdir, G_VECTOR(OFS_RETURN));
+	}
+}
+
+/*
+==============
+PF_changeyaw
+
+This was a major timewaster in progs, so it was converted to C
+==============
+*/
+void PF_changeyaw (void)
+{
+	edict_t		*ent;
+	float		ideal, current, move, speed;
+	
+	ent = PROG_TO_EDICT(pr_global_struct->self);
+	current = anglemod( ent->v.angles[1] );
+	ideal = ent->v.ideal_yaw;
+	speed = ent->v.yaw_speed;
+	
+	if (current == ideal)
+		return;
+	move = ideal - current;
+	if (ideal > current)
+	{
+		if (move >= 180)
+			move = move - 360;
+	}
+	else
+	{
+		if (move <= -180)
+			move = move + 360;
+	}
+	if (move > 0)
+	{
+		if (move > speed)
+			move = speed;
+	}
+	else
+	{
+		if (move < -speed)
+			move = -speed;
+	}
+	
+	ent->v.angles[1] = anglemod (current + move);
+}
+
+#ifdef QUAKE2
+/*
+==============
+PF_changepitch
+==============
+*/
+void PF_changepitch (void)
+{
+	edict_t		*ent;
+	float		ideal, current, move, speed;
+	
+	ent = G_EDICT(OFS_PARM0);
+	current = anglemod( ent->v.angles[0] );
+	ideal = ent->v.idealpitch;
+	speed = ent->v.pitch_speed;
+	
+	if (current == ideal)
+		return;
+	move = ideal - current;
+	if (ideal > current)
+	{
+		if (move >= 180)
+			move = move - 360;
+	}
+	else
+	{
+		if (move <= -180)
+			move = move + 360;
+	}
+	if (move > 0)
+	{
+		if (move > speed)
+			move = speed;
+	}
+	else
+	{
+		if (move < -speed)
+			move = -speed;
+	}
+	
+	ent->v.angles[0] = anglemod (current + move);
+}
+#endif
+
+/*
+===============================================================================
+
+MESSAGE WRITING
+
+===============================================================================
+*/
+
+#define	MSG_BROADCAST	0		// unreliable to all
+#define	MSG_ONE			1		// reliable to one (msg_entity)
+#define	MSG_ALL			2		// reliable to all
+#define	MSG_INIT		3		// write to the init string
+
+sizebuf_t *WriteDest (void)
+{
+	int		entnum;
+	int		dest;
+	edict_t	*ent;
+
+	dest = G_FLOAT(OFS_PARM0);
+	switch (dest)
+	{
+	case MSG_BROADCAST:
+		return &sv.datagram;
+	
+	case MSG_ONE:
+		ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
+		entnum = NUM_FOR_EDICT(ent);
+		if (entnum < 1 || entnum > svs.maxclients)
+			PR_RunError ("WriteDest: not a client");
+		return &svs.clients[entnum-1].message;
+		
+	case MSG_ALL:
+		return &sv.reliable_datagram;
+	
+	case MSG_INIT:
+		return &sv.signon;
+
+	default:
+		PR_RunError ("WriteDest: bad destination");
+		break;
+	}
+	
+	return NULL;
+}
+
+void PF_WriteByte (void)
+{
+	MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteChar (void)
+{
+	MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteShort (void)
+{
+	MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteLong (void)
+{
+	MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteAngle (void)
+{
+	MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteCoord (void)
+{
+	MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1));
+}
+
+void PF_WriteString (void)
+{
+	MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
+}
+
+
+void PF_WriteEntity (void)
+{
+	MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
+}
+
+//=============================================================================
+
+int SV_ModelIndex (char *name);
+
+void PF_makestatic (void)
+{
+	edict_t	*ent;
+	int		i;
+	
+	ent = G_EDICT(OFS_PARM0);
+
+	MSG_WriteByte (&sv.signon,svc_spawnstatic);
+
+	MSG_WriteByte (&sv.signon, SV_ModelIndex(pr_strings + ent->v.model));
+
+	MSG_WriteByte (&sv.signon, ent->v.frame);
+	MSG_WriteByte (&sv.signon, ent->v.colormap);
+	MSG_WriteByte (&sv.signon, ent->v.skin);
+	for (i=0 ; i<3 ; i++)
+	{
+		MSG_WriteCoord(&sv.signon, ent->v.origin[i]);
+		MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
+	}
+
+// throw the entity away now
+	ED_Free (ent);
+}
+
+//=============================================================================
+
+/*
+==============
+PF_setspawnparms
+==============
+*/
+void PF_setspawnparms (void)
+{
+	edict_t	*ent;
+	int		i;
+	client_t	*client;
+
+	ent = G_EDICT(OFS_PARM0);
+	i = NUM_FOR_EDICT(ent);
+	if (i < 1 || i > svs.maxclients)
+		PR_RunError ("Entity is not a client");
+
+	// copy spawn parms out of the client_t
+	client = svs.clients + (i-1);
+
+	for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
+		(&pr_global_struct->parm1)[i] = client->spawn_parms[i];
+}
+
+/*
+==============
+PF_changelevel
+==============
+*/
+void PF_changelevel (void)
+{
+#ifdef QUAKE2
+	char	*s1, *s2;
+
+	if (svs.changelevel_issued)
+		return;
+	svs.changelevel_issued = true;
+
+	s1 = G_STRING(OFS_PARM0);
+	s2 = G_STRING(OFS_PARM1);
+
+	if ((int)pr_global_struct->serverflags & (SFL_NEW_UNIT | SFL_NEW_EPISODE))
+		Cbuf_AddText (va("changelevel %s %s\n",s1, s2));
+	else
+		Cbuf_AddText (va("changelevel2 %s %s\n",s1, s2));
+#else
+	char	*s;
+
+// make sure we don't issue two changelevels
+	if (svs.changelevel_issued)
+		return;
+	svs.changelevel_issued = true;
+	
+	s = G_STRING(OFS_PARM0);
+	Cbuf_AddText (va("changelevel %s\n",s));
+#endif
+}
+
+
+#ifdef QUAKE2
+
+#define	CONTENT_WATER	-3
+#define CONTENT_SLIME	-4
+#define CONTENT_LAVA	-5
+
+#define FL_IMMUNE_WATER	131072
+#define	FL_IMMUNE_SLIME	262144
+#define FL_IMMUNE_LAVA	524288
+
+#define	CHAN_VOICE	2
+#define	CHAN_BODY	4
+
+#define	ATTN_NORM	1
+
+void PF_WaterMove (void)
+{
+	edict_t		*self;
+	int			flags;
+	int			waterlevel;
+	int			watertype;
+	float		drownlevel;
+	float		damage = 0.0;
+
+	self = PROG_TO_EDICT(pr_global_struct->self);
+
+	if (self->v.movetype == MOVETYPE_NOCLIP)
+	{
+		self->v.air_finished = sv.time + 12;
+		G_FLOAT(OFS_RETURN) = damage;
+		return;
+	}
+
+	if (self->v.health < 0)
+	{
+		G_FLOAT(OFS_RETURN) = damage;
+		return;
+	}
+
+	if (self->v.deadflag == DEAD_NO)
+		drownlevel = 3;
+	else
+		drownlevel = 1;
+
+	flags = (int)self->v.flags;
+	waterlevel = (int)self->v.waterlevel;
+	watertype = (int)self->v.watertype;
+
+	if (!(flags & (FL_IMMUNE_WATER + FL_GODMODE)))
+		if (((flags & FL_SWIM) && (waterlevel < drownlevel)) || (waterlevel >= drownlevel))
+		{
+			if (self->v.air_finished < sv.time)
+				if (self->v.pain_finished < sv.time)
+				{
+					self->v.dmg = self->v.dmg + 2;
+					if (self->v.dmg > 15)
+						self->v.dmg = 10;
+//					T_Damage (self, world, world, self.dmg, 0, FALSE);
+					damage = self->v.dmg;
+					self->v.pain_finished = sv.time + 1.0;
+				}
+		}
+		else
+		{
+			if (self->v.air_finished < sv.time)
+//				sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
+				SV_StartSound (self, CHAN_VOICE, "player/gasp2.wav", 255, ATTN_NORM);
+			else if (self->v.air_finished < sv.time + 9)
+//				sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
+				SV_StartSound (self, CHAN_VOICE, "player/gasp1.wav", 255, ATTN_NORM);
+			self->v.air_finished = sv.time + 12.0;
+			self->v.dmg = 2;
+		}
+	
+	if (!waterlevel)
+	{
+		if (flags & FL_INWATER)
+		{	
+			// play leave water sound
+//			sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
+			SV_StartSound (self, CHAN_BODY, "misc/outwater.wav", 255, ATTN_NORM);
+			self->v.flags = (float)(flags &~FL_INWATER);
+		}
+		self->v.air_finished = sv.time + 12.0;
+		G_FLOAT(OFS_RETURN) = damage;
+		return;
+	}
+
+	if (watertype == CONTENT_LAVA)
+	{	// do damage
+		if (!(flags & (FL_IMMUNE_LAVA + FL_GODMODE)))
+			if (self->v.dmgtime < sv.time)
+			{
+				if (self->v.radsuit_finished < sv.time)
+					self->v.dmgtime = sv.time + 0.2;
+				else
+					self->v.dmgtime = sv.time + 1.0;
+//				T_Damage (self, world, world, 10*self.waterlevel, 0, TRUE);
+				damage = (float)(10*waterlevel);
+			}
+	}
+	else if (watertype == CONTENT_SLIME)
+	{	// do damage
+		if (!(flags & (FL_IMMUNE_SLIME + FL_GODMODE)))
+			if (self->v.dmgtime < sv.time && self->v.radsuit_finished < sv.time)
+			{
+				self->v.dmgtime = sv.time + 1.0;
+//				T_Damage (self, world, world, 4*self.waterlevel, 0, TRUE);
+				damage = (float)(4*waterlevel);
+			}
+	}
+	
+	if ( !(flags & FL_INWATER) )
+	{	
+
+// player enter water sound
+		if (watertype == CONTENT_LAVA)
+//			sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
+			SV_StartSound (self, CHAN_BODY, "player/inlava.wav", 255, ATTN_NORM);
+		if (watertype == CONTENT_WATER)
+//			sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
+			SV_StartSound (self, CHAN_BODY, "player/inh2o.wav", 255, ATTN_NORM);
+		if (watertype == CONTENT_SLIME)
+//			sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
+			SV_StartSound (self, CHAN_BODY, "player/slimbrn2.wav", 255, ATTN_NORM);
+
+		self->v.flags = (float)(flags | FL_INWATER);
+		self->v.dmgtime = 0;
+	}
+	
+	if (! (flags & FL_WATERJUMP) )
+	{
+//		self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;
+		VectorMA (self->v.velocity, -0.8 * self->v.waterlevel * host_frametime, self->v.velocity, self->v.velocity);
+	}
+
+	G_FLOAT(OFS_RETURN) = damage;
+}
+
+
+void PF_sin (void)
+{
+	G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0));
+}
+
+void PF_cos (void)
+{
+	G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0));
+}
+
+void PF_sqrt (void)
+{
+	G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));
+}
+#endif
+
+void PF_Fixme (void)
+{
+	PR_RunError ("unimplemented bulitin");
+}
+
+
+
+builtin_t pr_builtin[] =
+{
+PF_Fixme,
+PF_makevectors,	// void(entity e)	makevectors 		= #1;
+PF_setorigin,	// void(entity e, vector o) setorigin	= #2;
+PF_setmodel,	// void(entity e, string m) setmodel	= #3;
+PF_setsize,	// void(entity e, vector min, vector max) setsize = #4;
+PF_Fixme,	// void(entity e, vector min, vector max) setabssize = #5;
+PF_break,	// void() break						= #6;
+PF_random,	// float() random						= #7;
+PF_sound,	// void(entity e, float chan, string samp) sound = #8;
+PF_normalize,	// vector(vector v) normalize			= #9;
+PF_error,	// void(string e) error				= #10;
+PF_objerror,	// void(string e) objerror				= #11;
+PF_vlen,	// float(vector v) vlen				= #12;
+PF_vectoyaw,	// float(vector v) vectoyaw		= #13;
+PF_Spawn,	// entity() spawn						= #14;
+PF_Remove,	// void(entity e) remove				= #15;
+PF_traceline,	// float(vector v1, vector v2, float tryents) traceline = #16;
+PF_checkclient,	// entity() clientlist					= #17;
+PF_Find,	// entity(entity start, .string fld, string match) find = #18;
+PF_precache_sound,	// void(string s) precache_sound		= #19;
+PF_precache_model,	// void(string s) precache_model		= #20;
+PF_stuffcmd,	// void(entity client, string s)stuffcmd = #21;
+PF_findradius,	// entity(vector org, float rad) findradius = #22;
+PF_bprint,	// void(string s) bprint				= #23;
+PF_sprint,	// void(entity client, string s) sprint = #24;
+PF_dprint,	// void(string s) dprint				= #25;
+PF_ftos,	// void(string s) ftos				= #26;
+PF_vtos,	// void(string s) vtos				= #27;
+PF_coredump,
+PF_traceon,
+PF_traceoff,
+PF_eprint,	// void(entity e) debug print an entire entity
+PF_walkmove, // float(float yaw, float dist) walkmove
+PF_Fixme, // float(float yaw, float dist) walkmove
+PF_droptofloor,
+PF_lightstyle,
+PF_rint,
+PF_floor,
+PF_ceil,
+PF_Fixme,
+PF_checkbottom,
+PF_pointcontents,
+PF_Fixme,
+PF_fabs,
+PF_aim,
+PF_cvar,
+PF_localcmd,
+PF_nextent,
+PF_particle,
+PF_changeyaw,
+PF_Fixme,
+PF_vectoangles,
+
+PF_WriteByte,
+PF_WriteChar,
+PF_WriteShort,
+PF_WriteLong,
+PF_WriteCoord,
+PF_WriteAngle,
+PF_WriteString,
+PF_WriteEntity,
+
+#ifdef QUAKE2
+PF_sin,
+PF_cos,
+PF_sqrt,
+PF_changepitch,
+PF_TraceToss,
+PF_etos,
+PF_WaterMove,
+#else
+PF_Fixme,
+PF_Fixme,
+PF_Fixme,
+PF_Fixme,
+PF_Fixme,
+PF_Fixme,
+PF_Fixme,
+#endif
+
+SV_MoveToGoal,
+PF_precache_file,
+PF_makestatic,
+
+PF_changelevel,
+PF_Fixme,
+
+PF_cvar_set,
+PF_centerprint,
+
+PF_ambientsound,
+
+PF_precache_model,
+PF_precache_sound,		// precache_sound2 is different only for qcc
+PF_precache_file,
+
+PF_setspawnparms
+};
+
+builtin_t *pr_builtins = pr_builtin;
+int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]);
+
diff --git a/apps/plugins/sdl/progs/quake/pr_comp.h b/apps/plugins/sdl/progs/quake/pr_comp.h
new file mode 100644
index 0000000..483e8e6
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/pr_comp.h
@@ -0,0 +1,180 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// this file is shared by quake and qcc
+
+typedef int	func_t;
+typedef int	string_t;
+
+typedef int etype_t;
+enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer};
+
+#define	OFS_NULL		0
+#define	OFS_RETURN		1
+#define	OFS_PARM0		4		// leave 3 ofs for each parm to hold vectors
+#define	OFS_PARM1		7
+#define	OFS_PARM2		10
+#define	OFS_PARM3		13
+#define	OFS_PARM4		16
+#define	OFS_PARM5		19
+#define	OFS_PARM6		22
+#define	OFS_PARM7		25
+#define	RESERVED_OFS	28
+
+
+enum {
+	OP_DONE,
+	OP_MUL_F,
+	OP_MUL_V,
+	OP_MUL_FV,
+	OP_MUL_VF,
+	OP_DIV_F,
+	OP_ADD_F,
+	OP_ADD_V,
+	OP_SUB_F,
+	OP_SUB_V,
+	
+	OP_EQ_F,
+	OP_EQ_V,
+	OP_EQ_S,
+	OP_EQ_E,
+	OP_EQ_FNC,
+	
+	OP_NE_F,
+	OP_NE_V,
+	OP_NE_S,
+	OP_NE_E,
+	OP_NE_FNC,
+	
+	OP_LE,
+	OP_GE,
+	OP_LT,
+	OP_GT,
+
+	OP_LOAD_F,
+	OP_LOAD_V,
+	OP_LOAD_S,
+	OP_LOAD_ENT,
+	OP_LOAD_FLD,
+	OP_LOAD_FNC,
+
+	OP_ADDRESS,
+
+	OP_STORE_F,
+	OP_STORE_V,
+	OP_STORE_S,
+	OP_STORE_ENT,
+	OP_STORE_FLD,
+	OP_STORE_FNC,
+
+	OP_STOREP_F,
+	OP_STOREP_V,
+	OP_STOREP_S,
+	OP_STOREP_ENT,
+	OP_STOREP_FLD,
+	OP_STOREP_FNC,
+
+	OP_RETURN,
+	OP_NOT_F,
+	OP_NOT_V,
+	OP_NOT_S,
+	OP_NOT_ENT,
+	OP_NOT_FNC,
+	OP_IF,
+	OP_IFNOT,
+	OP_CALL0,
+	OP_CALL1,
+	OP_CALL2,
+	OP_CALL3,
+	OP_CALL4,
+	OP_CALL5,
+	OP_CALL6,
+	OP_CALL7,
+	OP_CALL8,
+	OP_STATE,
+	OP_GOTO,
+	OP_AND,
+	OP_OR,
+	
+	OP_BITAND,
+	OP_BITOR
+};
+
+
+typedef struct statement_s
+{
+	unsigned short	op;
+	short	a,b,c;
+} dstatement_t;
+
+typedef struct
+{
+	unsigned short	type;		// if DEF_SAVEGLOBGAL bit is set
+								// the variable needs to be saved in savegames
+	unsigned short	ofs;
+	int			s_name;
+} ddef_t;
+#define	DEF_SAVEGLOBAL	(1<<15)
+
+#define	MAX_PARMS	8
+
+typedef struct
+{
+	int		first_statement;	// negative numbers are builtins
+	int		parm_start;
+	int		locals;				// total ints of parms + locals
+	
+	int		profile;		// runtime
+	
+	int		s_name;
+	int		s_file;			// source file defined in
+	
+	int		numparms;
+	byte	parm_size[MAX_PARMS];
+} dfunction_t;
+
+
+#define	PROG_VERSION	6
+typedef struct
+{
+	int		version;
+	int		crc;			// check of header file
+	
+	int		ofs_statements;
+	int		numstatements;	// statement 0 is an error
+
+	int		ofs_globaldefs;
+	int		numglobaldefs;
+	
+	int		ofs_fielddefs;
+	int		numfielddefs;
+	
+	int		ofs_functions;
+	int		numfunctions;	// function 0 is an empty
+	
+	int		ofs_strings;
+	int		numstrings;		// first string is a null string
+
+	int		ofs_globals;
+	int		numglobals;
+	
+	int		entityfields;
+} dprograms_t;
+
diff --git a/apps/plugins/sdl/progs/quake/pr_edict.c b/apps/plugins/sdl/progs/quake/pr_edict.c
new file mode 100644
index 0000000..2f0e8ec
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/pr_edict.c
@@ -0,0 +1,1113 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_edict.c -- entity dictionary
+
+#include "quakedef.h"
+
+dprograms_t		*progs;
+dfunction_t		*pr_functions;
+char			*pr_strings;
+ddef_t			*pr_fielddefs;
+ddef_t			*pr_globaldefs;
+dstatement_t	*pr_statements;
+globalvars_t	*pr_global_struct;
+float			*pr_globals;			// same as pr_global_struct
+int				pr_edict_size;	// in bytes
+
+unsigned short		pr_crc;
+
+int		type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
+
+ddef_t *ED_FieldAtOfs (int ofs);
+qboolean	ED_ParseEpair (void *base, ddef_t *key, char *s);
+
+cvar_t	nomonsters = {"nomonsters", "0"};
+cvar_t	gamecfg = {"gamecfg", "0"};
+cvar_t	scratch1 = {"scratch1", "0"};
+cvar_t	scratch2 = {"scratch2", "0"};
+cvar_t	scratch3 = {"scratch3", "0"};
+cvar_t	scratch4 = {"scratch4", "0"};
+cvar_t	savedgamecfg = {"savedgamecfg", "0", true};
+cvar_t	saved1 = {"saved1", "0", true};
+cvar_t	saved2 = {"saved2", "0", true};
+cvar_t	saved3 = {"saved3", "0", true};
+cvar_t	saved4 = {"saved4", "0", true};
+
+#define	MAX_FIELD_LEN	64
+#define GEFV_CACHESIZE	2
+
+typedef struct {
+	ddef_t	*pcache;
+	char	field[MAX_FIELD_LEN];
+} gefv_cache;
+
+static gefv_cache	gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}};
+
+/*
+=================
+ED_ClearEdict
+
+Sets everything to NULL
+=================
+*/
+void ED_ClearEdict (edict_t *e)
+{
+	memset (&e->v, 0, progs->entityfields * 4);
+	e->free = false;
+}
+
+/*
+=================
+ED_Alloc
+
+Either finds a free edict, or allocates a new one.
+Try to avoid reusing an entity that was recently freed, because it
+can cause the client to think the entity morphed into something else
+instead of being removed and recreated, which can cause interpolated
+angles and bad trails.
+=================
+*/
+edict_t *ED_Alloc (void)
+{
+	int			i;
+	edict_t		*e;
+
+	for ( i=svs.maxclients+1 ; i<sv.num_edicts ; i++)
+	{
+		e = EDICT_NUM(i);
+		// the first couple seconds of server time can involve a lot of
+		// freeing and allocating, so relax the replacement policy
+		if (e->free && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) )
+		{
+			ED_ClearEdict (e);
+			return e;
+		}
+	}
+	
+	if (i == MAX_EDICTS)
+		Sys_Error ("ED_Alloc: no free edicts");
+		
+	sv.num_edicts++;
+	e = EDICT_NUM(i);
+	ED_ClearEdict (e);
+
+	return e;
+}
+
+/*
+=================
+ED_Free
+
+Marks the edict as free
+FIXME: walk all entities and NULL out references to this entity
+=================
+*/
+void ED_Free (edict_t *ed)
+{
+	SV_UnlinkEdict (ed);		// unlink from world bsp
+
+	ed->free = true;
+	ed->v.model = 0;
+	ed->v.takedamage = 0;
+	ed->v.modelindex = 0;
+	ed->v.colormap = 0;
+	ed->v.skin = 0;
+	ed->v.frame = 0;
+	VectorCopy (vec3_origin, ed->v.origin);
+	VectorCopy (vec3_origin, ed->v.angles);
+	ed->v.nextthink = -1;
+	ed->v.solid = 0;
+	
+	ed->freetime = sv.time;
+}
+
+//===========================================================================
+
+/*
+============
+ED_GlobalAtOfs
+============
+*/
+ddef_t *ED_GlobalAtOfs (int ofs)
+{
+	ddef_t		*def;
+	int			i;
+	
+	for (i=0 ; i<progs->numglobaldefs ; i++)
+	{
+		def = &pr_globaldefs[i];
+		if (def->ofs == ofs)
+			return def;
+	}
+	return NULL;
+}
+
+/*
+============
+ED_FieldAtOfs
+============
+*/
+ddef_t *ED_FieldAtOfs (int ofs)
+{
+	ddef_t		*def;
+	int			i;
+	
+	for (i=0 ; i<progs->numfielddefs ; i++)
+	{
+		def = &pr_fielddefs[i];
+		if (def->ofs == ofs)
+			return def;
+	}
+	return NULL;
+}
+
+/*
+============
+ED_FindField
+============
+*/
+ddef_t *ED_FindField (char *name)
+{
+	ddef_t		*def;
+	int			i;
+	
+	for (i=0 ; i<progs->numfielddefs ; i++)
+	{
+		def = &pr_fielddefs[i];
+		if (!strcmp(pr_strings + def->s_name,name) )
+			return def;
+	}
+	return NULL;
+}
+
+
+/*
+============
+ED_FindGlobal
+============
+*/
+ddef_t *ED_FindGlobal (char *name)
+{
+	ddef_t		*def;
+	int			i;
+	
+	for (i=0 ; i<progs->numglobaldefs ; i++)
+	{
+		def = &pr_globaldefs[i];
+		if (!strcmp(pr_strings + def->s_name,name) )
+			return def;
+	}
+	return NULL;
+}
+
+
+/*
+============
+ED_FindFunction
+============
+*/
+dfunction_t *ED_FindFunction (char *name)
+{
+	dfunction_t		*func;
+	int				i;
+	
+	for (i=0 ; i<progs->numfunctions ; i++)
+	{
+		func = &pr_functions[i];
+		if (!strcmp(pr_strings + func->s_name,name) )
+			return func;
+	}
+	return NULL;
+}
+
+
+eval_t *GetEdictFieldValue(edict_t *ed, char *field)
+{
+	ddef_t			*def = NULL;
+	int				i;
+	static int		rep = 0;
+
+	for (i=0 ; i<GEFV_CACHESIZE ; i++)
+	{
+		if (!strcmp(field, gefvCache[i].field))
+		{
+			def = gefvCache[i].pcache;
+			goto Done;
+		}
+	}
+
+	def = ED_FindField (field);
+
+	if (strlen(field) < MAX_FIELD_LEN)
+	{
+		gefvCache[rep].pcache = def;
+		strcpy (gefvCache[rep].field, field);
+		rep ^= 1;
+	}
+
+Done:
+	if (!def)
+		return NULL;
+
+	return (eval_t *)((char *)&ed->v + def->ofs*4);
+}
+
+
+/*
+============
+PR_ValueString
+
+Returns a string describing *data in a type specific manner
+=============
+*/
+char *PR_ValueString (etype_t type, eval_t *val)
+{
+	static char	line[256];
+	ddef_t		*def;
+	dfunction_t	*f;
+	
+	type &= ~DEF_SAVEGLOBAL;
+
+	switch (type)
+	{
+	case ev_string:
+		sprintf (line, "%s", pr_strings + val->string);
+		break;
+	case ev_entity:	
+		sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) );
+		break;
+	case ev_function:
+		f = pr_functions + val->function;
+		sprintf (line, "%s()", pr_strings + f->s_name);
+		break;
+	case ev_field:
+		def = ED_FieldAtOfs ( val->_int );
+		sprintf (line, ".%s", pr_strings + def->s_name);
+		break;
+	case ev_void:
+		sprintf (line, "void");
+		break;
+	case ev_float:
+		sprintf (line, "%5.1f", val->_float);
+		break;
+	case ev_vector:
+		sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
+		break;
+	case ev_pointer:
+		sprintf (line, "pointer");
+		break;
+	default:
+		sprintf (line, "bad type %i", type);
+		break;
+	}
+	
+	return line;
+}
+
+/*
+============
+PR_UglyValueString
+
+Returns a string describing *data in a type specific manner
+Easier to parse than PR_ValueString
+=============
+*/
+char *PR_UglyValueString (etype_t type, eval_t *val)
+{
+	static char	line[256];
+	ddef_t		*def;
+	dfunction_t	*f;
+	
+	type &= ~DEF_SAVEGLOBAL;
+
+	switch (type)
+	{
+	case ev_string:
+		sprintf (line, "%s", pr_strings + val->string);
+		break;
+	case ev_entity:	
+		sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
+		break;
+	case ev_function:
+		f = pr_functions + val->function;
+		sprintf (line, "%s", pr_strings + f->s_name);
+		break;
+	case ev_field:
+		def = ED_FieldAtOfs ( val->_int );
+		sprintf (line, "%s", pr_strings + def->s_name);
+		break;
+	case ev_void:
+		sprintf (line, "void");
+		break;
+	case ev_float:
+		sprintf (line, "%f", val->_float);
+		break;
+	case ev_vector:
+		sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
+		break;
+	default:
+		sprintf (line, "bad type %i", type);
+		break;
+	}
+	
+	return line;
+}
+
+/*
+============
+PR_GlobalString
+
+Returns a string with a description and the contents of a global,
+padded to 20 field width
+============
+*/
+char *PR_GlobalString (int ofs)
+{
+	char	*s;
+	int		i;
+	ddef_t	*def;
+	void	*val;
+	static char	line[128];
+	
+	val = (void *)&pr_globals[ofs];
+	def = ED_GlobalAtOfs(ofs);
+	if (!def)
+		sprintf (line,"%i(???)", ofs);
+	else
+	{
+		s = PR_ValueString (def->type, val);
+		sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s);
+	}
+	
+	i = strlen(line);
+	for ( ; i<20 ; i++)
+		strcat (line," ");
+	strcat (line," ");
+		
+	return line;
+}
+
+char *PR_GlobalStringNoContents (int ofs)
+{
+	int		i;
+	ddef_t	*def;
+	static char	line[128];
+	
+	def = ED_GlobalAtOfs(ofs);
+	if (!def)
+		sprintf (line,"%i(???)", ofs);
+	else
+		sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name);
+	
+	i = strlen(line);
+	for ( ; i<20 ; i++)
+		strcat (line," ");
+	strcat (line," ");
+		
+	return line;
+}
+
+
+/*
+=============
+ED_Print
+
+For debugging
+=============
+*/
+void ED_Print (edict_t *ed)
+{
+	int		l;
+	ddef_t	*d;
+	int		*v;
+	int		i, j;
+	char	*name;
+	int		type;
+
+	if (ed->free)
+	{
+		Con_Printf ("FREE\n");
+		return;
+	}
+
+	Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed));
+	for (i=1 ; i<progs->numfielddefs ; i++)
+	{
+		d = &pr_fielddefs[i];
+		name = pr_strings + d->s_name;
+		if (name[strlen(name)-2] == '_')
+			continue;	// skip _x, _y, _z vars
+			
+		v = (int *)((char *)&ed->v + d->ofs*4);
+
+	// if the value is still all 0, skip the field
+		type = d->type & ~DEF_SAVEGLOBAL;
+		
+		for (j=0 ; j<type_size[type] ; j++)
+			if (v[j])
+				break;
+		if (j == type_size[type])
+			continue;
+	
+		Con_Printf ("%s",name);
+		l = strlen (name);
+		while (l++ < 15)
+			Con_Printf (" ");
+
+		Con_Printf ("%s\n", PR_ValueString(d->type, (eval_t *)v));		
+	}
+}
+
+/*
+=============
+ED_Write
+
+For savegames
+=============
+*/
+void ED_Write (FILE *f, edict_t *ed)
+{
+	ddef_t	*d;
+	int		*v;
+	int		i, j;
+	char	*name;
+	int		type;
+
+	fprintf (f, "{\n");
+
+	if (ed->free)
+	{
+		fprintf (f, "}\n");
+		return;
+	}
+	
+	for (i=1 ; i<progs->numfielddefs ; i++)
+	{
+		d = &pr_fielddefs[i];
+		name = pr_strings + d->s_name;
+		if (name[strlen(name)-2] == '_')
+			continue;	// skip _x, _y, _z vars
+			
+		v = (int *)((char *)&ed->v + d->ofs*4);
+
+	// if the value is still all 0, skip the field
+		type = d->type & ~DEF_SAVEGLOBAL;
+		for (j=0 ; j<type_size[type] ; j++)
+			if (v[j])
+				break;
+		if (j == type_size[type])
+			continue;
+	
+		fprintf (f,"\"%s\" ",name);
+		fprintf (f,"\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v));		
+	}
+
+	fprintf (f, "}\n");
+}
+
+void ED_PrintNum (int ent)
+{
+	ED_Print (EDICT_NUM(ent));
+}
+
+/*
+=============
+ED_PrintEdicts
+
+For debugging, prints all the entities in the current server
+=============
+*/
+void ED_PrintEdicts (void)
+{
+	int		i;
+	
+	Con_Printf ("%i entities\n", sv.num_edicts);
+	for (i=0 ; i<sv.num_edicts ; i++)
+		ED_PrintNum (i);
+}
+
+/*
+=============
+ED_PrintEdict_f
+
+For debugging, prints a single edicy
+=============
+*/
+void ED_PrintEdict_f (void)
+{
+	int		i;
+	
+	i = Q_atoi (Cmd_Argv(1));
+	if (i >= sv.num_edicts)
+	{
+		Con_Printf("Bad edict number\n");
+		return;
+	}
+	ED_PrintNum (i);
+}
+
+/*
+=============
+ED_Count
+
+For debugging
+=============
+*/
+void ED_Count (void)
+{
+	int		i;
+	edict_t	*ent;
+	int		active, models, solid, step;
+
+	active = models = solid = step = 0;
+	for (i=0 ; i<sv.num_edicts ; i++)
+	{
+		ent = EDICT_NUM(i);
+		if (ent->free)
+			continue;
+		active++;
+		if (ent->v.solid)
+			solid++;
+		if (ent->v.model)
+			models++;
+		if (ent->v.movetype == MOVETYPE_STEP)
+			step++;
+	}
+
+	Con_Printf ("num_edicts:%3i\n", sv.num_edicts);
+	Con_Printf ("active    :%3i\n", active);
+	Con_Printf ("view      :%3i\n", models);
+	Con_Printf ("touch     :%3i\n", solid);
+	Con_Printf ("step      :%3i\n", step);
+
+}
+
+/*
+==============================================================================
+
+					ARCHIVING GLOBALS
+
+FIXME: need to tag constants, doesn't really work
+==============================================================================
+*/
+
+/*
+=============
+ED_WriteGlobals
+=============
+*/
+void ED_WriteGlobals (FILE *f)
+{
+	ddef_t		*def;
+	int			i;
+	char		*name;
+	int			type;
+
+	fprintf (f,"{\n");
+	for (i=0 ; i<progs->numglobaldefs ; i++)
+	{
+		def = &pr_globaldefs[i];
+		type = def->type;
+		if ( !(def->type & DEF_SAVEGLOBAL) )
+			continue;
+		type &= ~DEF_SAVEGLOBAL;
+
+		if (type != ev_string
+		&& type != ev_float
+		&& type != ev_entity)
+			continue;
+
+		name = pr_strings + def->s_name;		
+		fprintf (f,"\"%s\" ", name);
+		fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs]));		
+	}
+	fprintf (f,"}\n");
+}
+
+/*
+=============
+ED_ParseGlobals
+=============
+*/
+void ED_ParseGlobals (char *data)
+{
+	char	keyname[64];
+	ddef_t	*key;
+
+	while (1)
+	{	
+	// parse key
+		data = COM_Parse (data);
+		if (com_token[0] == '}')
+			break;
+		if (!data)
+			Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+		strcpy (keyname, com_token);
+
+	// parse value	
+		data = COM_Parse (data);
+		if (!data)
+			Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+		if (com_token[0] == '}')
+			Sys_Error ("ED_ParseEntity: closing brace without data");
+
+		key = ED_FindGlobal (keyname);
+		if (!key)
+		{
+			Con_Printf ("'%s' is not a global\n", keyname);
+			continue;
+		}
+
+		if (!ED_ParseEpair ((void *)pr_globals, key, com_token))
+			Host_Error ("ED_ParseGlobals: parse error");
+	}
+}
+
+//============================================================================
+
+
+/*
+=============
+ED_NewString
+=============
+*/
+char *ED_NewString (char *string)
+{
+	char	*new, *new_p;
+	int		i,l;
+	
+	l = strlen(string) + 1;
+	new = Hunk_Alloc (l);
+	new_p = new;
+
+	for (i=0 ; i< l ; i++)
+	{
+		if (string[i] == '\\' && i < l-1)
+		{
+			i++;
+			if (string[i] == 'n')
+				*new_p++ = '\n';
+			else
+				*new_p++ = '\\';
+		}
+		else
+			*new_p++ = string[i];
+	}
+	
+	return new;
+}
+
+
+/*
+=============
+ED_ParseEval
+
+Can parse either fields or globals
+returns false if error
+=============
+*/
+qboolean	ED_ParseEpair (void *base, ddef_t *key, char *s)
+{
+	int		i;
+	char	string[128];
+	ddef_t	*def;
+	char	*v, *w;
+	void	*d;
+	dfunction_t	*func;
+	
+	d = (void *)((int *)base + key->ofs);
+	
+	switch (key->type & ~DEF_SAVEGLOBAL)
+	{
+	case ev_string:
+		*(string_t *)d = ED_NewString (s) - pr_strings;
+		break;
+		
+	case ev_float:
+		*(float *)d = Q_atof (s);
+		break;
+		
+	case ev_vector:
+		strcpy (string, s);
+		v = string;
+		w = string;
+		for (i=0 ; i<3 ; i++)
+		{
+			while (*v && *v != ' ')
+				v++;
+			*v = 0;
+			((float *)d)[i] = Q_atof (w);
+			w = v = v+1;
+		}
+		break;
+		
+	case ev_entity:
+		*(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s)));
+		break;
+		
+	case ev_field:
+		def = ED_FindField (s);
+		if (!def)
+		{
+			Con_Printf ("Can't find field %s\n", s);
+			return false;
+		}
+		*(int *)d = G_INT(def->ofs);
+		break;
+	
+	case ev_function:
+		func = ED_FindFunction (s);
+		if (!func)
+		{
+			Con_Printf ("Can't find function %s\n", s);
+			return false;
+		}
+		*(func_t *)d = func - pr_functions;
+		break;
+		
+	default:
+		break;
+	}
+	return true;
+}
+
+/*
+====================
+ED_ParseEdict
+
+Parses an edict out of the given string, returning the new position
+ed should be a properly initialized empty edict.
+Used for initial level load and for savegames.
+====================
+*/
+char *ED_ParseEdict (char *data, edict_t *ent)
+{
+	ddef_t		*key;
+	qboolean	anglehack;
+	qboolean	init;
+	char		keyname[256];
+	int			n;
+
+	init = false;
+
+// clear it
+	if (ent != sv.edicts)	// hack
+		memset (&ent->v, 0, progs->entityfields * 4);
+
+// go through all the dictionary pairs
+	while (1)
+	{	
+	// parse key
+		data = COM_Parse (data);
+		if (com_token[0] == '}')
+			break;
+		if (!data)
+			Sys_Error ("ED_ParseEntity: EOF without closing brace");
+		
+// anglehack is to allow QuakeEd to write single scalar angles
+// and allow them to be turned into vectors. (FIXME...)
+if (!strcmp(com_token, "angle"))
+{
+	strcpy (com_token, "angles");
+	anglehack = true;
+}
+else
+	anglehack = false;
+
+// FIXME: change light to _light to get rid of this hack
+if (!strcmp(com_token, "light"))
+	strcpy (com_token, "light_lev");	// hack for single light def
+
+		strcpy (keyname, com_token);
+
+		// another hack to fix heynames with trailing spaces
+		n = strlen(keyname);
+		while (n && keyname[n-1] == ' ')
+		{
+			keyname[n-1] = 0;
+			n--;
+		}
+
+	// parse value	
+		data = COM_Parse (data);
+		if (!data)
+			Sys_Error ("ED_ParseEntity: EOF without closing brace");
+
+		if (com_token[0] == '}')
+			Sys_Error ("ED_ParseEntity: closing brace without data");
+
+		init = true;	
+
+// keynames with a leading underscore are used for utility comments,
+// and are immediately discarded by quake
+		if (keyname[0] == '_')
+			continue;
+		
+		key = ED_FindField (keyname);
+		if (!key)
+		{
+			Con_Printf ("'%s' is not a field\n", keyname);
+			continue;
+		}
+
+if (anglehack)
+{
+char	temp[32];
+strcpy (temp, com_token);
+sprintf (com_token, "0 %s 0", temp);
+}
+
+		if (!ED_ParseEpair ((void *)&ent->v, key, com_token))
+			Host_Error ("ED_ParseEdict: parse error");
+	}
+
+	if (!init)
+		ent->free = true;
+
+	return data;
+}
+
+
+/*
+================
+ED_LoadFromFile
+
+The entities are directly placed in the array, rather than allocated with
+ED_Alloc, because otherwise an error loading the map would have entity
+number references out of order.
+
+Creates a server's entity / program execution context by
+parsing textual entity definitions out of an ent file.
+
+Used for both fresh maps and savegame loads.  A fresh map would also need
+to call ED_CallSpawnFunctions () to let the objects initialize themselves.
+================
+*/
+void ED_LoadFromFile (char *data)
+{	
+	edict_t		*ent;
+	int			inhibit;
+	dfunction_t	*func;
+	
+	ent = NULL;
+	inhibit = 0;
+	pr_global_struct->time = sv.time;
+	
+// parse ents
+	while (1)
+	{
+// parse the opening brace	
+		data = COM_Parse (data);
+		if (!data)
+			break;
+		if (com_token[0] != '{')
+			Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token);
+
+		if (!ent)
+			ent = EDICT_NUM(0);
+		else
+			ent = ED_Alloc ();
+		data = ED_ParseEdict (data, ent);
+
+// remove things from different skill levels or deathmatch
+		if (deathmatch.value)
+		{
+			if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH))
+			{
+				ED_Free (ent);	
+				inhibit++;
+				continue;
+			}
+		}
+		else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY))
+				|| (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM))
+				|| (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) )
+		{
+			ED_Free (ent);	
+			inhibit++;
+			continue;
+		}
+
+//
+// immediately call spawn function
+//
+		if (!ent->v.classname)
+		{
+			Con_Printf ("No classname for:\n");
+			ED_Print (ent);
+			ED_Free (ent);
+			continue;
+		}
+
+	// look for the spawn function
+		func = ED_FindFunction ( pr_strings + ent->v.classname );
+
+		if (!func)
+		{
+			Con_Printf ("No spawn function for:\n");
+			ED_Print (ent);
+			ED_Free (ent);
+			continue;
+		}
+
+		pr_global_struct->self = EDICT_TO_PROG(ent);
+		PR_ExecuteProgram (func - pr_functions);
+	}	
+
+	Con_DPrintf ("%i entities inhibited\n", inhibit);
+}
+
+
+/*
+===============
+PR_LoadProgs
+===============
+*/
+void PR_LoadProgs (void)
+{
+	int		i;
+
+// flush the non-C variable lookup cache
+	for (i=0 ; i<GEFV_CACHESIZE ; i++)
+		gefvCache[i].field[0] = 0;
+
+	CRC_Init (&pr_crc);
+
+        //printf("loadprogs 1");
+
+	progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat");
+        //printf("loadprogs 2");
+        
+	if (!progs)
+		Sys_Error ("PR_LoadProgs: couldn't load progs.dat");
+	Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024);
+        //printf("loadprogs 3");
+
+	for (i=0 ; i<com_filesize ; i++)
+		CRC_ProcessByte (&pr_crc, ((byte *)progs)[i]);
+
+        //printf("loadprogs 4");
+// byte swap the header
+	for (i=0 ; i<sizeof(*progs)/4 ; i++)
+		((int *)progs)[i] = LittleLong ( ((int *)progs)[i] );		
+
+	if (progs->version != PROG_VERSION)
+		Sys_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION);
+	if (progs->crc != PROGHEADER_CRC)
+		Sys_Error ("progs.dat system vars have been modified, progdefs.h is out of date");
+        //printf("loadprogs 5");
+
+	pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions);
+	pr_strings = (char *)progs + progs->ofs_strings;
+	pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs);
+	pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs);
+	pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements);
+
+	pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals);
+	pr_globals = (float *)pr_global_struct;
+	
+	pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t);
+	
+// byte swap the lumps
+	for (i=0 ; i<progs->numstatements ; i++)
+	{
+		pr_statements[i].op = LittleShort(pr_statements[i].op);
+		pr_statements[i].a = LittleShort(pr_statements[i].a);
+		pr_statements[i].b = LittleShort(pr_statements[i].b);
+		pr_statements[i].c = LittleShort(pr_statements[i].c);
+	}
+
+	for (i=0 ; i<progs->numfunctions; i++)
+	{
+	pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement);
+	pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start);
+	pr_functions[i].s_name = LittleLong (pr_functions[i].s_name);
+	pr_functions[i].s_file = LittleLong (pr_functions[i].s_file);
+	pr_functions[i].numparms = LittleLong (pr_functions[i].numparms);
+	pr_functions[i].locals = LittleLong (pr_functions[i].locals);
+	}	
+
+	for (i=0 ; i<progs->numglobaldefs ; i++)
+	{
+		pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type);
+		pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs);
+		pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name);
+	}
+
+	for (i=0 ; i<progs->numfielddefs ; i++)
+	{
+		pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type);
+		if (pr_fielddefs[i].type & DEF_SAVEGLOBAL)
+			Sys_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
+		pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs);
+		pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
+	}
+
+	for (i=0 ; i<progs->numglobals ; i++)
+		((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
+}
+
+
+/*
+===============
+PR_Init
+===============
+*/
+void PR_Init (void)
+{
+	Cmd_AddCommand ("edict", ED_PrintEdict_f);
+	Cmd_AddCommand ("edicts", ED_PrintEdicts);
+	Cmd_AddCommand ("edictcount", ED_Count);
+	Cmd_AddCommand ("profile", PR_Profile_f);
+	Cvar_RegisterVariable (&nomonsters);
+	Cvar_RegisterVariable (&gamecfg);
+	Cvar_RegisterVariable (&scratch1);
+	Cvar_RegisterVariable (&scratch2);
+	Cvar_RegisterVariable (&scratch3);
+	Cvar_RegisterVariable (&scratch4);
+	Cvar_RegisterVariable (&savedgamecfg);
+	Cvar_RegisterVariable (&saved1);
+	Cvar_RegisterVariable (&saved2);
+	Cvar_RegisterVariable (&saved3);
+	Cvar_RegisterVariable (&saved4);
+}
+
+
+
+edict_t *EDICT_NUM(int n)
+{
+	if (n < 0 || n >= sv.max_edicts)
+		Sys_Error ("EDICT_NUM: bad number %i", n);
+	return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size);
+}
+
+int NUM_FOR_EDICT(edict_t *e)
+{
+	int		b;
+	
+	b = (byte *)e - (byte *)sv.edicts;
+	b = b / pr_edict_size;
+	
+	if (b < 0 || b >= sv.num_edicts)
+		Sys_Error ("NUM_FOR_EDICT: bad pointer");
+	return b;
+}
diff --git a/apps/plugins/sdl/progs/quake/pr_exec.c b/apps/plugins/sdl/progs/quake/pr_exec.c
new file mode 100644
index 0000000..4f2aa36
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/pr_exec.c
@@ -0,0 +1,668 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+
+
+/*
+
+*/
+
+typedef struct
+{
+	int				s;
+	dfunction_t		*f;
+} prstack_t;
+
+#define	MAX_STACK_DEPTH		32
+prstack_t	pr_stack[MAX_STACK_DEPTH];
+int			pr_depth;
+
+#define	LOCALSTACK_SIZE		2048
+int			localstack[LOCALSTACK_SIZE];
+int			localstack_used;
+
+
+qboolean	pr_trace;
+dfunction_t	*pr_xfunction;
+int			pr_xstatement;
+
+
+int		pr_argc;
+
+char *pr_opnames[] =
+{
+"DONE",
+
+"MUL_F",
+"MUL_V", 
+"MUL_FV",
+"MUL_VF",
+ 
+"DIV",
+
+"ADD_F",
+"ADD_V", 
+  
+"SUB_F",
+"SUB_V",
+
+"EQ_F",
+"EQ_V",
+"EQ_S", 
+"EQ_E",
+"EQ_FNC",
+ 
+"NE_F",
+"NE_V", 
+"NE_S",
+"NE_E", 
+"NE_FNC",
+ 
+"LE",
+"GE",
+"LT",
+"GT", 
+
+"INDIRECT",
+"INDIRECT",
+"INDIRECT", 
+"INDIRECT", 
+"INDIRECT",
+"INDIRECT", 
+
+"ADDRESS", 
+
+"STORE_F",
+"STORE_V",
+"STORE_S",
+"STORE_ENT",
+"STORE_FLD",
+"STORE_FNC",
+
+"STOREP_F",
+"STOREP_V",
+"STOREP_S",
+"STOREP_ENT",
+"STOREP_FLD",
+"STOREP_FNC",
+
+"RETURN",
+  
+"NOT_F",
+"NOT_V",
+"NOT_S", 
+"NOT_ENT", 
+"NOT_FNC", 
+  
+"IF",
+"IFNOT",
+  
+"CALL0",
+"CALL1",
+"CALL2",
+"CALL3",
+"CALL4",
+"CALL5",
+"CALL6",
+"CALL7",
+"CALL8",
+  
+"STATE",
+  
+"GOTO", 
+  
+"AND",
+"OR", 
+
+"BITAND",
+"BITOR"
+};
+
+char *PR_GlobalString (int ofs);
+char *PR_GlobalStringNoContents (int ofs);
+
+
+//=============================================================================
+
+/*
+=================
+PR_PrintStatement
+=================
+*/
+void PR_PrintStatement (dstatement_t *s)
+{
+	int		i;
+	
+	if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0]))
+	{
+		Con_Printf ("%s ",  pr_opnames[s->op]);
+		i = strlen(pr_opnames[s->op]);
+		for ( ; i<10 ; i++)
+			Con_Printf (" ");
+	}
+		
+	if (s->op == OP_IF || s->op == OP_IFNOT)
+		Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b);
+	else if (s->op == OP_GOTO)
+	{
+		Con_Printf ("branch %i",s->a);
+	}
+	else if ( (unsigned)(s->op - OP_STORE_F) < 6)
+	{
+		Con_Printf ("%s",PR_GlobalString(s->a));
+		Con_Printf ("%s", PR_GlobalStringNoContents(s->b));
+	}
+	else
+	{
+		if (s->a)
+			Con_Printf ("%s",PR_GlobalString(s->a));
+		if (s->b)
+			Con_Printf ("%s",PR_GlobalString(s->b));
+		if (s->c)
+			Con_Printf ("%s", PR_GlobalStringNoContents(s->c));
+	}
+	Con_Printf ("\n");
+}
+
+/*
+============
+PR_StackTrace
+============
+*/
+void PR_StackTrace (void)
+{
+	dfunction_t	*f;
+	int			i;
+	
+	if (pr_depth == 0)
+	{
+		Con_Printf ("<NO STACK>\n");
+		return;
+	}
+	
+	pr_stack[pr_depth].f = pr_xfunction;
+	for (i=pr_depth ; i>=0 ; i--)
+	{
+		f = pr_stack[i].f;
+		
+		if (!f)
+		{
+			Con_Printf ("<NO FUNCTION>\n");
+		}
+		else
+			Con_Printf ("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name);		
+	}
+}
+
+
+/*
+============
+PR_Profile_f
+
+============
+*/
+void PR_Profile_f (void)
+{
+	dfunction_t	*f, *best;
+	int			max;
+	int			num;
+	int			i;
+	
+	num = 0;	
+	do
+	{
+		max = 0;
+		best = NULL;
+		for (i=0 ; i<progs->numfunctions ; i++)
+		{
+			f = &pr_functions[i];
+			if (f->profile > max)
+			{
+				max = f->profile;
+				best = f;
+			}
+		}
+		if (best)
+		{
+			if (num < 10)
+				Con_Printf ("%7i %s\n", best->profile, pr_strings+best->s_name);
+			num++;
+			best->profile = 0;
+		}
+	} while (best);
+}
+
+
+/*
+============
+PR_RunError
+
+Aborts the currently executing function
+============
+*/
+void PR_RunError (char *error, ...)
+{
+	va_list		argptr;
+	char		string[1024];
+
+	va_start (argptr,error);
+	vsprintf (string,error,argptr);
+	va_end (argptr);
+
+	PR_PrintStatement (pr_statements + pr_xstatement);
+	PR_StackTrace ();
+	Con_Printf ("%s\n", string);
+	
+	pr_depth = 0;		// dump the stack so host_error can shutdown functions
+
+	Host_Error ("Program error");
+}
+
+/*
+============================================================================
+PR_ExecuteProgram
+
+The interpretation main loop
+============================================================================
+*/
+
+/*
+====================
+PR_EnterFunction
+
+Returns the new program statement counter
+====================
+*/
+int PR_EnterFunction (dfunction_t *f)
+{
+	int		i, j, c, o;
+
+	pr_stack[pr_depth].s = pr_xstatement;
+	pr_stack[pr_depth].f = pr_xfunction;	
+	pr_depth++;
+	if (pr_depth >= MAX_STACK_DEPTH)
+		PR_RunError ("stack overflow");
+
+// save off any locals that the new function steps on
+	c = f->locals;
+	if (localstack_used + c > LOCALSTACK_SIZE)
+		PR_RunError ("PR_ExecuteProgram: locals stack overflow\n");
+
+	for (i=0 ; i < c ; i++)
+		localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];
+	localstack_used += c;
+
+// copy parameters
+	o = f->parm_start;
+	for (i=0 ; i<f->numparms ; i++)
+	{
+		for (j=0 ; j<f->parm_size[i] ; j++)
+		{
+			((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];
+			o++;
+		}
+	}
+
+	pr_xfunction = f;
+	return f->first_statement - 1;	// offset the s++
+}
+
+/*
+====================
+PR_LeaveFunction
+====================
+*/
+int PR_LeaveFunction (void)
+{
+	int		i, c;
+
+	if (pr_depth <= 0)
+		Sys_Error ("prog stack underflow");
+
+// restore locals from the stack
+	c = pr_xfunction->locals;
+	localstack_used -= c;
+	if (localstack_used < 0)
+		PR_RunError ("PR_ExecuteProgram: locals stack underflow\n");
+
+	for (i=0 ; i < c ; i++)
+		((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i];
+
+// up stack
+	pr_depth--;
+	pr_xfunction = pr_stack[pr_depth].f;
+	return pr_stack[pr_depth].s;
+}
+
+
+/*
+====================
+PR_ExecuteProgram
+====================
+*/
+void PR_ExecuteProgram (func_t fnum)
+{
+	eval_t	*a, *b, *c;
+	int			s;
+	dstatement_t	*st;
+	dfunction_t	*f, *newf;
+	int		runaway;
+	int		i;
+	edict_t	*ed;
+	int		exitdepth;
+	eval_t	*ptr;
+
+	if (!fnum || fnum >= progs->numfunctions)
+	{
+		if (pr_global_struct->self)
+			ED_Print (PROG_TO_EDICT(pr_global_struct->self));
+		Host_Error ("PR_ExecuteProgram: NULL function");
+	}
+	
+	f = &pr_functions[fnum];
+
+	runaway = 100000;
+	pr_trace = false;
+
+// make a stack frame
+	exitdepth = pr_depth;
+
+	s = PR_EnterFunction (f);
+	
+while (1)
+{
+	s++;	// next statement
+
+	st = &pr_statements[s];
+	a = (eval_t *)&pr_globals[st->a];
+	b = (eval_t *)&pr_globals[st->b];
+	c = (eval_t *)&pr_globals[st->c];
+	
+	if (!--runaway)
+		PR_RunError ("runaway loop error");
+		
+	pr_xfunction->profile++;
+	pr_xstatement = s;
+	
+	if (pr_trace)
+		PR_PrintStatement (st);
+		
+	switch (st->op)
+	{
+	case OP_ADD_F:
+		c->_float = a->_float + b->_float;
+		break;
+	case OP_ADD_V:
+		c->vector[0] = a->vector[0] + b->vector[0];
+		c->vector[1] = a->vector[1] + b->vector[1];
+		c->vector[2] = a->vector[2] + b->vector[2];
+		break;
+		
+	case OP_SUB_F:
+		c->_float = a->_float - b->_float;
+		break;
+	case OP_SUB_V:
+		c->vector[0] = a->vector[0] - b->vector[0];
+		c->vector[1] = a->vector[1] - b->vector[1];
+		c->vector[2] = a->vector[2] - b->vector[2];
+		break;
+
+	case OP_MUL_F:
+		c->_float = a->_float * b->_float;
+		break;
+	case OP_MUL_V:
+		c->_float = a->vector[0]*b->vector[0]
+				+ a->vector[1]*b->vector[1]
+				+ a->vector[2]*b->vector[2];
+		break;
+	case OP_MUL_FV:
+		c->vector[0] = a->_float * b->vector[0];
+		c->vector[1] = a->_float * b->vector[1];
+		c->vector[2] = a->_float * b->vector[2];
+		break;
+	case OP_MUL_VF:
+		c->vector[0] = b->_float * a->vector[0];
+		c->vector[1] = b->_float * a->vector[1];
+		c->vector[2] = b->_float * a->vector[2];
+		break;
+
+	case OP_DIV_F:
+		c->_float = a->_float / b->_float;
+		break;
+	
+	case OP_BITAND:
+		c->_float = (int)a->_float & (int)b->_float;
+		break;
+	
+	case OP_BITOR:
+		c->_float = (int)a->_float | (int)b->_float;
+		break;
+	
+		
+	case OP_GE:
+		c->_float = a->_float >= b->_float;
+		break;
+	case OP_LE:
+		c->_float = a->_float <= b->_float;
+		break;
+	case OP_GT:
+		c->_float = a->_float > b->_float;
+		break;
+	case OP_LT:
+		c->_float = a->_float < b->_float;
+		break;
+	case OP_AND:
+		c->_float = a->_float && b->_float;
+		break;
+	case OP_OR:
+		c->_float = a->_float || b->_float;
+		break;
+		
+	case OP_NOT_F:
+		c->_float = !a->_float;
+		break;
+	case OP_NOT_V:
+		c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2];
+		break;
+	case OP_NOT_S:
+		c->_float = !a->string || !pr_strings[a->string];
+		break;
+	case OP_NOT_FNC:
+		c->_float = !a->function;
+		break;
+	case OP_NOT_ENT:
+		c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts);
+		break;
+
+	case OP_EQ_F:
+		c->_float = a->_float == b->_float;
+		break;
+	case OP_EQ_V:
+		c->_float = (a->vector[0] == b->vector[0]) &&
+					(a->vector[1] == b->vector[1]) &&
+					(a->vector[2] == b->vector[2]);
+		break;
+	case OP_EQ_S:
+		c->_float = !strcmp(pr_strings+a->string,pr_strings+b->string);
+		break;
+	case OP_EQ_E:
+		c->_float = a->_int == b->_int;
+		break;
+	case OP_EQ_FNC:
+		c->_float = a->function == b->function;
+		break;
+
+
+	case OP_NE_F:
+		c->_float = a->_float != b->_float;
+		break;
+	case OP_NE_V:
+		c->_float = (a->vector[0] != b->vector[0]) ||
+					(a->vector[1] != b->vector[1]) ||
+					(a->vector[2] != b->vector[2]);
+		break;
+	case OP_NE_S:
+		c->_float = strcmp(pr_strings+a->string,pr_strings+b->string);
+		break;
+	case OP_NE_E:
+		c->_float = a->_int != b->_int;
+		break;
+	case OP_NE_FNC:
+		c->_float = a->function != b->function;
+		break;
+
+//==================
+	case OP_STORE_F:
+	case OP_STORE_ENT:
+	case OP_STORE_FLD:		// integers
+	case OP_STORE_S:
+	case OP_STORE_FNC:		// pointers
+		b->_int = a->_int;
+		break;
+	case OP_STORE_V:
+		b->vector[0] = a->vector[0];
+		b->vector[1] = a->vector[1];
+		b->vector[2] = a->vector[2];
+		break;
+		
+	case OP_STOREP_F:
+	case OP_STOREP_ENT:
+	case OP_STOREP_FLD:		// integers
+	case OP_STOREP_S:
+	case OP_STOREP_FNC:		// pointers
+		ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+		ptr->_int = a->_int;
+		break;
+	case OP_STOREP_V:
+		ptr = (eval_t *)((byte *)sv.edicts + b->_int);
+		ptr->vector[0] = a->vector[0];
+		ptr->vector[1] = a->vector[1];
+		ptr->vector[2] = a->vector[2];
+		break;
+		
+	case OP_ADDRESS:
+		ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+		NUM_FOR_EDICT(ed);		// make sure it's in range
+#endif
+		if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
+			PR_RunError ("assignment to world entity");
+		c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts;
+		break;
+		
+	case OP_LOAD_F:
+	case OP_LOAD_FLD:
+	case OP_LOAD_ENT:
+	case OP_LOAD_S:
+	case OP_LOAD_FNC:
+		ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+		NUM_FOR_EDICT(ed);		// make sure it's in range
+#endif
+		a = (eval_t *)((int *)&ed->v + b->_int);
+		c->_int = a->_int;
+		break;
+
+	case OP_LOAD_V:
+		ed = PROG_TO_EDICT(a->edict);
+#ifdef PARANOID
+		NUM_FOR_EDICT(ed);		// make sure it's in range
+#endif
+		a = (eval_t *)((int *)&ed->v + b->_int);
+		c->vector[0] = a->vector[0];
+		c->vector[1] = a->vector[1];
+		c->vector[2] = a->vector[2];
+		break;
+		
+//==================
+
+	case OP_IFNOT:
+		if (!a->_int)
+			s += st->b - 1;	// offset the s++
+		break;
+		
+	case OP_IF:
+		if (a->_int)
+			s += st->b - 1;	// offset the s++
+		break;
+		
+	case OP_GOTO:
+		s += st->a - 1;	// offset the s++
+		break;
+		
+	case OP_CALL0:
+	case OP_CALL1:
+	case OP_CALL2:
+	case OP_CALL3:
+	case OP_CALL4:
+	case OP_CALL5:
+	case OP_CALL6:
+	case OP_CALL7:
+	case OP_CALL8:
+		pr_argc = st->op - OP_CALL0;
+		if (!a->function)
+			PR_RunError ("NULL function");
+
+		newf = &pr_functions[a->function];
+
+		if (newf->first_statement < 0)
+		{	// negative statements are built in functions
+			i = -newf->first_statement;
+			if (i >= pr_numbuiltins)
+				PR_RunError ("Bad builtin call number");
+			pr_builtins[i] ();
+			break;
+		}
+
+		s = PR_EnterFunction (newf);
+		break;
+
+	case OP_DONE:
+	case OP_RETURN:
+		pr_globals[OFS_RETURN] = pr_globals[st->a];
+		pr_globals[OFS_RETURN+1] = pr_globals[st->a+1];
+		pr_globals[OFS_RETURN+2] = pr_globals[st->a+2];
+	
+		s = PR_LeaveFunction ();
+		if (pr_depth == exitdepth)
+			return;		// all done
+		break;
+		
+	case OP_STATE:
+		ed = PROG_TO_EDICT(pr_global_struct->self);
+#ifdef FPS_20
+		ed->v.nextthink = pr_global_struct->time + 0.05;
+#else
+		ed->v.nextthink = pr_global_struct->time + 0.1;
+#endif
+		if (a->_float != ed->v.frame)
+		{
+			ed->v.frame = a->_float;
+		}
+		ed->v.think = b->function;
+		break;
+		
+	default:
+		PR_RunError ("Bad opcode %i", st->op);
+	}
+}
+
+}
diff --git a/apps/plugins/sdl/progs/quake/progdefs.h b/apps/plugins/sdl/progs/quake/progdefs.h
new file mode 100644
index 0000000..db86de1
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/progdefs.h
@@ -0,0 +1,24 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 QUAKE2
+#include "progdefs.q2"
+#else
+#include "progdefs.q1"
+#endif
diff --git a/apps/plugins/sdl/progs/quake/progdefs.q1 b/apps/plugins/sdl/progs/quake/progdefs.q1
new file mode 100644
index 0000000..eb15c45
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/progdefs.q1
@@ -0,0 +1,143 @@
+
+/* file generated by qcc, do not modify */
+
+typedef struct
+{	int	pad[28];
+	int	self;
+	int	other;
+	int	world;
+	float	time;
+	float	frametime;
+	float	force_retouch;
+	string_t	mapname;
+	float	deathmatch;
+	float	coop;
+	float	teamplay;
+	float	serverflags;
+	float	total_secrets;
+	float	total_monsters;
+	float	found_secrets;
+	float	killed_monsters;
+	float	parm1;
+	float	parm2;
+	float	parm3;
+	float	parm4;
+	float	parm5;
+	float	parm6;
+	float	parm7;
+	float	parm8;
+	float	parm9;
+	float	parm10;
+	float	parm11;
+	float	parm12;
+	float	parm13;
+	float	parm14;
+	float	parm15;
+	float	parm16;
+	vec3_t	v_forward;
+	vec3_t	v_up;
+	vec3_t	v_right;
+	float	trace_allsolid;
+	float	trace_startsolid;
+	float	trace_fraction;
+	vec3_t	trace_endpos;
+	vec3_t	trace_plane_normal;
+	float	trace_plane_dist;
+	int	trace_ent;
+	float	trace_inopen;
+	float	trace_inwater;
+	int	msg_entity;
+	func_t	main;
+	func_t	StartFrame;
+	func_t	PlayerPreThink;
+	func_t	PlayerPostThink;
+	func_t	ClientKill;
+	func_t	ClientConnect;
+	func_t	PutClientInServer;
+	func_t	ClientDisconnect;
+	func_t	SetNewParms;
+	func_t	SetChangeParms;
+} globalvars_t;
+
+typedef struct
+{
+	float	modelindex;
+	vec3_t	absmin;
+	vec3_t	absmax;
+	float	ltime;
+	float	movetype;
+	float	solid;
+	vec3_t	origin;
+	vec3_t	oldorigin;
+	vec3_t	velocity;
+	vec3_t	angles;
+	vec3_t	avelocity;
+	vec3_t	punchangle;
+	string_t	classname;
+	string_t	model;
+	float	frame;
+	float	skin;
+	float	effects;
+	vec3_t	mins;
+	vec3_t	maxs;
+	vec3_t	size;
+	func_t	touch;
+	func_t	use;
+	func_t	think;
+	func_t	blocked;
+	float	nextthink;
+	int	groundentity;
+	float	health;
+	float	frags;
+	float	weapon;
+	string_t	weaponmodel;
+	float	weaponframe;
+	float	currentammo;
+	float	ammo_shells;
+	float	ammo_nails;
+	float	ammo_rockets;
+	float	ammo_cells;
+	float	items;
+	float	takedamage;
+	int	chain;
+	float	deadflag;
+	vec3_t	view_ofs;
+	float	button0;
+	float	button1;
+	float	button2;
+	float	impulse;
+	float	fixangle;
+	vec3_t	v_angle;
+	float	idealpitch;
+	string_t	netname;
+	int	enemy;
+	float	flags;
+	float	colormap;
+	float	team;
+	float	max_health;
+	float	teleport_time;
+	float	armortype;
+	float	armorvalue;
+	float	waterlevel;
+	float	watertype;
+	float	ideal_yaw;
+	float	yaw_speed;
+	int	aiment;
+	int	goalentity;
+	float	spawnflags;
+	string_t	target;
+	string_t	targetname;
+	float	dmg_take;
+	float	dmg_save;
+	int	dmg_inflictor;
+	int	owner;
+	vec3_t	movedir;
+	string_t	message;
+	float	sounds;
+	string_t	noise;
+	string_t	noise1;
+	string_t	noise2;
+	string_t	noise3;
+} entvars_t;
+
+#define PROGHEADER_CRC 5927
diff --git a/apps/plugins/sdl/progs/quake/progdefs.q2 b/apps/plugins/sdl/progs/quake/progdefs.q2
new file mode 100644
index 0000000..dc7f3be
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/progdefs.q2
@@ -0,0 +1,158 @@
+
+/* file generated by qcc, do not modify */
+
+typedef struct
+{	int	pad[28];
+	int	self;
+	int	other;
+	int	world;
+	float	time;
+	float	frametime;
+	float	force_retouch;
+	string_t	mapname;
+	string_t	startspot;
+	float	deathmatch;
+	float	coop;
+	float	teamplay;
+	float	serverflags;
+	float	total_secrets;
+	float	total_monsters;
+	float	found_secrets;
+	float	killed_monsters;
+	float	parm1;
+	float	parm2;
+	float	parm3;
+	float	parm4;
+	float	parm5;
+	float	parm6;
+	float	parm7;
+	float	parm8;
+	float	parm9;
+	float	parm10;
+	float	parm11;
+	float	parm12;
+	float	parm13;
+	float	parm14;
+	float	parm15;
+	float	parm16;
+	vec3_t	v_forward;
+	vec3_t	v_up;
+	vec3_t	v_right;
+	float	trace_allsolid;
+	float	trace_startsolid;
+	float	trace_fraction;
+	vec3_t	trace_endpos;
+	vec3_t	trace_plane_normal;
+	float	trace_plane_dist;
+	int	trace_ent;
+	float	trace_inopen;
+	float	trace_inwater;
+	int	msg_entity;
+	string_t	null;
+	func_t	main;
+	func_t	StartFrame;
+	func_t	PlayerPreThink;
+	func_t	PlayerPostThink;
+	func_t	ClientKill;
+	func_t	ClientConnect;
+	func_t	PutClientInServer;
+	func_t	ClientDisconnect;
+	func_t	SetNewParms;
+	func_t	SetChangeParms;
+} globalvars_t;
+
+typedef struct
+{
+	float	modelindex;
+	vec3_t	absmin;
+	vec3_t	absmax;
+	float	ltime;
+	float	movetype;
+	float	solid;
+	vec3_t	origin;
+	vec3_t	oldorigin;
+	vec3_t	velocity;
+	vec3_t	angles;
+	vec3_t	avelocity;
+	vec3_t	basevelocity;
+	vec3_t	punchangle;
+	string_t	classname;
+	string_t	model;
+	float	frame;
+	float	skin;
+	float	effects;
+	float	drawPercent;
+	float	gravity;
+	float	mass;
+	float	light_level;
+	vec3_t	mins;
+	vec3_t	maxs;
+	vec3_t	size;
+	func_t	touch;
+	func_t	use;
+	func_t	think;
+	func_t	blocked;
+	float	nextthink;
+	int	groundentity;
+	float	health;
+	float	frags;
+	float	weapon;
+	string_t	weaponmodel;
+	float	weaponframe;
+	float	currentammo;
+	float	ammo_shells;
+	float	ammo_nails;
+	float	ammo_rockets;
+	float	ammo_cells;
+	float	items;
+	float	items2;
+	float	takedamage;
+	int	chain;
+	float	deadflag;
+	vec3_t	view_ofs;
+	float	button0;
+	float	button1;
+	float	button2;
+	float	impulse;
+	float	fixangle;
+	vec3_t	v_angle;
+	float	idealpitch;
+	float	pitch_speed;
+	string_t	netname;
+	int	enemy;
+	float	flags;
+	float	colormap;
+	float	team;
+	float	max_health;
+	float	teleport_time;
+	float	armortype;
+	float	armorvalue;
+	float	waterlevel;
+	float	watertype;
+	float	ideal_yaw;
+	float	yaw_speed;
+	int	aiment;
+	int	goalentity;
+	float	spawnflags;
+	string_t	target;
+	string_t	targetname;
+	float	dmg_take;
+	float	dmg_save;
+	int	dmg_inflictor;
+	int	owner;
+	vec3_t	movedir;
+	string_t	message;
+	float	sounds;
+	string_t	noise;
+	string_t	noise1;
+	string_t	noise2;
+	string_t	noise3;
+	float	dmg;
+	float	dmgtime;
+	float	air_finished;
+	float	pain_finished;
+	float	radsuit_finished;
+	float	speed;
+} entvars_t;
+
+#define PROGHEADER_CRC 31586
diff --git a/apps/plugins/sdl/progs/quake/progs.h b/apps/plugins/sdl/progs/quake/progs.h
new file mode 100644
index 0000000..6d3aa8a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/progs.h
@@ -0,0 +1,134 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "pr_comp.h"			// defs shared with qcc
+#include "progdefs.h"			// generated by program cdefs
+
+typedef union eval_s
+{
+	string_t		string;
+	float			_float;
+	float			vector[3];
+	func_t			function;
+	int				_int;
+	int				edict;
+} eval_t;	
+
+#define	MAX_ENT_LEAFS	16
+typedef struct edict_s
+{
+	qboolean	free;
+	link_t		area;				// linked to a division node or leaf
+	
+	int			num_leafs;
+	short		leafnums[MAX_ENT_LEAFS];
+
+	entity_state_t	baseline;
+	
+	float		freetime;			// sv.time when the object was freed
+	entvars_t	v;					// C exported fields from progs
+// other fields from progs come immediately after
+} edict_t;
+#define	EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
+
+//============================================================================
+
+extern	dprograms_t		*progs;
+extern	dfunction_t		*pr_functions;
+extern	char			*pr_strings;
+extern	ddef_t			*pr_globaldefs;
+extern	ddef_t			*pr_fielddefs;
+extern	dstatement_t	*pr_statements;
+extern	globalvars_t	*pr_global_struct;
+extern	float			*pr_globals;			// same as pr_global_struct
+
+extern	int				pr_edict_size;	// in bytes
+
+//============================================================================
+
+void PR_Init (void);
+
+void PR_ExecuteProgram (func_t fnum);
+void PR_LoadProgs (void);
+
+void PR_Profile_f (void);
+
+edict_t *ED_Alloc (void);
+void ED_Free (edict_t *ed);
+
+char	*ED_NewString (char *string);
+// returns a copy of the string allocated from the server's string heap
+
+void ED_Print (edict_t *ed);
+void ED_Write (FILE *f, edict_t *ed);
+char *ED_ParseEdict (char *data, edict_t *ent);
+
+void ED_WriteGlobals (FILE *f);
+void ED_ParseGlobals (char *data);
+
+void ED_LoadFromFile (char *data);
+
+//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
+//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)
+
+edict_t *EDICT_NUM(int n);
+int NUM_FOR_EDICT(edict_t *e);
+
+#define	NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
+
+#define	EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
+#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
+
+//============================================================================
+
+#define	G_FLOAT(o) (pr_globals[o])
+#define	G_INT(o) (*(int *)&pr_globals[o])
+#define	G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
+#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
+#define	G_VECTOR(o) (&pr_globals[o])
+#define	G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o])
+#define	G_FUNCTION(o) (*(func_t *)&pr_globals[o])
+
+#define	E_FLOAT(e,o) (((float*)&e->v)[o])
+#define	E_INT(e,o) (*(int *)&((float*)&e->v)[o])
+#define	E_VECTOR(e,o) (&((float*)&e->v)[o])
+#define	E_STRING(e,o) (pr_strings + *(string_t *)&((float*)&e->v)[o])
+
+extern	int		type_size[8];
+
+typedef void (*builtin_t) (void);
+extern	builtin_t *pr_builtins;
+extern int pr_numbuiltins;
+
+extern int		pr_argc;
+
+extern	qboolean	pr_trace;
+extern	dfunction_t	*pr_xfunction;
+extern	int			pr_xstatement;
+
+extern	unsigned short		pr_crc;
+
+void PR_RunError (char *error, ...);
+
+void ED_PrintEdicts (void);
+void ED_PrintNum (int ent);
+
+eval_t *GetEdictFieldValue(edict_t *ed, char *field);
+
diff --git a/apps/plugins/sdl/progs/quake/protocol.h b/apps/plugins/sdl/progs/quake/protocol.h
new file mode 100644
index 0000000..01780f8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/protocol.h
@@ -0,0 +1,167 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// protocol.h -- communications protocols
+
+#define	PROTOCOL_VERSION	15
+
+// if the high bit of the servercmd is set, the low bits are fast update flags:
+#define	U_MOREBITS	(1<<0)
+#define	U_ORIGIN1	(1<<1)
+#define	U_ORIGIN2	(1<<2)
+#define	U_ORIGIN3	(1<<3)
+#define	U_ANGLE2	(1<<4)
+#define	U_NOLERP	(1<<5)		// don't interpolate movement
+#define	U_FRAME		(1<<6)
+#define U_SIGNAL	(1<<7)		// just differentiates from other updates
+
+// svc_update can pass all of the fast update bits, plus more
+#define	U_ANGLE1	(1<<8)
+#define	U_ANGLE3	(1<<9)
+#define	U_MODEL		(1<<10)
+#define	U_COLORMAP	(1<<11)
+#define	U_SKIN		(1<<12)
+#define	U_EFFECTS	(1<<13)
+#define	U_LONGENTITY	(1<<14)
+
+
+#define	SU_VIEWHEIGHT	(1<<0)
+#define	SU_IDEALPITCH	(1<<1)
+#define	SU_PUNCH1		(1<<2)
+#define	SU_PUNCH2		(1<<3)
+#define	SU_PUNCH3		(1<<4)
+#define	SU_VELOCITY1	(1<<5)
+#define	SU_VELOCITY2	(1<<6)
+#define	SU_VELOCITY3	(1<<7)
+//define	SU_AIMENT		(1<<8)  AVAILABLE BIT
+#define	SU_ITEMS		(1<<9)
+#define	SU_ONGROUND		(1<<10)		// no data follows, the bit is it
+#define	SU_INWATER		(1<<11)		// no data follows, the bit is it
+#define	SU_WEAPONFRAME	(1<<12)
+#define	SU_ARMOR		(1<<13)
+#define	SU_WEAPON		(1<<14)
+
+// a sound with no channel is a local only sound
+#define	SND_VOLUME		(1<<0)		// a byte
+#define	SND_ATTENUATION	(1<<1)		// a byte
+#define	SND_LOOPING		(1<<2)		// a long
+
+
+// defaults for clientinfo messages
+#define	DEFAULT_VIEWHEIGHT	22
+
+
+// game types sent by serverinfo
+// these determine which intermission screen plays
+#define	GAME_COOP			0
+#define	GAME_DEATHMATCH		1
+
+//==================
+// note that there are some defs.qc that mirror to these numbers
+// also related to svc_strings[] in cl_parse
+//==================
+
+//
+// server to client
+//
+#define	svc_bad				0
+#define	svc_nop				1
+#define	svc_disconnect		2
+#define	svc_updatestat		3	// [byte] [long]
+#define	svc_version			4	// [long] server version
+#define	svc_setview			5	// [short] entity number
+#define	svc_sound			6	// <see code>
+#define	svc_time			7	// [float] server time
+#define	svc_print			8	// [string] null terminated string
+#define	svc_stufftext		9	// [string] stuffed into client's console buffer
+								// the string should be \n terminated
+#define	svc_setangle		10	// [angle3] set the view angle to this absolute value
+	
+#define	svc_serverinfo		11	// [long] version
+						// [string] signon string
+						// [string]..[0]model cache
+						// [string]...[0]sounds cache
+#define	svc_lightstyle		12	// [byte] [string]
+#define	svc_updatename		13	// [byte] [string]
+#define	svc_updatefrags		14	// [byte] [short]
+#define	svc_clientdata		15	// <shortbits + data>
+#define	svc_stopsound		16	// <see code>
+#define	svc_updatecolors	17	// [byte] [byte]
+#define	svc_particle		18	// [vec3] <variable>
+#define	svc_damage			19
+	
+#define	svc_spawnstatic		20
+//	svc_spawnbinary		21
+#define	svc_spawnbaseline	22
+	
+#define	svc_temp_entity		23
+
+#define	svc_setpause		24	// [byte] on / off
+#define	svc_signonnum		25	// [byte]  used for the signon sequence
+
+#define	svc_centerprint		26	// [string] to put in center of the screen
+
+#define	svc_killedmonster	27
+#define	svc_foundsecret		28
+
+#define	svc_spawnstaticsound	29	// [coord3] [byte] samp [byte] vol [byte] aten
+
+#define	svc_intermission	30		// [string] music
+#define	svc_finale			31		// [string] music [string] text
+
+#define	svc_cdtrack			32		// [byte] track [byte] looptrack
+#define svc_sellscreen		33
+
+#define svc_cutscene		34
+
+//
+// client to server
+//
+#define	clc_bad			0
+#define	clc_nop 		1
+#define	clc_disconnect	2
+#define	clc_move		3			// [usercmd_t]
+#define	clc_stringcmd	4		// [string] message
+
+
+//
+// temp entity events
+//
+#define	TE_SPIKE			0
+#define	TE_SUPERSPIKE		1
+#define	TE_GUNSHOT			2
+#define	TE_EXPLOSION		3
+#define	TE_TAREXPLOSION		4
+#define	TE_LIGHTNING1		5
+#define	TE_LIGHTNING2		6
+#define	TE_WIZSPIKE			7
+#define	TE_KNIGHTSPIKE		8
+#define	TE_LIGHTNING3		9
+#define	TE_LAVASPLASH		10
+#define	TE_TELEPORT			11
+#define TE_EXPLOSION2		12
+
+// PGM 01/21/97 
+#define TE_BEAM				13
+// PGM 01/21/97 
+
+#ifdef QUAKE2
+#define TE_IMPLOSION		14
+#define TE_RAILTRAIL		15
+#endif
diff --git a/apps/plugins/sdl/progs/quake/quakeasm.h b/apps/plugins/sdl/progs/quake/quakeasm.h
new file mode 100644
index 0000000..3038539
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/quakeasm.h
@@ -0,0 +1,280 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// quakeasm.h: general asm header file
+//
+
+//#define GLQUAKE	1
+
+#if defined(_WIN32) && !defined(WINDED)
+
+#if defined(_M_IX86)
+#define __i386__	1
+#endif
+
+#endif
+
+#if defined(__i386__) && defined(USE_ASM)
+#define id386	1
+#else
+#define id386	0
+#endif
+
+// !!! must be kept the same as in d_iface.h !!!
+#define TRANSPARENT_COLOR	255
+
+#ifndef NeXT
+#ifndef GLQUAKE
+	.extern C(d_zistepu)
+	.extern C(d_pzbuffer)
+	.extern C(d_zistepv)
+	.extern C(d_zrowbytes)
+	.extern C(d_ziorigin)
+	.extern C(r_turb_s)
+	.extern C(r_turb_t)
+	.extern C(r_turb_pdest)
+	.extern C(r_turb_spancount)
+	.extern C(r_turb_turb)
+	.extern C(r_turb_pbase)
+	.extern C(r_turb_sstep)
+	.extern C(r_turb_tstep)
+	.extern	C(r_bmodelactive)
+	.extern	C(d_sdivzstepu)
+	.extern	C(d_tdivzstepu)
+	.extern	C(d_sdivzstepv)
+	.extern	C(d_tdivzstepv)
+	.extern	C(d_sdivzorigin)
+	.extern	C(d_tdivzorigin)
+	.extern	C(sadjust)
+	.extern	C(tadjust)
+	.extern	C(bbextents)
+	.extern	C(bbextentt)
+	.extern	C(cacheblock)
+	.extern	C(d_viewbuffer)
+	.extern	C(cachewidth)
+	.extern	C(d_pzbuffer)
+	.extern	C(d_zrowbytes)
+	.extern	C(d_zwidth)
+	.extern C(d_scantable)
+	.extern C(r_lightptr)
+	.extern C(r_numvblocks)
+	.extern C(prowdestbase)
+	.extern C(pbasesource)
+	.extern C(r_lightwidth)
+	.extern C(lightright)
+	.extern C(lightrightstep)
+	.extern C(lightdeltastep)
+	.extern C(lightdelta)
+	.extern C(lightright)
+	.extern C(lightdelta)
+	.extern C(sourcetstep)
+	.extern C(surfrowbytes)
+	.extern C(lightrightstep)
+	.extern C(lightdeltastep)
+	.extern C(r_sourcemax)
+	.extern C(r_stepback)
+	.extern C(colormap)
+	.extern C(blocksize)
+	.extern C(sourcesstep)
+	.extern C(lightleft)
+	.extern C(blockdivshift)
+	.extern C(blockdivmask)
+	.extern C(lightleftstep)
+	.extern C(r_origin)
+	.extern C(r_ppn)
+	.extern C(r_pup)
+	.extern C(r_pright)
+	.extern C(ycenter)
+	.extern C(xcenter)
+	.extern C(d_vrectbottom_particle)
+	.extern C(d_vrectright_particle)
+	.extern C(d_vrecty)
+	.extern C(d_vrectx)
+	.extern C(d_pix_shift)
+	.extern C(d_pix_min)
+	.extern C(d_pix_max)
+	.extern C(d_y_aspect_shift)
+	.extern C(screenwidth)
+	.extern C(r_leftclipped)
+	.extern C(r_leftenter)
+	.extern C(r_rightclipped)
+	.extern C(r_rightenter)
+	.extern C(modelorg)
+	.extern C(xscale)
+	.extern C(r_refdef)
+	.extern C(yscale)
+	.extern C(r_leftexit)
+	.extern C(r_rightexit)
+	.extern C(r_lastvertvalid)
+	.extern C(cacheoffset)
+	.extern C(newedges)
+	.extern C(removeedges)
+	.extern C(r_pedge)
+	.extern C(r_framecount)
+	.extern C(r_u1)
+	.extern C(r_emitted)
+	.extern C(edge_p)
+	.extern C(surface_p)
+	.extern C(surfaces)
+	.extern C(r_lzi1)
+	.extern C(r_v1)
+	.extern C(r_ceilv1)
+	.extern C(r_nearzi)
+	.extern C(r_nearzionly)
+	.extern C(edge_aftertail)
+	.extern C(edge_tail)
+	.extern C(current_iv)
+	.extern C(edge_head_u_shift20)
+	.extern C(span_p)
+	.extern C(edge_head)
+	.extern C(fv)
+	.extern C(edge_tail_u_shift20)
+	.extern C(r_apverts)
+	.extern C(r_anumverts)
+	.extern C(aliastransform)
+	.extern C(r_avertexnormals)
+	.extern C(r_plightvec)
+	.extern C(r_ambientlight)
+	.extern C(r_shadelight)
+	.extern C(aliasxcenter)
+	.extern C(aliasycenter)
+	.extern C(a_sstepxfrac)
+	.extern C(r_affinetridesc)
+	.extern C(acolormap)
+	.extern C(d_pcolormap)
+	.extern C(r_affinetridesc)
+	.extern C(d_sfrac)
+	.extern C(d_ptex)
+	.extern C(d_pedgespanpackage)
+	.extern C(d_tfrac)
+	.extern C(d_light)
+	.extern C(d_zi)
+	.extern C(d_pdest)
+	.extern C(d_pz)
+	.extern C(d_aspancount)
+	.extern C(erroradjustup)
+	.extern C(errorterm)
+	.extern C(d_xdenom)
+	.extern C(r_p0)
+	.extern C(r_p1)
+	.extern C(r_p2)
+	.extern C(a_tstepxfrac)
+	.extern C(r_sstepx)
+	.extern C(r_tstepx)
+	.extern C(a_ststepxwhole)
+	.extern C(zspantable)
+	.extern C(skintable)
+	.extern C(r_zistepx)
+	.extern C(erroradjustdown)
+	.extern C(d_countextrastep)
+	.extern C(ubasestep)
+	.extern C(a_ststepxwhole)
+	.extern C(a_tstepxfrac)
+	.extern C(r_lstepx)
+	.extern C(a_spans)
+	.extern C(erroradjustdown)
+	.extern C(d_pdestextrastep)
+	.extern C(d_pzextrastep)
+	.extern C(d_sfracextrastep)
+	.extern C(d_ptexextrastep)
+	.extern C(d_countextrastep)
+	.extern C(d_tfracextrastep)
+	.extern C(d_lightextrastep)
+	.extern C(d_ziextrastep)
+	.extern C(d_pdestbasestep)
+	.extern C(d_pzbasestep)
+	.extern C(d_sfracbasestep)
+	.extern C(d_ptexbasestep)
+	.extern C(ubasestep)
+	.extern C(d_tfracbasestep)
+	.extern C(d_lightbasestep)
+	.extern C(d_zibasestep)
+	.extern C(zspantable)
+	.extern C(r_lstepy)
+	.extern C(r_sstepy)
+	.extern C(r_tstepy)
+	.extern C(r_zistepy)
+	.extern C(D_PolysetSetEdgeTable)
+	.extern C(D_RasterizeAliasPolySmooth)
+
+	.extern float_point5
+	.extern Float2ToThe31nd
+	.extern izistep
+	.extern izi
+	.extern FloatMinus2ToThe31nd
+	.extern float_1
+	.extern float_particle_z_clip
+	.extern float_minus_1
+	.extern float_0
+	.extern fp_16
+	.extern fp_64k
+	.extern fp_1m
+	.extern fp_1m_minus_1
+	.extern fp_8 
+	.extern entryvec_table
+	.extern advancetable
+	.extern sstep
+	.extern tstep
+	.extern pspantemp
+	.extern counttemp
+	.extern jumptemp
+	.extern reciprocal_table
+	.extern DP_Count
+	.extern DP_u
+	.extern DP_v
+	.extern DP_32768
+	.extern DP_Color
+	.extern DP_Pix
+	.extern DP_EntryTable
+	.extern	pbase
+	.extern s
+	.extern t
+	.extern sfracf
+	.extern tfracf
+	.extern snext
+	.extern tnext
+	.extern	spancountminus1
+	.extern zi16stepu
+	.extern sdivz16stepu
+	.extern tdivz16stepu
+	.extern	zi8stepu
+	.extern sdivz8stepu
+	.extern tdivz8stepu
+	.extern reciprocal_table_16
+	.extern entryvec_table_16
+	.extern ceil_cw
+	.extern single_cw
+	.extern fp_64kx64k
+	.extern pz
+	.extern spr8entryvec_table
+#endif
+
+	.extern C(snd_scaletable)
+	.extern C(paintbuffer)
+	.extern C(snd_linear_count)
+	.extern C(snd_p)
+	.extern C(snd_vol)
+	.extern C(snd_out)
+	.extern C(vright)
+	.extern C(vup)
+	.extern C(vpn)
+	.extern C(BOPS_Error)
+
+#endif
diff --git a/apps/plugins/sdl/progs/quake/quakedef.h b/apps/plugins/sdl/progs/quake/quakedef.h
new file mode 100644
index 0000000..e676b7c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/quakedef.h
@@ -0,0 +1,333 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// quakedef.h -- primary header for client
+
+//#define	GLTEST			// experimental stuff
+
+#include "SDL.h"
+#include <stdarg.h>
+#include <setjmp.h>
+
+#define	QUAKE_GAME			// as opposed to utilities
+
+#undef VERSION
+#define	VERSION				1.09
+#define	GLQUAKE_VERSION		1.00
+#define	D3DQUAKE_VERSION	0.01
+#define	WINQUAKE_VERSION	0.996
+#define	LINUX_VERSION		1.30
+#define	X11_VERSION			1.10
+
+//define	PARANOID			// speed sapping error checking
+
+#ifdef QUAKE2
+#define	GAMENAME	"id1"		// directory to look in by default
+#else
+#define	GAMENAME	"id1"
+#endif
+
+#if defined(_WIN32) && !defined(WINDED)
+
+#if defined(_M_IX86)
+#define __i386__	1
+#endif
+
+void	VID_LockBuffer (void);
+void	VID_UnlockBuffer (void);
+
+#else
+
+#define	VID_LockBuffer()
+#define	VID_UnlockBuffer()
+
+#endif
+
+#if defined(__i386__) && defined(USE_ASM)
+#define id386	1
+#else
+#define id386	0
+#endif
+
+#if id386
+#define UNALIGNED_OK	1	// set to 0 if unaligned accesses are not supported
+#else
+#define UNALIGNED_OK	0
+#endif
+
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define CACHE_SIZE	32		// used to align key data structures
+
+#define UNUSED(x)	(x = x)	// for pesky compiler / lint warnings
+
+#define	MINIMUM_MEMORY			0x550000
+#define	MINIMUM_MEMORY_LEVELPAK	(MINIMUM_MEMORY + 0x100000)
+
+#define MAX_NUM_ARGVS	50
+
+// up / down
+#define	PITCH	0
+
+// left / right
+#define	YAW		1
+
+// fall over
+#define	ROLL	2
+
+
+#define	MAX_QPATH		64			// max length of a quake game pathname
+#define	MAX_OSPATH		128			// max length of a filesystem pathname
+
+#define	ON_EPSILON		0.1			// point on plane side epsilon
+
+#define	MAX_MSGLEN		8000		// max length of a reliable message
+#define	MAX_DATAGRAM	1024		// max length of unreliable message
+
+//
+// per-level limits
+//
+#define	MAX_EDICTS		600			// FIXME: ouch! ouch! ouch!
+#define	MAX_LIGHTSTYLES	64
+#define	MAX_MODELS		256			// these are sent over the net as bytes
+#define	MAX_SOUNDS		256			// so they cannot be blindly increased
+
+#define	SAVEGAME_COMMENT_LENGTH	39
+
+#define	MAX_STYLESTRING	64
+
+//
+// stats are integers communicated to the client by the server
+//
+#define	MAX_CL_STATS		32
+#define	STAT_HEALTH			0
+#define	STAT_FRAGS			1
+#define	STAT_WEAPON			2
+#define	STAT_AMMO			3
+#define	STAT_ARMOR			4
+#define	STAT_WEAPONFRAME	5
+#define	STAT_SHELLS			6
+#define	STAT_NAILS			7
+#define	STAT_ROCKETS		8
+#define	STAT_CELLS			9
+#define	STAT_ACTIVEWEAPON	10
+#define	STAT_TOTALSECRETS	11
+#define	STAT_TOTALMONSTERS	12
+#define	STAT_SECRETS		13		// bumped on client side by svc_foundsecret
+#define	STAT_MONSTERS		14		// bumped by svc_killedmonster
+
+// stock defines
+
+#define	IT_SHOTGUN				1
+#define	IT_SUPER_SHOTGUN		2
+#define	IT_NAILGUN				4
+#define	IT_SUPER_NAILGUN		8
+#define	IT_GRENADE_LAUNCHER		16
+#define	IT_ROCKET_LAUNCHER		32
+#define	IT_LIGHTNING			64
+#define IT_SUPER_LIGHTNING      128
+#define IT_SHELLS               256
+#define IT_NAILS                512
+#define IT_ROCKETS              1024
+#define IT_CELLS                2048
+#define IT_AXE                  4096
+#define IT_ARMOR1               8192
+#define IT_ARMOR2               16384
+#define IT_ARMOR3               32768
+#define IT_SUPERHEALTH          65536
+#define IT_KEY1                 131072
+#define IT_KEY2                 262144
+#define	IT_INVISIBILITY			524288
+#define	IT_INVULNERABILITY		1048576
+#define	IT_SUIT					2097152
+#define	IT_QUAD					4194304
+#define IT_SIGIL1               (1<<28)
+#define IT_SIGIL2               (1<<29)
+#define IT_SIGIL3               (1<<30)
+#define IT_SIGIL4               (1<<31)
+
+//===========================================
+//rogue changed and added defines
+
+#define RIT_SHELLS              128
+#define RIT_NAILS               256
+#define RIT_ROCKETS             512
+#define RIT_CELLS               1024
+#define RIT_AXE                 2048
+#define RIT_LAVA_NAILGUN        4096
+#define RIT_LAVA_SUPER_NAILGUN  8192
+#define RIT_MULTI_GRENADE       16384
+#define RIT_MULTI_ROCKET        32768
+#define RIT_PLASMA_GUN          65536
+#define RIT_ARMOR1              8388608
+#define RIT_ARMOR2              16777216
+#define RIT_ARMOR3              33554432
+#define RIT_LAVA_NAILS          67108864
+#define RIT_PLASMA_AMMO         134217728
+#define RIT_MULTI_ROCKETS       268435456
+#define RIT_SHIELD              536870912
+#define RIT_ANTIGRAV            1073741824
+#define RIT_SUPERHEALTH         2147483648
+
+//MED 01/04/97 added hipnotic defines
+//===========================================
+//hipnotic added defines
+#define HIT_PROXIMITY_GUN_BIT 16
+#define HIT_MJOLNIR_BIT       7
+#define HIT_LASER_CANNON_BIT  23
+#define HIT_PROXIMITY_GUN   (1<<HIT_PROXIMITY_GUN_BIT)
+#define HIT_MJOLNIR         (1<<HIT_MJOLNIR_BIT)
+#define HIT_LASER_CANNON    (1<<HIT_LASER_CANNON_BIT)
+#define HIT_WETSUIT         (1<<(23+2))
+#define HIT_EMPATHY_SHIELDS (1<<(23+3))
+
+//===========================================
+
+#define	MAX_SCOREBOARD		16
+#define	MAX_SCOREBOARDNAME	32
+
+#define	SOUND_CHANNELS		8
+
+// This makes anyone on id's net privileged
+// Use for multiplayer testing only - VERY dangerous!!!
+// #define IDGODS
+
+#include "common.h"
+#include "bspfile.h"
+#include "vid.h"
+#include "sys.h"
+#include "zone.h"
+#include "mathlib.h"
+
+typedef struct
+{
+	vec3_t	origin;
+	vec3_t	angles;
+	int		modelindex;
+	int		frame;
+	int		colormap;
+	int		skin;
+	int		effects;
+} entity_state_t;
+
+
+#include "wad.h"
+#include "draw.h"
+#include "cvar.h"
+#include "screen.h"
+#include "net.h"
+#include "protocol.h"
+#include "cmd.h"
+#include "sbar.h"
+#include "quakesound.h"
+#include "render.h"
+#include "client.h"
+#include "progs.h"
+#include "server.h"
+
+#ifdef GLQUAKE
+#include "gl_model.h"
+#else
+#include "model.h"
+#include "d_iface.h"
+#endif
+
+#include "input.h"
+#include "world.h"
+#include "keys.h"
+#include "console.h"
+#include "view.h"
+#include "menu.h"
+#include "crc.h"
+#include "cdaudio.h"
+
+#ifdef GLQUAKE
+#include "glquake.h"
+#endif
+
+//=============================================================================
+
+// the host system specifies the base of the directory tree, the
+// command line parms passed to the program, and the amount of memory
+// available for the program to use
+
+typedef struct
+{
+	char	*basedir;
+	char	*cachedir;		// for development over ISDN lines
+	int		argc;
+	char	**argv;
+	void	*membase;
+	int		memsize;
+} quakeparms_t;
+
+
+//=============================================================================
+
+
+
+extern qboolean noclip_anglehack;
+
+
+//
+// host
+//
+extern	quakeparms_t host_parms;
+
+extern	cvar_t		sys_ticrate;
+extern	cvar_t		sys_nostdout;
+extern	cvar_t		developer;
+
+extern	qboolean	host_initialized;		// true if into command execution
+extern	double		host_frametime;
+extern	byte		*host_basepal;
+extern	byte		*host_colormap;
+extern	int			host_framecount;	// incremented every frame, never reset
+extern	double		realtime;			// not bounded in any way, changed at
+										// start of every frame, never reset
+
+void Host_ClearMemory (void);
+void Host_ServerFrame (void);
+void Host_InitCommands (void);
+void Host_Init (quakeparms_t *parms);
+void Host_Shutdown(void);
+void Host_Error (char *error, ...);
+void Host_EndGame (char *message, ...);
+void Host_Frame (float time);
+void Host_Quit_f (void);
+void Host_ClientCommands (char *fmt, ...);
+void Host_ShutdownServer (qboolean crash);
+
+extern qboolean		msg_suppress_1;		// suppresses resolution and cache size console output
+										//  an fullscreen DIB focus gain/loss
+extern int			current_skill;		// skill level for currently loaded level (in case
+										//  the user changes the cvar while the level is
+										//  running, this reflects the level actually in use)
+
+extern qboolean		isDedicated;
+
+extern int			minimum_memory;
+
+//
+// chase
+//
+extern	cvar_t	chase_active;
+
+void Chase_Init (void);
+void Chase_Reset (void);
+void Chase_Update (void);
diff --git a/apps/plugins/sdl/progs/quake/quakesound.h b/apps/plugins/sdl/progs/quake/quakesound.h
new file mode 100644
index 0000000..1ba08d3
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/quakesound.h
@@ -0,0 +1,177 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sound.h -- client sound i/o functions
+
+#ifndef __SOUND__
+#define __SOUND__
+
+#define DEFAULT_SOUND_PACKET_VOLUME 255
+#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+	int left;
+	int right;
+} portable_samplepair_t;
+
+typedef struct sfx_s
+{
+	char 	name[MAX_QPATH];
+	cache_user_t	cache;
+} sfx_t;
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+	int 	length;
+	int 	loopstart;
+	int 	speed;
+	int 	width;
+	int 	stereo;
+	byte	data[1];		// variable sized
+} sfxcache_t;
+
+typedef struct
+{
+	qboolean		gamealive;
+	qboolean		soundalive;
+	qboolean		splitbuffer;
+	int				channels;
+	int				samples;				// mono samples in buffer
+	int				submission_chunk;		// don't mix less than this #
+	int				samplepos;				// in mono samples
+	int				samplebits;
+	int				speed;
+	unsigned char	*buffer;
+} dma_t;
+
+// !!! if this is changed, it much be changed in asm_i386.h too !!!
+typedef struct
+{
+	sfx_t	*sfx;			// sfx number
+	int		leftvol;		// 0-255 volume
+	int		rightvol;		// 0-255 volume
+	int		end;			// end time in global paintsamples
+	int 	pos;			// sample position in sfx
+	int		looping;		// where to loop, -1 = no looping
+	int		entnum;			// to allow overriding a specific sound
+	int		entchannel;		//
+	vec3_t	origin;			// origin of sound effect
+	vec_t	dist_mult;		// distance multiplier (attenuation/clipK)
+	int		master_vol;		// 0-255 master volume
+} channel_t;
+
+typedef struct
+{
+	int		rate;
+	int		width;
+	int		channels;
+	int		loopstart;
+	int		samples;
+	int		dataofs;		// chunk starts this many bytes from file start
+} wavinfo_t;
+
+void S_Init (void);
+void S_Startup (void);
+void S_Shutdown (void);
+void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol,  float attenuation);
+void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation);
+void S_StopSound (int entnum, int entchannel);
+void S_StopAllSounds(qboolean clear);
+void S_ClearBuffer (void);
+void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
+void S_ExtraUpdate (void);
+
+sfx_t *S_PrecacheSound (char *sample);
+void S_TouchSound (char *sample);
+void S_ClearPrecache (void);
+void S_BeginPrecaching (void);
+void S_EndPrecaching (void);
+void S_PaintChannels(int endtime);
+void S_InitPaintChannels (void);
+
+// picks a channel based on priorities, empty slots, number of channels
+channel_t *SND_PickChannel(int entnum, int entchannel);
+
+// spatializes a channel
+void SND_Spatialize(channel_t *ch);
+
+// initializes cycling through a DMA buffer and returns information on it
+qboolean SNDDMA_Init(void);
+
+// gets the current DMA position
+int SNDDMA_GetDMAPos(void);
+
+// shutdown the DMA xfer.
+void SNDDMA_Shutdown(void);
+
+// ====================================================================
+// User-setable variables
+// ====================================================================
+
+#define	MAX_CHANNELS			128
+#define	MAX_DYNAMIC_CHANNELS	8
+
+
+extern	channel_t   channels[MAX_CHANNELS];
+// 0 to MAX_DYNAMIC_CHANNELS-1	= normal entity sounds
+// MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc
+// MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds
+
+extern	int			total_channels;
+
+//
+// Fake dma is a synchronous faking of the DMA progress used for
+// isolating performance in the renderer.  The fakedma_updates is
+// number of times S_Update() is called per second.
+//
+
+extern qboolean 		fakedma;
+extern int 			fakedma_updates;
+extern int		paintedtime;
+extern vec3_t listener_origin;
+extern vec3_t listener_forward;
+extern vec3_t listener_right;
+extern vec3_t listener_up;
+extern volatile dma_t *shm;
+extern volatile dma_t sn;
+extern vec_t sound_nominal_clip_dist;
+
+extern	cvar_t loadas8bit;
+extern	cvar_t bgmvolume;
+extern	cvar_t volume;
+
+extern qboolean	snd_initialized;
+
+extern int		snd_blocked;
+
+void S_LocalSound (char *s);
+sfxcache_t *S_LoadSound (sfx_t *s);
+
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength);
+
+void SND_InitScaletable (void);
+void SNDDMA_Submit(void);
+
+void S_AmbientOff (void);
+void S_AmbientOn (void);
+
+#endif
diff --git a/apps/plugins/sdl/progs/quake/r_aclip.c b/apps/plugins/sdl/progs/quake/r_aclip.c
new file mode 100644
index 0000000..61fef0c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_aclip.c
@@ -0,0 +1,350 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_aclip.c: clip routines for drawing Alias models directly to the screen
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+static finalvert_t		fv[2][8];
+static auxvert_t		av[8];
+
+void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av);
+void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out);
+void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out);
+void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out);
+void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out);
+
+
+/*
+================
+R_Alias_clip_z
+
+pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex
+================
+*/
+void R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
+{
+	float		scale;
+	auxvert_t	*pav0, *pav1, avout;
+
+	pav0 = &av[pfv0 - &fv[0][0]];
+	pav1 = &av[pfv1 - &fv[0][0]];
+
+	if (pfv0->v[1] >= pfv1->v[1])
+	{
+		scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) /
+				(pav1->fv[2] - pav0->fv[2]);
+	
+		avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale;
+		avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale;
+		avout.fv[2] = ALIAS_Z_CLIP_PLANE;
+	
+		out->v[2] =	pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale;
+		out->v[3] =	pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale;
+		out->v[4] =	pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale;
+	}
+	else
+	{
+		scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) /
+				(pav0->fv[2] - pav1->fv[2]);
+	
+		avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale;
+		avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale;
+		avout.fv[2] = ALIAS_Z_CLIP_PLANE;
+	
+		out->v[2] =	pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale;
+		out->v[3] =	pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale;
+		out->v[4] =	pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale;
+	}
+
+	R_AliasProjectFinalVert (out, &avout);
+
+	if (out->v[0] < r_refdef.aliasvrect.x)
+		out->flags |= ALIAS_LEFT_CLIP;
+	if (out->v[1] < r_refdef.aliasvrect.y)
+		out->flags |= ALIAS_TOP_CLIP;
+	if (out->v[0] > r_refdef.aliasvrectright)
+		out->flags |= ALIAS_RIGHT_CLIP;
+	if (out->v[1] > r_refdef.aliasvrectbottom)
+		out->flags |= ALIAS_BOTTOM_CLIP;	
+}
+
+
+#if	!id386
+
+void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
+{
+	float		scale;
+	int			i;
+
+	if (pfv0->v[1] >= pfv1->v[1])
+	{
+		scale = (float)(r_refdef.aliasvrect.x - pfv0->v[0]) /
+				(pfv1->v[0] - pfv0->v[0]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5;
+	}
+	else
+	{
+		scale = (float)(r_refdef.aliasvrect.x - pfv1->v[0]) /
+				(pfv0->v[0] - pfv1->v[0]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5;
+	}
+}
+
+
+void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out)
+{
+	float		scale;
+	int			i;
+
+	if (pfv0->v[1] >= pfv1->v[1])
+	{
+		scale = (float)(r_refdef.aliasvrectright - pfv0->v[0]) /
+				(pfv1->v[0] - pfv0->v[0]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5;
+	}
+	else
+	{
+		scale = (float)(r_refdef.aliasvrectright - pfv1->v[0]) /
+				(pfv0->v[0] - pfv1->v[0]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5;
+	}
+}
+
+
+void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
+{
+	float		scale;
+	int			i;
+
+	if (pfv0->v[1] >= pfv1->v[1])
+	{
+		scale = (float)(r_refdef.aliasvrect.y - pfv0->v[1]) /
+				(pfv1->v[1] - pfv0->v[1]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5;
+	}
+	else
+	{
+		scale = (float)(r_refdef.aliasvrect.y - pfv1->v[1]) /
+				(pfv0->v[1] - pfv1->v[1]);
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5;
+	}
+}
+
+
+void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1,
+	finalvert_t *out)
+{
+	float		scale;
+	int			i;
+
+	if (pfv0->v[1] >= pfv1->v[1])
+	{
+		scale = (float)(r_refdef.aliasvrectbottom - pfv0->v[1]) /
+				(pfv1->v[1] - pfv0->v[1]);
+
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5;
+	}
+	else
+	{
+		scale = (float)(r_refdef.aliasvrectbottom - pfv1->v[1]) /
+				(pfv0->v[1] - pfv1->v[1]);
+
+		for (i=0 ; i<6 ; i++)
+			out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5;
+	}
+}
+
+#endif
+
+
+int R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count,
+	void(*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) )
+{
+	int			i,j,k;
+	int			flags, oldflags;
+	
+	j = count-1;
+	k = 0;
+	for (i=0 ; i<count ; j = i, i++)
+	{
+		oldflags = in[j].flags & flag;
+		flags = in[i].flags & flag;
+
+		if (flags && oldflags)
+			continue;
+		if (oldflags ^ flags)
+		{
+			clip (&in[j], &in[i], &out[k]);
+			out[k].flags = 0;
+			if (out[k].v[0] < r_refdef.aliasvrect.x)
+				out[k].flags |= ALIAS_LEFT_CLIP;
+			if (out[k].v[1] < r_refdef.aliasvrect.y)
+				out[k].flags |= ALIAS_TOP_CLIP;
+			if (out[k].v[0] > r_refdef.aliasvrectright)
+				out[k].flags |= ALIAS_RIGHT_CLIP;
+			if (out[k].v[1] > r_refdef.aliasvrectbottom)
+				out[k].flags |= ALIAS_BOTTOM_CLIP;	
+			k++;
+		}
+		if (!flags)
+		{
+			out[k] = in[i];
+			k++;
+		}
+	}
+	
+	return k;
+}
+
+
+/*
+================
+R_AliasClipTriangle
+================
+*/
+void R_AliasClipTriangle (mtriangle_t *ptri)
+{
+	int				i, k, pingpong;
+	mtriangle_t		mtri;
+	unsigned		clipflags;
+
+// copy vertexes and fix seam texture coordinates
+	if (ptri->facesfront)
+	{
+		fv[0][0] = pfinalverts[ptri->vertindex[0]];
+		fv[0][1] = pfinalverts[ptri->vertindex[1]];
+		fv[0][2] = pfinalverts[ptri->vertindex[2]];
+	}
+	else
+	{
+		for (i=0 ; i<3 ; i++)
+		{
+			fv[0][i] = pfinalverts[ptri->vertindex[i]];
+	
+			if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM) )
+				fv[0][i].v[2] += r_affinetridesc.seamfixupX16;
+		}
+	}
+
+// clip
+	clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags;
+
+	if (clipflags & ALIAS_Z_CLIP)
+	{
+		for (i=0 ; i<3 ; i++)
+			av[i] = pauxverts[ptri->vertindex[i]];
+
+		k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z);
+		if (k == 0)
+			return;
+
+		pingpong = 1;
+		clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags;
+	}
+	else
+	{
+		pingpong = 0;
+		k = 3;
+	}
+
+	if (clipflags & ALIAS_LEFT_CLIP)
+	{
+		k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1],
+							ALIAS_LEFT_CLIP, k, R_Alias_clip_left);
+		if (k == 0)
+			return;
+
+		pingpong ^= 1;
+	}
+
+	if (clipflags & ALIAS_RIGHT_CLIP)
+	{
+		k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1],
+							ALIAS_RIGHT_CLIP, k, R_Alias_clip_right);
+		if (k == 0)
+			return;
+
+		pingpong ^= 1;
+	}
+
+	if (clipflags & ALIAS_BOTTOM_CLIP)
+	{
+		k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1],
+							ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom);
+		if (k == 0)
+			return;
+
+		pingpong ^= 1;
+	}
+
+	if (clipflags & ALIAS_TOP_CLIP)
+	{
+		k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1],
+							ALIAS_TOP_CLIP, k, R_Alias_clip_top);
+		if (k == 0)
+			return;
+
+		pingpong ^= 1;
+	}
+
+	for (i=0 ; i<k ; i++)
+	{
+		if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x)
+			fv[pingpong][i].v[0] = r_refdef.aliasvrect.x;
+		else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright)
+			fv[pingpong][i].v[0] = r_refdef.aliasvrectright;
+
+		if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y)
+			fv[pingpong][i].v[1] = r_refdef.aliasvrect.y;
+		else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom)
+			fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom;
+
+		fv[pingpong][i].flags = 0;
+	}
+
+// draw triangles
+	mtri.facesfront = ptri->facesfront;
+	r_affinetridesc.ptriangles = &mtri;
+	r_affinetridesc.pfinalverts = fv[pingpong];
+
+// FIXME: do all at once as trifan?
+	mtri.vertindex[0] = 0;
+	for (i=1 ; i<k-1 ; i++)
+	{
+		mtri.vertindex[1] = i;
+		mtri.vertindex[2] = i+1;
+		D_PolysetDraw ();
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_alias.c b/apps/plugins/sdl/progs/quake/r_alias.c
new file mode 100644
index 0000000..8607419
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_alias.c
@@ -0,0 +1,753 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_alias.c: routines for setting up to draw alias models
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"	// FIXME: shouldn't be needed (is needed for patch
+						// right now, but that should move)
+
+#define LIGHT_MIN	5		// lowest light value we'll allow, to avoid the
+							//  need for inner-loop light clamping
+
+mtriangle_t		*ptriangles;
+affinetridesc_t	r_affinetridesc;
+
+void *			acolormap;	// FIXME: should go away
+
+trivertx_t		*r_apverts;
+
+// TODO: these probably will go away with optimized rasterization
+mdl_t				*pmdl;
+vec3_t				r_plightvec;
+int					r_ambientlight;
+float				r_shadelight;
+aliashdr_t			*paliashdr;
+finalvert_t			*pfinalverts;
+auxvert_t			*pauxverts;
+static float		ziscale;
+static model_t		*pmodel;
+
+static vec3_t		alias_forward, alias_right, alias_up;
+
+static maliasskindesc_t	*pskindesc;
+
+int				r_amodels_drawn;
+int				a_skinwidth;
+int				r_anumverts;
+
+float	aliastransform[3][4];
+
+typedef struct {
+	int	index0;
+	int	index1;
+} aedge_t;
+
+static aedge_t	aedges[12] = {
+{0, 1}, {1, 2}, {2, 3}, {3, 0},
+{4, 5}, {5, 6}, {6, 7}, {7, 4},
+{0, 5}, {1, 4}, {2, 7}, {3, 6}
+};
+
+#define NUMVERTEXNORMALS	162
+
+float	r_avertexnormals[NUMVERTEXNORMALS][3] = {
+#include "anorms.h"
+};
+
+void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv,
+	stvert_t *pstverts);
+void R_AliasSetUpTransform (int trivial_accept);
+void R_AliasTransformVector (vec3_t in, vec3_t out);
+void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av,
+	trivertx_t *pverts, stvert_t *pstverts);
+void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av);
+
+
+/*
+================
+R_AliasCheckBBox
+================
+*/
+qboolean R_AliasCheckBBox (void)
+{
+	int					i, flags, frame, numv;
+	aliashdr_t			*pahdr;
+	float				zi, basepts[8][3], v0, v1, frac;
+	finalvert_t			*pv0, *pv1, viewpts[16];
+	auxvert_t			*pa0, *pa1, viewaux[16];
+	maliasframedesc_t	*pframedesc;
+	qboolean			zclipped, zfullyclipped;
+	unsigned			anyclip, allclip;
+	int					minz;
+	
+// expand, rotate, and translate points into worldspace
+
+	currententity->trivial_accept = 0;
+	pmodel = currententity->model;
+	pahdr = Mod_Extradata (pmodel);
+	pmdl = (mdl_t *)((byte *)pahdr + pahdr->model);
+
+	R_AliasSetUpTransform (0);
+
+// construct the base bounding box for this frame
+	frame = currententity->frame;
+// TODO: don't repeat this check when drawing?
+	if ((frame >= pmdl->numframes) || (frame < 0))
+	{
+		Con_DPrintf ("No such frame %d %s\n", frame,
+				pmodel->name);
+		frame = 0;
+	}
+
+	pframedesc = &pahdr->frames[frame];
+
+// x worldspace coordinates
+	basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] =
+			(float)pframedesc->bboxmin.v[0];
+	basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] =
+			(float)pframedesc->bboxmax.v[0];
+
+// y worldspace coordinates
+	basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] =
+			(float)pframedesc->bboxmin.v[1];
+	basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] =
+			(float)pframedesc->bboxmax.v[1];
+
+// z worldspace coordinates
+	basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] =
+			(float)pframedesc->bboxmin.v[2];
+	basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] =
+			(float)pframedesc->bboxmax.v[2];
+
+	zclipped = false;
+	zfullyclipped = true;
+
+	minz = 9999;
+	for (i=0; i<8 ; i++)
+	{
+		R_AliasTransformVector  (&basepts[i][0], &viewaux[i].fv[0]);
+
+		if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE)
+		{
+		// we must clip points that are closer than the near clip plane
+			viewpts[i].flags = ALIAS_Z_CLIP;
+			zclipped = true;
+		}
+		else
+		{
+			if (viewaux[i].fv[2] < minz)
+				minz = viewaux[i].fv[2];
+			viewpts[i].flags = 0;
+			zfullyclipped = false;
+		}
+	}
+
+	
+	if (zfullyclipped)
+	{
+		return false;	// everything was near-z-clipped
+	}
+
+	numv = 8;
+
+	if (zclipped)
+	{
+	// organize points by edges, use edges to get new points (possible trivial
+	// reject)
+		for (i=0 ; i<12 ; i++)
+		{
+		// edge endpoints
+			pv0 = &viewpts[aedges[i].index0];
+			pv1 = &viewpts[aedges[i].index1];
+			pa0 = &viewaux[aedges[i].index0];
+			pa1 = &viewaux[aedges[i].index1];
+
+		// if one end is clipped and the other isn't, make a new point
+			if (pv0->flags ^ pv1->flags)
+			{
+				frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) /
+					   (pa1->fv[2] - pa0->fv[2]);
+				viewaux[numv].fv[0] = pa0->fv[0] +
+						(pa1->fv[0] - pa0->fv[0]) * frac;
+				viewaux[numv].fv[1] = pa0->fv[1] +
+						(pa1->fv[1] - pa0->fv[1]) * frac;
+				viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE;
+				viewpts[numv].flags = 0;
+				numv++;
+			}
+		}
+	}
+
+// project the vertices that remain after clipping
+	anyclip = 0;
+	allclip = ALIAS_XY_CLIP_MASK;
+
+// TODO: probably should do this loop in ASM, especially if we use floats
+	for (i=0 ; i<numv ; i++)
+	{
+	// we don't need to bother with vertices that were z-clipped
+		if (viewpts[i].flags & ALIAS_Z_CLIP)
+			continue;
+
+		zi = 1.0 / viewaux[i].fv[2];
+
+	// FIXME: do with chop mode in ASM, or convert to float
+		v0 = (viewaux[i].fv[0] * xscale * zi) + xcenter;
+		v1 = (viewaux[i].fv[1] * yscale * zi) + ycenter;
+
+		flags = 0;
+
+		if (v0 < r_refdef.fvrectx)
+			flags |= ALIAS_LEFT_CLIP;
+		if (v1 < r_refdef.fvrecty)
+			flags |= ALIAS_TOP_CLIP;
+		if (v0 > r_refdef.fvrectright)
+			flags |= ALIAS_RIGHT_CLIP;
+		if (v1 > r_refdef.fvrectbottom)
+			flags |= ALIAS_BOTTOM_CLIP;
+
+		anyclip |= flags;
+		allclip &= flags;
+	}
+
+	if (allclip)
+		return false;	// trivial reject off one side
+
+	currententity->trivial_accept = !anyclip & !zclipped;
+
+	if (currententity->trivial_accept)
+	{
+		if (minz > (r_aliastransition + (pmdl->size * r_resfudge)))
+		{
+			currententity->trivial_accept |= 2;
+		}
+	}
+
+	return true;
+}
+
+
+/*
+================
+R_AliasTransformVector
+================
+*/
+void R_AliasTransformVector (vec3_t in, vec3_t out)
+{
+	out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3];
+	out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3];
+	out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3];
+}
+
+
+/*
+================
+R_AliasPreparePoints
+
+General clipped case
+================
+*/
+void R_AliasPreparePoints (void)
+{
+	int			i;
+	stvert_t	*pstverts;
+	finalvert_t	*fv;
+	auxvert_t	*av;
+	mtriangle_t	*ptri;
+	finalvert_t	*pfv[3];
+
+	pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts);
+	r_anumverts = pmdl->numverts;
+ 	fv = pfinalverts;
+	av = pauxverts;
+
+	for (i=0 ; i<r_anumverts ; i++, fv++, av++, r_apverts++, pstverts++)
+	{
+		R_AliasTransformFinalVert (fv, av, r_apverts, pstverts);
+		if (av->fv[2] < ALIAS_Z_CLIP_PLANE)
+			fv->flags |= ALIAS_Z_CLIP;
+		else
+		{
+			 R_AliasProjectFinalVert (fv, av);
+
+			if (fv->v[0] < r_refdef.aliasvrect.x)
+				fv->flags |= ALIAS_LEFT_CLIP;
+			if (fv->v[1] < r_refdef.aliasvrect.y)
+				fv->flags |= ALIAS_TOP_CLIP;
+			if (fv->v[0] > r_refdef.aliasvrectright)
+				fv->flags |= ALIAS_RIGHT_CLIP;
+			if (fv->v[1] > r_refdef.aliasvrectbottom)
+				fv->flags |= ALIAS_BOTTOM_CLIP;	
+		}
+	}
+
+//
+// clip and draw all triangles
+//
+	r_affinetridesc.numtriangles = 1;
+
+	ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles);
+	for (i=0 ; i<pmdl->numtris ; i++, ptri++)
+	{
+		pfv[0] = &pfinalverts[ptri->vertindex[0]];
+		pfv[1] = &pfinalverts[ptri->vertindex[1]];
+		pfv[2] = &pfinalverts[ptri->vertindex[2]];
+
+		if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) )
+			continue;		// completely clipped
+		
+		if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) &
+			(ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) )
+		{	// totally unclipped
+			r_affinetridesc.pfinalverts = pfinalverts;
+			r_affinetridesc.ptriangles = ptri;
+			D_PolysetDraw ();
+		}
+		else		
+		{	// partially clipped
+			R_AliasClipTriangle (ptri);
+		}
+	}
+}
+
+
+/*
+================
+R_AliasSetUpTransform
+================
+*/
+void R_AliasSetUpTransform (int trivial_accept)
+{
+	int				i;
+	float			rotationmatrix[3][4], t2matrix[3][4];
+	static float	tmatrix[3][4];
+	static float	viewmatrix[3][4];
+	vec3_t			angles;
+
+// TODO: should really be stored with the entity instead of being reconstructed
+// TODO: should use a look-up table
+// TODO: could cache lazily, stored in the entity
+
+	angles[ROLL] = currententity->angles[ROLL];
+	angles[PITCH] = -currententity->angles[PITCH];
+	angles[YAW] = currententity->angles[YAW];
+	AngleVectors (angles, alias_forward, alias_right, alias_up);
+
+	tmatrix[0][0] = pmdl->scale[0];
+	tmatrix[1][1] = pmdl->scale[1];
+	tmatrix[2][2] = pmdl->scale[2];
+
+	tmatrix[0][3] = pmdl->scale_origin[0];
+	tmatrix[1][3] = pmdl->scale_origin[1];
+	tmatrix[2][3] = pmdl->scale_origin[2];
+
+// TODO: can do this with simple matrix rearrangement
+
+	for (i=0 ; i<3 ; i++)
+	{
+		t2matrix[i][0] = alias_forward[i];
+		t2matrix[i][1] = -alias_right[i];
+		t2matrix[i][2] = alias_up[i];
+	}
+
+	t2matrix[0][3] = -modelorg[0];
+	t2matrix[1][3] = -modelorg[1];
+	t2matrix[2][3] = -modelorg[2];
+
+// FIXME: can do more efficiently than full concatenation
+	R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix);
+
+// TODO: should be global, set when vright, etc., set
+	VectorCopy (vright, viewmatrix[0]);
+	VectorCopy (vup, viewmatrix[1]);
+	VectorInverse (viewmatrix[1]);
+	VectorCopy (vpn, viewmatrix[2]);
+
+//	viewmatrix[0][3] = 0;
+//	viewmatrix[1][3] = 0;
+//	viewmatrix[2][3] = 0;
+
+	R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform);
+
+// do the scaling up of x and y to screen coordinates as part of the transform
+// for the unclipped case (it would mess up clipping in the clipped case).
+// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y
+// correspondingly so the projected x and y come out right
+// FIXME: make this work for clipped case too?
+	if (trivial_accept)
+	{
+		for (i=0 ; i<4 ; i++)
+		{
+			aliastransform[0][i] *= aliasxscale *
+					(1.0 / ((float)0x8000 * 0x10000));
+			aliastransform[1][i] *= aliasyscale *
+					(1.0 / ((float)0x8000 * 0x10000));
+			aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000);
+
+		}
+	}
+}
+
+
+/*
+================
+R_AliasTransformFinalVert
+================
+*/
+void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av,
+	trivertx_t *pverts, stvert_t *pstverts)
+{
+	int		temp;
+	float	lightcos, *plightnormal;
+
+	av->fv[0] = DotProduct(pverts->v, aliastransform[0]) +
+			aliastransform[0][3];
+	av->fv[1] = DotProduct(pverts->v, aliastransform[1]) +
+			aliastransform[1][3];
+	av->fv[2] = DotProduct(pverts->v, aliastransform[2]) +
+			aliastransform[2][3];
+
+	fv->v[2] = pstverts->s;
+	fv->v[3] = pstverts->t;
+
+	fv->flags = pstverts->onseam;
+
+// lighting
+	plightnormal = r_avertexnormals[pverts->lightnormalindex];
+	lightcos = DotProduct (plightnormal, r_plightvec);
+	temp = r_ambientlight;
+
+	if (lightcos < 0)
+	{
+		temp += (int)(r_shadelight * lightcos);
+
+	// clamp; because we limited the minimum ambient and shading light, we
+	// don't have to clamp low light, just bright
+		if (temp < 0)
+			temp = 0;
+	}
+
+	fv->v[4] = temp;
+}
+
+
+#if	!id386
+
+/*
+================
+R_AliasTransformAndProjectFinalVerts
+================
+*/
+void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts)
+{
+	int			i, temp;
+	float		lightcos, *plightnormal, zi;
+	trivertx_t	*pverts;
+
+	pverts = r_apverts;
+
+	for (i=0 ; i<r_anumverts ; i++, fv++, pverts++, pstverts++)
+	{
+	// transform and project
+		zi = 1.0 / (DotProduct(pverts->v, aliastransform[2]) +
+				aliastransform[2][3]);
+
+	// x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is
+	// scaled up by 1/2**31, and the scaling cancels out for x and y in the
+	// projection
+		fv->v[5] = zi;
+
+		fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) +
+				aliastransform[0][3]) * zi) + aliasxcenter;
+		fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) +
+				aliastransform[1][3]) * zi) + aliasycenter;
+
+		fv->v[2] = pstverts->s;
+		fv->v[3] = pstverts->t;
+		fv->flags = pstverts->onseam;
+
+	// lighting
+		plightnormal = r_avertexnormals[pverts->lightnormalindex];
+		lightcos = DotProduct (plightnormal, r_plightvec);
+		temp = r_ambientlight;
+
+		if (lightcos < 0)
+		{
+			temp += (int)(r_shadelight * lightcos);
+
+		// clamp; because we limited the minimum ambient and shading light, we
+		// don't have to clamp low light, just bright
+			if (temp < 0)
+				temp = 0;
+		}
+
+		fv->v[4] = temp;
+	}
+}
+
+#endif
+
+
+/*
+================
+R_AliasProjectFinalVert
+================
+*/
+void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av)
+{
+	float	zi;
+
+// project points
+	zi = 1.0 / av->fv[2];
+
+	fv->v[5] = zi * ziscale;
+
+	fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter;
+	fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter;
+}
+
+
+/*
+================
+R_AliasPrepareUnclippedPoints
+================
+*/
+void R_AliasPrepareUnclippedPoints (void)
+{
+	stvert_t	*pstverts;
+	finalvert_t	*fv;
+
+	pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts);
+	r_anumverts = pmdl->numverts;
+// FIXME: just use pfinalverts directly?
+	fv = pfinalverts;
+
+	R_AliasTransformAndProjectFinalVerts (fv, pstverts);
+
+	if (r_affinetridesc.drawtype)
+		D_PolysetDrawFinalVerts (fv, r_anumverts);
+
+	r_affinetridesc.pfinalverts = pfinalverts;
+	r_affinetridesc.ptriangles = (mtriangle_t *)
+			((byte *)paliashdr + paliashdr->triangles);
+	r_affinetridesc.numtriangles = pmdl->numtris;
+
+	D_PolysetDraw ();
+}
+
+/*
+===============
+R_AliasSetupSkin
+===============
+*/
+void R_AliasSetupSkin (void)
+{
+	int					skinnum;
+	int					i, numskins;
+	maliasskingroup_t	*paliasskingroup;
+	float				*pskinintervals, fullskininterval;
+	float				skintargettime, skintime;
+
+	skinnum = currententity->skinnum;
+	if ((skinnum >= pmdl->numskins) || (skinnum < 0))
+	{
+		Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum);
+		skinnum = 0;
+	}
+
+	pskindesc = ((maliasskindesc_t *)
+			((byte *)paliashdr + paliashdr->skindesc)) + skinnum;
+	a_skinwidth = pmdl->skinwidth;
+
+	if (pskindesc->type == ALIAS_SKIN_GROUP)
+	{
+		paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr +
+				pskindesc->skin);
+		pskinintervals = (float *)
+				((byte *)paliashdr + paliasskingroup->intervals);
+		numskins = paliasskingroup->numskins;
+		fullskininterval = pskinintervals[numskins-1];
+	
+		skintime = cl.time + currententity->syncbase;
+	
+	// when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval
+	// values are positive, so we don't have to worry about division by 0
+		skintargettime = skintime -
+				((int)(skintime / fullskininterval)) * fullskininterval;
+	
+		for (i=0 ; i<(numskins-1) ; i++)
+		{
+			if (pskinintervals[i] > skintargettime)
+				break;
+		}
+	
+		pskindesc = &paliasskingroup->skindescs[i];
+	}
+
+	r_affinetridesc.pskindesc = pskindesc;
+	r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin);
+	r_affinetridesc.skinwidth = a_skinwidth;
+	r_affinetridesc.seamfixupX16 =  (a_skinwidth >> 1) << 16;
+	r_affinetridesc.skinheight = pmdl->skinheight;
+}
+
+/*
+================
+R_AliasSetupLighting
+================
+*/
+void R_AliasSetupLighting (alight_t *plighting)
+{
+
+// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have
+// to clamp off the bottom
+	r_ambientlight = plighting->ambientlight;
+
+	if (r_ambientlight < LIGHT_MIN)
+		r_ambientlight = LIGHT_MIN;
+
+	r_ambientlight = (255 - r_ambientlight) << VID_CBITS;
+
+	if (r_ambientlight < LIGHT_MIN)
+		r_ambientlight = LIGHT_MIN;
+
+	r_shadelight = plighting->shadelight;
+
+	if (r_shadelight < 0)
+		r_shadelight = 0;
+
+	r_shadelight *= VID_GRADES;
+
+// rotate the lighting vector into the model's frame of reference
+	r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward);
+	r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right);
+	r_plightvec[2] = DotProduct (plighting->plightvec, alias_up);
+}
+
+/*
+=================
+R_AliasSetupFrame
+
+set r_apverts
+=================
+*/
+void R_AliasSetupFrame (void)
+{
+	int				frame;
+	int				i, numframes;
+	maliasgroup_t	*paliasgroup;
+	float			*pintervals, fullinterval, targettime, time;
+
+	frame = currententity->frame;
+	if ((frame >= pmdl->numframes) || (frame < 0))
+	{
+		Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame);
+		frame = 0;
+	}
+
+	if (paliashdr->frames[frame].type == ALIAS_SINGLE)
+	{
+		r_apverts = (trivertx_t *)
+				((byte *)paliashdr + paliashdr->frames[frame].frame);
+		return;
+	}
+	
+	paliasgroup = (maliasgroup_t *)
+				((byte *)paliashdr + paliashdr->frames[frame].frame);
+	pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals);
+	numframes = paliasgroup->numframes;
+	fullinterval = pintervals[numframes-1];
+
+	time = cl.time + currententity->syncbase;
+
+//
+// when loading in Mod_LoadAliasGroup, we guaranteed all interval values
+// are positive, so we don't have to worry about division by 0
+//
+	targettime = time - ((int)(time / fullinterval)) * fullinterval;
+
+	for (i=0 ; i<(numframes-1) ; i++)
+	{
+		if (pintervals[i] > targettime)
+			break;
+	}
+
+	r_apverts = (trivertx_t *)
+				((byte *)paliashdr + paliasgroup->frames[i].frame);
+}
+
+
+/*
+================
+R_AliasDrawModel
+================
+*/
+void R_AliasDrawModel (alight_t *plighting)
+{
+	finalvert_t		finalverts[MAXALIASVERTS +
+						((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1];
+	auxvert_t		auxverts[MAXALIASVERTS];
+
+	r_amodels_drawn++;
+
+// cache align
+	pfinalverts = (finalvert_t *)
+			(((long)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
+	pauxverts = &auxverts[0];
+
+	paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);
+	pmdl = (mdl_t *)((byte *)paliashdr + paliashdr->model);
+
+	R_AliasSetupSkin ();
+	R_AliasSetUpTransform (currententity->trivial_accept);
+	R_AliasSetupLighting (plighting);
+	R_AliasSetupFrame ();
+
+	if (!currententity->colormap)
+		Sys_Error ("R_AliasDrawModel: !currententity->colormap");
+
+	r_affinetridesc.drawtype = (currententity->trivial_accept == 3) &&
+			r_recursiveaffinetriangles;
+
+	if (r_affinetridesc.drawtype)
+	{
+		D_PolysetUpdateTables ();		// FIXME: precalc...
+	}
+	else
+	{
+#if	id386
+		D_Aff8Patch (currententity->colormap);
+#endif
+	}
+
+	acolormap = currententity->colormap;
+
+	if (currententity != &cl.viewent)
+		ziscale = (float)0x8000 * (float)0x10000;
+	else
+		ziscale = (float)0x8000 * (float)0x10000 * 3.0;
+
+	if (currententity->trivial_accept)
+		R_AliasPrepareUnclippedPoints ();
+	else
+		R_AliasPreparePoints ();
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_bsp.c b/apps/plugins/sdl/progs/quake/r_bsp.c
new file mode 100644
index 0000000..f8c38cf
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_bsp.c
@@ -0,0 +1,675 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_bsp.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+//
+// current entity info
+//
+qboolean		insubmodel;
+entity_t		*currententity;
+vec3_t			modelorg, base_modelorg;
+								// modelorg is the viewpoint reletive to
+								// the currently rendering entity
+vec3_t			r_entorigin;	// the currently rendering entity in world
+								// coordinates
+
+float			entity_rotation[3][3];
+
+vec3_t			r_worldmodelorg;
+
+int				r_currentbkey;
+
+typedef int solidstate_t;
+enum {touchessolid, drawnode, nodrawnode};
+
+#define MAX_BMODEL_VERTS	500			// 6K
+#define MAX_BMODEL_EDGES	1000		// 12K
+
+static mvertex_t	*pbverts;
+static bedge_t		*pbedges;
+static int			numbverts, numbedges;
+
+static mvertex_t	*pfrontenter, *pfrontexit;
+
+static qboolean		makeclippededge;
+
+
+//===========================================================================
+
+/*
+================
+R_EntityRotate
+================
+*/
+void R_EntityRotate (vec3_t vec)
+{
+	vec3_t	tvec;
+
+	VectorCopy (vec, tvec);
+	vec[0] = DotProduct (entity_rotation[0], tvec);
+	vec[1] = DotProduct (entity_rotation[1], tvec);
+	vec[2] = DotProduct (entity_rotation[2], tvec);
+}
+
+
+/*
+================
+R_RotateBmodel
+================
+*/
+void R_RotateBmodel (void)
+{
+	float	angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3];
+
+// TODO: should use a look-up table
+// TODO: should really be stored with the entity instead of being reconstructed
+// TODO: could cache lazily, stored in the entity
+// TODO: share work with R_SetUpAliasTransform
+
+// yaw
+	angle = currententity->angles[YAW];		
+	angle = angle * M_PI*2 / 360;
+	s = sin(angle);
+	c = cos(angle);
+
+	temp1[0][0] = c;
+	temp1[0][1] = s;
+	temp1[0][2] = 0;
+	temp1[1][0] = -s;
+	temp1[1][1] = c;
+	temp1[1][2] = 0;
+	temp1[2][0] = 0;
+	temp1[2][1] = 0;
+	temp1[2][2] = 1;
+
+
+// pitch
+	angle = currententity->angles[PITCH];		
+	angle = angle * M_PI*2 / 360;
+	s = sin(angle);
+	c = cos(angle);
+
+	temp2[0][0] = c;
+	temp2[0][1] = 0;
+	temp2[0][2] = -s;
+	temp2[1][0] = 0;
+	temp2[1][1] = 1;
+	temp2[1][2] = 0;
+	temp2[2][0] = s;
+	temp2[2][1] = 0;
+	temp2[2][2] = c;
+
+	R_ConcatRotations (temp2, temp1, temp3);
+
+// roll
+	angle = currententity->angles[ROLL];		
+	angle = angle * M_PI*2 / 360;
+	s = sin(angle);
+	c = cos(angle);
+
+	temp1[0][0] = 1;
+	temp1[0][1] = 0;
+	temp1[0][2] = 0;
+	temp1[1][0] = 0;
+	temp1[1][1] = c;
+	temp1[1][2] = s;
+	temp1[2][0] = 0;
+	temp1[2][1] = -s;
+	temp1[2][2] = c;
+
+	R_ConcatRotations (temp1, temp3, entity_rotation);
+
+//
+// rotate modelorg and the transformation matrix
+//
+	R_EntityRotate (modelorg);
+	R_EntityRotate (vpn);
+	R_EntityRotate (vright);
+	R_EntityRotate (vup);
+
+	R_TransformFrustum ();
+}
+
+
+/*
+================
+R_RecursiveClipBPoly
+================
+*/
+void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf)
+{
+	bedge_t		*psideedges[2], *pnextedge, *ptedge;
+	int			i, side, lastside;
+	float		dist, frac, lastdist;
+	mplane_t	*splitplane, tplane;
+	mvertex_t	*pvert, *plastvert, *ptvert;
+	mnode_t		*pn;
+
+	psideedges[0] = psideedges[1] = NULL;
+
+	makeclippededge = false;
+
+// transform the BSP plane into model space
+// FIXME: cache these?
+	splitplane = pnode->plane;
+	tplane.dist = splitplane->dist -
+			DotProduct(r_entorigin, splitplane->normal);
+	tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal);
+	tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal);
+	tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal);
+
+// clip edges to BSP plane
+	for ( ; pedges ; pedges = pnextedge)
+	{
+		pnextedge = pedges->pnext;
+
+	// set the status for the last point as the previous point
+	// FIXME: cache this stuff somehow?
+		plastvert = pedges->v[0];
+		lastdist = DotProduct (plastvert->position, tplane.normal) -
+				   tplane.dist;
+
+		if (lastdist > 0)
+			lastside = 0;
+		else
+			lastside = 1;
+
+		pvert = pedges->v[1];
+
+		dist = DotProduct (pvert->position, tplane.normal) - tplane.dist;
+
+		if (dist > 0)
+			side = 0;
+		else
+			side = 1;
+
+		if (side != lastside)
+		{
+		// clipped
+			if (numbverts >= MAX_BMODEL_VERTS)
+				return;
+
+		// generate the clipped vertex
+			frac = lastdist / (lastdist - dist);
+			ptvert = &pbverts[numbverts++];
+			ptvert->position[0] = plastvert->position[0] +
+					frac * (pvert->position[0] -
+					plastvert->position[0]);
+			ptvert->position[1] = plastvert->position[1] +
+					frac * (pvert->position[1] -
+					plastvert->position[1]);
+			ptvert->position[2] = plastvert->position[2] +
+					frac * (pvert->position[2] -
+					plastvert->position[2]);
+
+		// split into two edges, one on each side, and remember entering
+		// and exiting points
+		// FIXME: share the clip edge by having a winding direction flag?
+			if (numbedges >= (MAX_BMODEL_EDGES - 1))
+			{
+				Con_Printf ("Out of edges for bmodel\n");
+				return;
+			}
+
+			ptedge = &pbedges[numbedges];
+			ptedge->pnext = psideedges[lastside];
+			psideedges[lastside] = ptedge;
+			ptedge->v[0] = plastvert;
+			ptedge->v[1] = ptvert;
+
+			ptedge = &pbedges[numbedges + 1];
+			ptedge->pnext = psideedges[side];
+			psideedges[side] = ptedge;
+			ptedge->v[0] = ptvert;
+			ptedge->v[1] = pvert;
+
+			numbedges += 2;
+
+			if (side == 0)
+			{
+			// entering for front, exiting for back
+				pfrontenter = ptvert;
+				makeclippededge = true;
+			}
+			else
+			{
+				pfrontexit = ptvert;
+				makeclippededge = true;
+			}
+		}
+		else
+		{
+		// add the edge to the appropriate side
+			pedges->pnext = psideedges[side];
+			psideedges[side] = pedges;
+		}
+	}
+
+// if anything was clipped, reconstitute and add the edges along the clip
+// plane to both sides (but in opposite directions)
+	if (makeclippededge)
+	{
+		if (numbedges >= (MAX_BMODEL_EDGES - 2))
+		{
+			Con_Printf ("Out of edges for bmodel\n");
+			return;
+		}
+
+		ptedge = &pbedges[numbedges];
+		ptedge->pnext = psideedges[0];
+		psideedges[0] = ptedge;
+		ptedge->v[0] = pfrontexit;
+		ptedge->v[1] = pfrontenter;
+
+		ptedge = &pbedges[numbedges + 1];
+		ptedge->pnext = psideedges[1];
+		psideedges[1] = ptedge;
+		ptedge->v[0] = pfrontenter;
+		ptedge->v[1] = pfrontexit;
+
+		numbedges += 2;
+	}
+
+// draw or recurse further
+	for (i=0 ; i<2 ; i++)
+	{
+		if (psideedges[i])
+		{
+		// draw if we've reached a non-solid leaf, done if all that's left is a
+		// solid leaf, and continue down the tree if it's not a leaf
+			pn = pnode->children[i];
+
+		// we're done with this branch if the node or leaf isn't in the PVS
+			if (pn->visframe == r_visframecount)
+			{
+				if (pn->contents < 0)
+				{
+					if (pn->contents != CONTENTS_SOLID)
+					{
+						r_currentbkey = ((mleaf_t *)pn)->key;
+						R_RenderBmodelFace (psideedges[i], psurf);
+					}
+				}
+				else
+				{
+					R_RecursiveClipBPoly (psideedges[i], pnode->children[i],
+									  psurf);
+				}
+			}
+		}
+	}
+}
+
+
+/*
+================
+R_DrawSolidClippedSubmodelPolygons
+================
+*/
+void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel)
+{
+	int			i, j, lindex;
+	vec_t		dot;
+	msurface_t	*psurf;
+	int			numsurfaces;
+	mplane_t	*pplane;
+	mvertex_t	bverts[MAX_BMODEL_VERTS];
+	bedge_t		bedges[MAX_BMODEL_EDGES], *pbedge;
+	medge_t		*pedge, *pedges;
+
+// FIXME: use bounding-box-based frustum clipping info?
+
+	psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
+	numsurfaces = pmodel->nummodelsurfaces;
+	pedges = pmodel->edges;
+
+	for (i=0 ; i<numsurfaces ; i++, psurf++)
+	{
+	// find which side of the node we are on
+		pplane = psurf->plane;
+
+		dot = DotProduct (modelorg, pplane->normal) - pplane->dist;
+
+	// draw the polygon
+		if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
+			(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
+		{
+		// FIXME: use bounding-box-based frustum clipping info?
+
+		// copy the edges to bedges, flipping if necessary so always
+		// clockwise winding
+		// FIXME: if edges and vertices get caches, these assignments must move
+		// outside the loop, and overflow checking must be done here
+			pbverts = bverts;
+			pbedges = bedges;
+			numbverts = numbedges = 0;
+
+			if (psurf->numedges > 0)
+			{
+				pbedge = &bedges[numbedges];
+				numbedges += psurf->numedges;
+
+				for (j=0 ; j<psurf->numedges ; j++)
+				{
+				   lindex = pmodel->surfedges[psurf->firstedge+j];
+
+					if (lindex > 0)
+					{
+						pedge = &pedges[lindex];
+						pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]];
+						pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]];
+					}
+					else
+					{
+						lindex = -lindex;
+						pedge = &pedges[lindex];
+						pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]];
+						pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]];
+					}
+
+					pbedge[j].pnext = &pbedge[j+1];
+				}
+
+				pbedge[j-1].pnext = NULL;	// mark end of edges
+
+				R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf);
+			}
+			else
+			{
+				Sys_Error ("no edges in bmodel");
+			}
+		}
+	}
+}
+
+
+/*
+================
+R_DrawSubmodelPolygons
+================
+*/
+void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags)
+{
+	int			i;
+	vec_t		dot;
+	msurface_t	*psurf;
+	int			numsurfaces;
+	mplane_t	*pplane;
+
+// FIXME: use bounding-box-based frustum clipping info?
+
+	psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
+	numsurfaces = pmodel->nummodelsurfaces;
+
+	for (i=0 ; i<numsurfaces ; i++, psurf++)
+	{
+	// find which side of the node we are on
+		pplane = psurf->plane;
+
+		dot = DotProduct (modelorg, pplane->normal) - pplane->dist;
+
+	// draw the polygon
+		if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
+			(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
+		{
+			r_currentkey = ((mleaf_t *)currententity->topnode)->key;
+
+		// FIXME: use bounding-box-based frustum clipping info?
+			R_RenderFace (psurf, clipflags);
+		}
+	}
+}
+
+
+/*
+================
+R_RecursiveWorldNode
+================
+*/
+void R_RecursiveWorldNode (mnode_t *node, int clipflags)
+{
+	int			i, c, side, *pindex;
+	vec3_t		acceptpt, rejectpt;
+	mplane_t	*plane;
+	msurface_t	*surf, **mark;
+	mleaf_t		*pleaf;
+	double		d, dot;
+
+	if (node->contents == CONTENTS_SOLID)
+		return;		// solid
+
+	if (node->visframe != r_visframecount)
+		return;
+
+// cull the clipping planes if not trivial accept
+// FIXME: the compiler is doing a lousy job of optimizing here; it could be
+//  twice as fast in ASM
+	if (clipflags)
+	{
+		for (i=0 ; i<4 ; i++)
+		{
+			if (! (clipflags & (1<<i)) )
+				continue;	// don't need to clip against it
+
+		// generate accept and reject points
+		// FIXME: do with fast look-ups or integer tests based on the sign bit
+		// of the floating point values
+
+			pindex = pfrustum_indexes[i];
+
+			rejectpt[0] = (float)node->minmaxs[pindex[0]];
+			rejectpt[1] = (float)node->minmaxs[pindex[1]];
+			rejectpt[2] = (float)node->minmaxs[pindex[2]];
+			
+			d = DotProduct (rejectpt, view_clipplanes[i].normal);
+			d -= view_clipplanes[i].dist;
+
+			if (d <= 0)
+				return;
+
+			acceptpt[0] = (float)node->minmaxs[pindex[3+0]];
+			acceptpt[1] = (float)node->minmaxs[pindex[3+1]];
+			acceptpt[2] = (float)node->minmaxs[pindex[3+2]];
+
+			d = DotProduct (acceptpt, view_clipplanes[i].normal);
+			d -= view_clipplanes[i].dist;
+
+			if (d >= 0)
+				clipflags &= ~(1<<i);	// node is entirely on screen
+		}
+	}
+	
+// if a leaf node, draw stuff
+	if (node->contents < 0)
+	{
+		pleaf = (mleaf_t *)node;
+
+		mark = pleaf->firstmarksurface;
+		c = pleaf->nummarksurfaces;
+
+		if (c)
+		{
+			do
+			{
+				(*mark)->visframe = r_framecount;
+				mark++;
+			} while (--c);
+		}
+
+	// deal with model fragments in this leaf
+		if (pleaf->efrags)
+		{
+			R_StoreEfrags (&pleaf->efrags);
+		}
+
+		pleaf->key = r_currentkey;
+		r_currentkey++;		// all bmodels in a leaf share the same key
+	}
+	else
+	{
+	// node is just a decision point, so go down the apropriate sides
+
+	// find which side of the node we are on
+		plane = node->plane;
+
+		switch (plane->type)
+		{
+		case PLANE_X:
+			dot = modelorg[0] - plane->dist;
+			break;
+		case PLANE_Y:
+			dot = modelorg[1] - plane->dist;
+			break;
+		case PLANE_Z:
+			dot = modelorg[2] - plane->dist;
+			break;
+		default:
+			dot = DotProduct (modelorg, plane->normal) - plane->dist;
+			break;
+		}
+	
+		if (dot >= 0)
+			side = 0;
+		else
+			side = 1;
+
+	// recurse down the children, front side first
+		R_RecursiveWorldNode (node->children[side], clipflags);
+
+	// draw stuff
+		c = node->numsurfaces;
+
+		if (c)
+		{
+			surf = cl.worldmodel->surfaces + node->firstsurface;
+
+			if (dot < -BACKFACE_EPSILON)
+			{
+				do
+				{
+					if ((surf->flags & SURF_PLANEBACK) &&
+						(surf->visframe == r_framecount))
+					{
+						if (r_drawpolys)
+						{
+							if (r_worldpolysbacktofront)
+							{
+								if (numbtofpolys < MAX_BTOFPOLYS)
+								{
+									pbtofpolys[numbtofpolys].clipflags =
+											clipflags;
+									pbtofpolys[numbtofpolys].psurf = surf;
+									numbtofpolys++;
+								}
+							}
+							else
+							{
+								R_RenderPoly (surf, clipflags);
+							}
+						}
+						else
+						{
+							R_RenderFace (surf, clipflags);
+						}
+					}
+
+					surf++;
+				} while (--c);
+			}
+			else if (dot > BACKFACE_EPSILON)
+			{
+				do
+				{
+					if (!(surf->flags & SURF_PLANEBACK) &&
+						(surf->visframe == r_framecount))
+					{
+						if (r_drawpolys)
+						{
+							if (r_worldpolysbacktofront)
+							{
+								if (numbtofpolys < MAX_BTOFPOLYS)
+								{
+									pbtofpolys[numbtofpolys].clipflags =
+											clipflags;
+									pbtofpolys[numbtofpolys].psurf = surf;
+									numbtofpolys++;
+								}
+							}
+							else
+							{
+								R_RenderPoly (surf, clipflags);
+							}
+						}
+						else
+						{
+							R_RenderFace (surf, clipflags);
+						}
+					}
+
+					surf++;
+				} while (--c);
+			}
+
+		// all surfaces on the same node share the same sequence number
+			r_currentkey++;
+		}
+
+	// recurse down the back side
+		R_RecursiveWorldNode (node->children[!side], clipflags);
+	}
+}
+
+
+
+/*
+================
+R_RenderWorld
+================
+*/
+void R_RenderWorld (void)
+{
+	int			i;
+	model_t		*clmodel;
+	btofpoly_t	btofpolys[MAX_BTOFPOLYS];
+
+	pbtofpolys = btofpolys;
+
+	currententity = &cl_entities[0];
+	VectorCopy (r_origin, modelorg);
+	clmodel = currententity->model;
+	r_pcurrentvertbase = clmodel->vertexes;
+
+	R_RecursiveWorldNode (clmodel->nodes, 15);
+
+// if the driver wants the polygons back to front, play the visible ones back
+// in that order
+	if (r_worldpolysbacktofront)
+	{
+		for (i=numbtofpolys-1 ; i>=0 ; i--)
+		{
+			R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags);
+		}
+	}
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/r_draw.c b/apps/plugins/sdl/progs/quake/r_draw.c
new file mode 100644
index 0000000..8789cc0
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_draw.c
@@ -0,0 +1,908 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// r_draw.c
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"	// FIXME: shouldn't need to include this
+
+#define MAXLEFTCLIPEDGES		100
+
+// !!! if these are changed, they must be changed in asm_draw.h too !!!
+#define FULLY_CLIPPED_CACHED	0x80000000
+#define FRAMECOUNT_MASK			0x7FFFFFFF
+
+unsigned int	cacheoffset;
+
+int			c_faceclip;					// number of faces clipped
+
+zpointdesc_t	r_zpointdesc;
+
+polydesc_t		r_polydesc;
+
+
+
+clipplane_t	*entity_clipplanes;
+clipplane_t	view_clipplanes[4];
+clipplane_t	world_clipplanes[16];
+
+medge_t			*r_pedge;
+
+qboolean		r_leftclipped, r_rightclipped;
+static qboolean	makeleftedge, makerightedge;
+qboolean		r_nearzionly;
+
+int		sintable[SIN_BUFFER_SIZE];
+int		intsintable[SIN_BUFFER_SIZE];
+
+mvertex_t	r_leftenter, r_leftexit;
+mvertex_t	r_rightenter, r_rightexit;
+
+typedef struct
+{
+	float	u,v;
+	int		ceilv;
+} evert_t;
+
+int				r_emitted;
+float			r_nearzi;
+float			r_u1, r_v1, r_lzi1;
+int				r_ceilv1;
+
+qboolean	r_lastvertvalid;
+
+
+#if	!id386
+
+/*
+================
+R_EmitEdge
+================
+*/
+void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1)
+{
+	edge_t	*edge, *pcheck;
+	int		u_check;
+	float	u, u_step;
+	vec3_t	local, transformed;
+	float	*world;
+	int		v, v2, ceilv0;
+	float	scale, lzi0, u0, v0;
+	int		side;
+
+	if (r_lastvertvalid)
+	{
+		u0 = r_u1;
+		v0 = r_v1;
+		lzi0 = r_lzi1;
+		ceilv0 = r_ceilv1;
+	}
+	else
+	{
+		world = &pv0->position[0];
+	
+	// transform and project
+		VectorSubtract (world, modelorg, local);
+		TransformVector (local, transformed);
+	
+		if (transformed[2] < NEAR_CLIP)
+			transformed[2] = NEAR_CLIP;
+	
+		lzi0 = 1.0 / transformed[2];
+	
+	// FIXME: build x/yscale into transform?
+		scale = xscale * lzi0;
+		u0 = (xcenter + scale*transformed[0]);
+		if (u0 < r_refdef.fvrectx_adj)
+			u0 = r_refdef.fvrectx_adj;
+		if (u0 > r_refdef.fvrectright_adj)
+			u0 = r_refdef.fvrectright_adj;
+	
+		scale = yscale * lzi0;
+		v0 = (ycenter - scale*transformed[1]);
+		if (v0 < r_refdef.fvrecty_adj)
+			v0 = r_refdef.fvrecty_adj;
+		if (v0 > r_refdef.fvrectbottom_adj)
+			v0 = r_refdef.fvrectbottom_adj;
+	
+		ceilv0 = (int) ceil(v0);
+	}
+
+	world = &pv1->position[0];
+
+// transform and project
+	VectorSubtract (world, modelorg, local);
+	TransformVector (local, transformed);
+
+	if (transformed[2] < NEAR_CLIP)
+		transformed[2] = NEAR_CLIP;
+
+	r_lzi1 = 1.0 / transformed[2];
+
+	scale = xscale * r_lzi1;
+	r_u1 = (xcenter + scale*transformed[0]);
+	if (r_u1 < r_refdef.fvrectx_adj)
+		r_u1 = r_refdef.fvrectx_adj;
+	if (r_u1 > r_refdef.fvrectright_adj)
+		r_u1 = r_refdef.fvrectright_adj;
+
+	scale = yscale * r_lzi1;
+	r_v1 = (ycenter - scale*transformed[1]);
+	if (r_v1 < r_refdef.fvrecty_adj)
+		r_v1 = r_refdef.fvrecty_adj;
+	if (r_v1 > r_refdef.fvrectbottom_adj)
+		r_v1 = r_refdef.fvrectbottom_adj;
+
+	if (r_lzi1 > lzi0)
+		lzi0 = r_lzi1;
+
+	if (lzi0 > r_nearzi)	// for mipmap finding
+		r_nearzi = lzi0;
+
+// for right edges, all we want is the effect on 1/z
+	if (r_nearzionly)
+		return;
+
+	r_emitted = 1;
+
+	r_ceilv1 = (int) ceil(r_v1);
+
+
+// create the edge
+	if (ceilv0 == r_ceilv1)
+	{
+	// we cache unclipped horizontal edges as fully clipped
+		if (cacheoffset != 0x7FFFFFFF)
+		{
+			cacheoffset = FULLY_CLIPPED_CACHED |
+					(r_framecount & FRAMECOUNT_MASK);
+		}
+
+		return;		// horizontal edge
+	}
+
+	side = ceilv0 > r_ceilv1;
+
+	edge = edge_p++;
+
+	edge->owner = r_pedge;
+
+	edge->nearzi = lzi0;
+
+	if (side == 0)
+	{
+	// trailing edge (go from p1 to p2)
+		v = ceilv0;
+		v2 = r_ceilv1 - 1;
+
+		edge->surfs[0] = surface_p - surfaces;
+		edge->surfs[1] = 0;
+
+		u_step = ((r_u1 - u0) / (r_v1 - v0));
+		u = u0 + ((float)v - v0) * u_step;
+	}
+	else
+	{
+	// leading edge (go from p2 to p1)
+		v2 = ceilv0 - 1;
+		v = r_ceilv1;
+
+		edge->surfs[0] = 0;
+		edge->surfs[1] = surface_p - surfaces;
+
+		u_step = ((u0 - r_u1) / (v0 - r_v1));
+		u = r_u1 + ((float)v - r_v1) * u_step;
+	}
+
+	edge->u_step = u_step*0x100000;
+	edge->u = u*0x100000 + 0xFFFFF;
+
+// we need to do this to avoid stepping off the edges if a very nearly
+// horizontal edge is less than epsilon above a scan, and numeric error causes
+// it to incorrectly extend to the scan, and the extension of the line goes off
+// the edge of the screen
+// FIXME: is this actually needed?
+	if (edge->u < r_refdef.vrect_x_adj_shift20)
+		edge->u = r_refdef.vrect_x_adj_shift20;
+	if (edge->u > r_refdef.vrectright_adj_shift20)
+		edge->u = r_refdef.vrectright_adj_shift20;
+
+//
+// sort the edge in normally
+//
+	u_check = edge->u;
+	if (edge->surfs[0])
+		u_check++;	// sort trailers after leaders
+
+	if (!newedges[v] || newedges[v]->u >= u_check)
+	{
+		edge->next = newedges[v];
+		newedges[v] = edge;
+	}
+	else
+	{
+		pcheck = newedges[v];
+		while (pcheck->next && pcheck->next->u < u_check)
+			pcheck = pcheck->next;
+		edge->next = pcheck->next;
+		pcheck->next = edge;
+	}
+
+	edge->nextremove = removeedges[v2];
+	removeedges[v2] = edge;
+}
+
+
+/*
+================
+R_ClipEdge
+================
+*/
+static inline void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
+{
+	float		d0, d1, f;
+	mvertex_t	clipvert;
+
+	if (clip)
+	{
+		do
+		{
+			d0 = DotProduct (pv0->position, clip->normal) - clip->dist;
+			d1 = DotProduct (pv1->position, clip->normal) - clip->dist;
+
+			if (d0 >= 0)
+			{
+			// point 0 is unclipped
+				if (d1 >= 0)
+				{
+				// both points are unclipped
+					continue;
+				}
+
+			// only point 1 is clipped
+
+			// we don't cache clipped edges
+				cacheoffset = 0x7FFFFFFF;
+
+				f = d0 / (d0 - d1);
+				clipvert.position[0] = pv0->position[0] +
+						f * (pv1->position[0] - pv0->position[0]);
+				clipvert.position[1] = pv0->position[1] +
+						f * (pv1->position[1] - pv0->position[1]);
+				clipvert.position[2] = pv0->position[2] +
+						f * (pv1->position[2] - pv0->position[2]);
+
+				if (clip->leftedge)
+				{
+					r_leftclipped = true;
+					r_leftexit = clipvert;
+				}
+				else if (clip->rightedge)
+				{
+					r_rightclipped = true;
+					r_rightexit = clipvert;
+				}
+
+				R_ClipEdge (pv0, &clipvert, clip->next);
+				return;
+			}
+			else
+			{
+			// point 0 is clipped
+				if (d1 < 0)
+				{
+				// both points are clipped
+				// we do cache fully clipped edges
+					if (!r_leftclipped)
+						cacheoffset = FULLY_CLIPPED_CACHED |
+								(r_framecount & FRAMECOUNT_MASK);
+					return;
+				}
+
+			// only point 0 is clipped
+				r_lastvertvalid = false;
+
+			// we don't cache partially clipped edges
+				cacheoffset = 0x7FFFFFFF;
+
+				f = d0 / (d0 - d1);
+				clipvert.position[0] = pv0->position[0] +
+						f * (pv1->position[0] - pv0->position[0]);
+				clipvert.position[1] = pv0->position[1] +
+						f * (pv1->position[1] - pv0->position[1]);
+				clipvert.position[2] = pv0->position[2] +
+						f * (pv1->position[2] - pv0->position[2]);
+
+				if (clip->leftedge)
+				{
+					r_leftclipped = true;
+					r_leftenter = clipvert;
+				}
+				else if (clip->rightedge)
+				{
+					r_rightclipped = true;
+					r_rightenter = clipvert;
+				}
+
+				R_ClipEdge (&clipvert, pv1, clip->next);
+				return;
+			}
+		} while ((clip = clip->next) != NULL);
+	}
+
+// add the edge
+	R_EmitEdge (pv0, pv1);
+}
+
+#endif	// !id386
+
+
+/*
+================
+R_EmitCachedEdge
+================
+*/
+void R_EmitCachedEdge (void)
+{
+	edge_t		*pedge_t;
+
+	pedge_t = (edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset);
+
+	if (!pedge_t->surfs[0])
+		pedge_t->surfs[0] = surface_p - surfaces;
+	else
+		pedge_t->surfs[1] = surface_p - surfaces;
+
+	if (pedge_t->nearzi > r_nearzi)	// for mipmap finding
+		r_nearzi = pedge_t->nearzi;
+
+	r_emitted = 1;
+}
+
+
+/*
+================
+R_RenderFace
+================
+*/
+void R_RenderFace (msurface_t *fa, int clipflags)
+{
+	int			i, lindex;
+	unsigned	mask;
+	mplane_t	*pplane;
+	float		distinv;
+	vec3_t		p_normal;
+	medge_t		*pedges, tedge;
+	clipplane_t	*pclip;
+
+// skip out if no more surfs
+	if ((surface_p) >= surf_max)
+	{
+		r_outofsurfaces++;
+		return;
+	}
+
+// ditto if not enough edges left, or switch to auxedges if possible
+	if ((edge_p + fa->numedges + 4) >= edge_max)
+	{
+		r_outofedges += fa->numedges;
+		return;
+	}
+
+	c_faceclip++;
+
+// set up clip planes
+	pclip = NULL;
+
+	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
+	{
+		if (clipflags & mask)
+		{
+			view_clipplanes[i].next = pclip;
+			pclip = &view_clipplanes[i];
+		}
+	}
+
+// push the edges through
+	r_emitted = 0;
+	r_nearzi = 0;
+	r_nearzionly = false;
+	makeleftedge = makerightedge = false;
+	pedges = currententity->model->edges;
+	r_lastvertvalid = false;
+
+	for (i=0 ; i<fa->numedges ; i++)
+	{
+		lindex = currententity->model->surfedges[fa->firstedge + i];
+
+		if (lindex > 0)
+		{
+			r_pedge = &pedges[lindex];
+
+		// if the edge is cached, we can just reuse the edge
+			if (!insubmodel)
+			{
+				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
+				{
+					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
+						r_framecount)
+					{
+						r_lastvertvalid = false;
+						continue;
+					}
+				}
+				else
+				{
+					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
+						 r_pedge->cachededgeoffset) &&
+						(((edge_t *)((unsigned long)r_edges +
+						 r_pedge->cachededgeoffset))->owner == r_pedge))
+					{
+						R_EmitCachedEdge ();
+						r_lastvertvalid = false;
+						continue;
+					}
+				}
+			}
+
+		// assume it's cacheable
+			cacheoffset = (byte *)edge_p - (byte *)r_edges;
+			r_leftclipped = r_rightclipped = false;
+			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]],
+						&r_pcurrentvertbase[r_pedge->v[1]],
+						pclip);
+			r_pedge->cachededgeoffset = cacheoffset;
+
+			if (r_leftclipped)
+				makeleftedge = true;
+			if (r_rightclipped)
+				makerightedge = true;
+			r_lastvertvalid = true;
+		}
+		else
+		{
+			lindex = -lindex;
+			r_pedge = &pedges[lindex];
+		// if the edge is cached, we can just reuse the edge
+			if (!insubmodel)
+			{
+				if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
+				{
+					if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) ==
+						r_framecount)
+					{
+						r_lastvertvalid = false;
+						continue;
+					}
+				}
+				else
+				{
+				// it's cached if the cached edge is valid and is owned
+				// by this medge_t
+					if ((((unsigned long)edge_p - (unsigned long)r_edges) >
+						 r_pedge->cachededgeoffset) &&
+						(((edge_t *)((unsigned long)r_edges +
+						 r_pedge->cachededgeoffset))->owner == r_pedge))
+					{
+						R_EmitCachedEdge ();
+						r_lastvertvalid = false;
+						continue;
+					}
+				}
+			}
+
+		// assume it's cacheable
+			cacheoffset = (byte *)edge_p - (byte *)r_edges;
+			r_leftclipped = r_rightclipped = false;
+			R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]],
+						&r_pcurrentvertbase[r_pedge->v[0]],
+						pclip);
+			r_pedge->cachededgeoffset = cacheoffset;
+
+			if (r_leftclipped)
+				makeleftedge = true;
+			if (r_rightclipped)
+				makerightedge = true;
+			r_lastvertvalid = true;
+		}
+	}
+
+// if there was a clip off the left edge, add that edge too
+// FIXME: faster to do in screen space?
+// FIXME: share clipped edges?
+	if (makeleftedge)
+	{
+		r_pedge = &tedge;
+		r_lastvertvalid = false;
+		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
+	}
+
+// if there was a clip off the right edge, get the right r_nearzi
+	if (makerightedge)
+	{
+		r_pedge = &tedge;
+		r_lastvertvalid = false;
+		r_nearzionly = true;
+		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
+	}
+
+// if no edges made it out, return without posting the surface
+	if (!r_emitted)
+		return;
+
+	r_polycount++;
+
+	surface_p->data = (void *)fa;
+	surface_p->nearzi = r_nearzi;
+	surface_p->flags = fa->flags;
+	surface_p->insubmodel = insubmodel;
+	surface_p->spanstate = 0;
+	surface_p->entity = currententity;
+	surface_p->key = r_currentkey++;
+	surface_p->spans = NULL;
+
+	pplane = fa->plane;
+// FIXME: cache this?
+	TransformVector (pplane->normal, p_normal);
+// FIXME: cache this?
+	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
+
+	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
+	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
+	surface_p->d_ziorigin = p_normal[2] * distinv -
+			xcenter * surface_p->d_zistepu -
+			ycenter * surface_p->d_zistepv;
+
+//JDC	VectorCopy (r_worldmodelorg, surface_p->modelorg);
+	surface_p++;
+}
+
+
+/*
+================
+R_RenderBmodelFace
+================
+*/
+void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf)
+{
+	int			i;
+	unsigned	mask;
+	mplane_t	*pplane;
+	float		distinv;
+	vec3_t		p_normal;
+	medge_t		tedge;
+	clipplane_t	*pclip;
+
+// skip out if no more surfs
+	if (surface_p >= surf_max)
+	{
+		r_outofsurfaces++;
+		return;
+	}
+
+// ditto if not enough edges left, or switch to auxedges if possible
+	if ((edge_p + psurf->numedges + 4) >= edge_max)
+	{
+		r_outofedges += psurf->numedges;
+		return;
+	}
+
+	c_faceclip++;
+
+// this is a dummy to give the caching mechanism someplace to write to
+	r_pedge = &tedge;
+
+// set up clip planes
+	pclip = NULL;
+
+	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
+	{
+		if (r_clipflags & mask)
+		{
+			view_clipplanes[i].next = pclip;
+			pclip = &view_clipplanes[i];
+		}
+	}
+
+// push the edges through
+	r_emitted = 0;
+	r_nearzi = 0;
+	r_nearzionly = false;
+	makeleftedge = makerightedge = false;
+// FIXME: keep clipped bmodel edges in clockwise order so last vertex caching
+// can be used?
+	r_lastvertvalid = false;
+
+	for ( ; pedges ; pedges = pedges->pnext)
+	{
+		r_leftclipped = r_rightclipped = false;
+		R_ClipEdge (pedges->v[0], pedges->v[1], pclip);
+
+		if (r_leftclipped)
+			makeleftedge = true;
+		if (r_rightclipped)
+			makerightedge = true;
+	}
+
+// if there was a clip off the left edge, add that edge too
+// FIXME: faster to do in screen space?
+// FIXME: share clipped edges?
+	if (makeleftedge)
+	{
+		r_pedge = &tedge;
+		R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next);
+	}
+
+// if there was a clip off the right edge, get the right r_nearzi
+	if (makerightedge)
+	{
+		r_pedge = &tedge;
+		r_nearzionly = true;
+		R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next);
+	}
+
+// if no edges made it out, return without posting the surface
+	if (!r_emitted)
+		return;
+
+	r_polycount++;
+
+	surface_p->data = (void *)psurf;
+	surface_p->nearzi = r_nearzi;
+	surface_p->flags = psurf->flags;
+	surface_p->insubmodel = true;
+	surface_p->spanstate = 0;
+	surface_p->entity = currententity;
+	surface_p->key = r_currentbkey;
+	surface_p->spans = NULL;
+
+	pplane = psurf->plane;
+// FIXME: cache this?
+	TransformVector (pplane->normal, p_normal);
+// FIXME: cache this?
+	distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal));
+
+	surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv;
+	surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv;
+	surface_p->d_ziorigin = p_normal[2] * distinv -
+			xcenter * surface_p->d_zistepu -
+			ycenter * surface_p->d_zistepv;
+
+//JDC	VectorCopy (r_worldmodelorg, surface_p->modelorg);
+	surface_p++;
+}
+
+
+/*
+================
+R_RenderPoly
+================
+*/
+void R_RenderPoly (msurface_t *fa, int clipflags)
+{
+	int			i, lindex, lnumverts, s_axis, t_axis;
+	float		dist, lastdist, lzi, scale, u, v, frac;
+	unsigned	mask;
+	vec3_t		local, transformed;
+	clipplane_t	*pclip;
+	medge_t		*pedges;
+	mplane_t	*pplane;
+	mvertex_t	verts[2][100];	//FIXME: do real number
+	polyvert_t	pverts[100];	//FIXME: do real number, safely
+	int			vertpage, newverts, newpage, lastvert;
+	qboolean	visible;
+
+// FIXME: clean this up and make it faster
+// FIXME: guard against running out of vertices
+
+	s_axis = t_axis = 0;	// keep compiler happy
+
+// set up clip planes
+	pclip = NULL;
+
+	for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1)
+	{
+		if (clipflags & mask)
+		{
+			view_clipplanes[i].next = pclip;
+			pclip = &view_clipplanes[i];
+		}
+	}
+
+// reconstruct the polygon
+// FIXME: these should be precalculated and loaded off disk
+	pedges = currententity->model->edges;
+	lnumverts = fa->numedges;
+	vertpage = 0;
+
+	for (i=0 ; i<lnumverts ; i++)
+	{
+		lindex = currententity->model->surfedges[fa->firstedge + i];
+
+		if (lindex > 0)
+		{
+			r_pedge = &pedges[lindex];
+			verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]];
+		}
+		else
+		{
+			r_pedge = &pedges[-lindex];
+			verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]];
+		}
+	}
+
+// clip the polygon, done if not visible
+	while (pclip)
+	{
+		lastvert = lnumverts - 1;
+		lastdist = DotProduct (verts[vertpage][lastvert].position,
+							   pclip->normal) - pclip->dist;
+
+		visible = false;
+		newverts = 0;
+		newpage = vertpage ^ 1;
+
+		for (i=0 ; i<lnumverts ; i++)
+		{
+			dist = DotProduct (verts[vertpage][i].position, pclip->normal) -
+					pclip->dist;
+
+			if ((lastdist > 0) != (dist > 0))
+			{
+				frac = dist / (dist - lastdist);
+				verts[newpage][newverts].position[0] =
+						verts[vertpage][i].position[0] +
+						((verts[vertpage][lastvert].position[0] -
+						  verts[vertpage][i].position[0]) * frac);
+				verts[newpage][newverts].position[1] =
+						verts[vertpage][i].position[1] +
+						((verts[vertpage][lastvert].position[1] -
+						  verts[vertpage][i].position[1]) * frac);
+				verts[newpage][newverts].position[2] =
+						verts[vertpage][i].position[2] +
+						((verts[vertpage][lastvert].position[2] -
+						  verts[vertpage][i].position[2]) * frac);
+				newverts++;
+			}
+
+			if (dist >= 0)
+			{
+				verts[newpage][newverts] = verts[vertpage][i];
+				newverts++;
+				visible = true;
+			}
+
+			lastvert = i;
+			lastdist = dist;
+		}
+
+		if (!visible || (newverts < 3))
+			return;
+
+		lnumverts = newverts;
+		vertpage ^= 1;
+		pclip = pclip->next;
+	}
+
+// transform and project, remembering the z values at the vertices and
+// r_nearzi, and extract the s and t coordinates at the vertices
+	pplane = fa->plane;
+	switch (pplane->type)
+	{
+	case PLANE_X:
+	case PLANE_ANYX:
+		s_axis = 1;
+		t_axis = 2;
+		break;
+	case PLANE_Y:
+	case PLANE_ANYY:
+		s_axis = 0;
+		t_axis = 2;
+		break;
+	case PLANE_Z:
+	case PLANE_ANYZ:
+		s_axis = 0;
+		t_axis = 1;
+		break;
+	}
+
+	r_nearzi = 0;
+
+	for (i=0 ; i<lnumverts ; i++)
+	{
+	// transform and project
+		VectorSubtract (verts[vertpage][i].position, modelorg, local);
+		TransformVector (local, transformed);
+
+		if (transformed[2] < NEAR_CLIP)
+			transformed[2] = NEAR_CLIP;
+
+		lzi = 1.0 / transformed[2];
+
+		if (lzi > r_nearzi)	// for mipmap finding
+			r_nearzi = lzi;
+
+	// FIXME: build x/yscale into transform?
+		scale = xscale * lzi;
+		u = (xcenter + scale*transformed[0]);
+		if (u < r_refdef.fvrectx_adj)
+			u = r_refdef.fvrectx_adj;
+		if (u > r_refdef.fvrectright_adj)
+			u = r_refdef.fvrectright_adj;
+
+		scale = yscale * lzi;
+		v = (ycenter - scale*transformed[1]);
+		if (v < r_refdef.fvrecty_adj)
+			v = r_refdef.fvrecty_adj;
+		if (v > r_refdef.fvrectbottom_adj)
+			v = r_refdef.fvrectbottom_adj;
+
+		pverts[i].u = u;
+		pverts[i].v = v;
+		pverts[i].zi = lzi;
+		pverts[i].s = verts[vertpage][i].position[s_axis];
+		pverts[i].t = verts[vertpage][i].position[t_axis];
+	}
+
+// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z
+// for each vertex
+	r_polydesc.numverts = lnumverts;
+	r_polydesc.nearzi = r_nearzi;
+	r_polydesc.pcurrentface = fa;
+	r_polydesc.pverts = pverts;
+
+// draw the polygon
+	D_DrawPoly ();
+}
+
+
+/*
+================
+R_ZDrawSubmodelPolys
+================
+*/
+void R_ZDrawSubmodelPolys (model_t *pmodel)
+{
+	int			i, numsurfaces;
+	msurface_t	*psurf;
+	float		dot;
+	mplane_t	*pplane;
+
+	psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
+	numsurfaces = pmodel->nummodelsurfaces;
+
+	for (i=0 ; i<numsurfaces ; i++, psurf++)
+	{
+	// find which side of the node we are on
+		pplane = psurf->plane;
+
+		dot = DotProduct (modelorg, pplane->normal) - pplane->dist;
+
+	// draw the polygon
+		if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
+			(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
+		{
+		// FIXME: use bounding-box-based frustum clipping info?
+			R_RenderPoly (psurf, 15);
+		}
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_edge.c b/apps/plugins/sdl/progs/quake/r_edge.c
new file mode 100644
index 0000000..1fff765
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_edge.c
@@ -0,0 +1,775 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_edge.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+#if 0
+// FIXME
+the complex cases add new polys on most lines, so dont optimize for keeping them the same
+have multiple free span lists to try to get better coherence?
+low depth complexity -- 1 to 3 or so
+
+this breaks spans at every edge, even hidden ones (bad)
+
+have a sentinal at both ends?
+#endif
+
+
+edge_t	*auxedges;
+edge_t	*r_edges, *edge_p, *edge_max;
+
+surf_t	*surfaces, *surface_p, *surf_max;
+
+// surfaces are generated in back to front order by the bsp, so if a surf
+// pointer is greater than another one, it should be drawn in front
+// surfaces[1] is the background, and is used as the active surface stack
+
+edge_t	*newedges[MAXHEIGHT];
+edge_t	*removeedges[MAXHEIGHT];
+
+espan_t	*span_p, *max_span_p;
+
+int		r_currentkey;
+
+extern	int	screenwidth;
+
+int	current_iv;
+
+int	edge_head_u_shift20, edge_tail_u_shift20;
+
+static void (*pdrawfunc)(void);
+
+edge_t	edge_head;
+edge_t	edge_tail;
+edge_t	edge_aftertail;
+edge_t	edge_sentinel;
+
+float	fv;
+
+void R_GenerateSpans (void);
+void R_GenerateSpansBackward (void);
+
+// made static
+//void R_LeadingEdge (edge_t *edge);
+//void R_LeadingEdgeBackwards (edge_t *edge);
+//void R_TrailingEdge (surf_t *surf, edge_t *edge);
+
+
+//=============================================================================
+
+
+/*
+==============
+R_DrawCulledPolys
+==============
+*/
+static inline void R_DrawCulledPolys (void)
+{
+	surf_t			*s;
+	msurface_t		*pface;
+
+	currententity = &cl_entities[0];
+
+	if (r_worldpolysbacktofront)
+	{
+		for (s=surface_p-1 ; s>&surfaces[1] ; s--)
+		{
+			if (!s->spans)
+				continue;
+
+			if (!(s->flags & SURF_DRAWBACKGROUND))
+			{
+				pface = (msurface_t *)s->data;
+				R_RenderPoly (pface, 15);
+			}
+		}
+	}
+	else
+	{
+		for (s = &surfaces[1] ; s<surface_p ; s++)
+		{
+			if (!s->spans)
+				continue;
+
+			if (!(s->flags & SURF_DRAWBACKGROUND))
+			{
+				pface = (msurface_t *)s->data;
+				R_RenderPoly (pface, 15);
+			}
+		}
+	}
+}
+
+
+/*
+==============
+R_BeginEdgeFrame
+==============
+*/
+void R_BeginEdgeFrame (void)
+{
+	int		v;
+
+	edge_p = r_edges;
+	edge_max = &r_edges[r_numallocatededges];
+
+	surface_p = &surfaces[2];	// background is surface 1,
+								//  surface 0 is a dummy
+	surfaces[1].spans = NULL;	// no background spans yet
+	surfaces[1].flags = SURF_DRAWBACKGROUND;
+
+// put the background behind everything in the world
+	if (r_draworder.value)
+	{
+		pdrawfunc = R_GenerateSpansBackward;
+		surfaces[1].key = 0;
+		r_currentkey = 1;
+	}
+	else
+	{
+		pdrawfunc = R_GenerateSpans;
+		surfaces[1].key = 0x7FFFFFFF;
+		r_currentkey = 0;
+	}
+
+// FIXME: set with memset
+	for (v=r_refdef.vrect.y ; v<r_refdef.vrectbottom ; v++)
+	{
+		newedges[v] = removeedges[v] = NULL;
+	}
+}
+
+
+#if	!id386
+
+/*
+==============
+R_InsertNewEdges
+
+Adds the edges in the linked list edgestoadd, adding them to the edges in the
+linked list edgelist.  edgestoadd is assumed to be sorted on u, and non-empty (this is actually newedges[v]).  edgelist is assumed to be sorted on u, with a
+sentinel at the end (actually, this is the active edge table starting at
+edge_head.next).
+==============
+*/
+void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist)
+{
+	edge_t	*next_edge;
+
+	do
+	{
+		next_edge = edgestoadd->next;
+edgesearch:
+		if (edgelist->u >= edgestoadd->u)
+			goto addedge;
+		edgelist=edgelist->next;
+		if (edgelist->u >= edgestoadd->u)
+			goto addedge;
+		edgelist=edgelist->next;
+		if (edgelist->u >= edgestoadd->u)
+			goto addedge;
+		edgelist=edgelist->next;
+		if (edgelist->u >= edgestoadd->u)
+			goto addedge;
+		edgelist=edgelist->next;
+		goto edgesearch;
+
+	// insert edgestoadd before edgelist
+addedge:
+		edgestoadd->next = edgelist;
+		edgestoadd->prev = edgelist->prev;
+		edgelist->prev->next = edgestoadd;
+		edgelist->prev = edgestoadd;
+	} while ((edgestoadd = next_edge) != NULL);
+}
+
+#endif	// !id386
+	
+
+#if	!id386
+
+/*
+==============
+R_RemoveEdges
+==============
+*/
+void R_RemoveEdges (edge_t *pedge)
+{
+
+	do
+	{
+		pedge->next->prev = pedge->prev;
+		pedge->prev->next = pedge->next;
+	} while ((pedge = pedge->nextremove) != NULL);
+}
+
+#endif	// !id386
+
+
+#if	!id386
+
+/*
+==============
+R_StepActiveU
+==============
+*/
+void R_StepActiveU (edge_t *pedge)
+{
+	edge_t		*pnext_edge, *pwedge;
+
+	while (1)
+	{
+nextedge:
+		pedge->u += pedge->u_step;
+		if (pedge->u < pedge->prev->u)
+			goto pushback;
+		pedge = pedge->next;
+			
+		pedge->u += pedge->u_step;
+		if (pedge->u < pedge->prev->u)
+			goto pushback;
+		pedge = pedge->next;
+			
+		pedge->u += pedge->u_step;
+		if (pedge->u < pedge->prev->u)
+			goto pushback;
+		pedge = pedge->next;
+			
+		pedge->u += pedge->u_step;
+		if (pedge->u < pedge->prev->u)
+			goto pushback;
+		pedge = pedge->next;
+			
+		goto nextedge;		
+		
+pushback:
+		if (pedge == &edge_aftertail)
+			return;
+			
+	// push it back to keep it sorted		
+		pnext_edge = pedge->next;
+
+	// pull the edge out of the edge list
+		pedge->next->prev = pedge->prev;
+		pedge->prev->next = pedge->next;
+
+	// find out where the edge goes in the edge list
+		pwedge = pedge->prev->prev;
+
+		while (pwedge->u > pedge->u)
+		{
+			pwedge = pwedge->prev;
+		}
+
+	// put the edge back into the edge list
+		pedge->next = pwedge->next;
+		pedge->prev = pwedge;
+		pedge->next->prev = pedge;
+		pwedge->next = pedge;
+
+		pedge = pnext_edge;
+		if (pedge == &edge_tail)
+			return;
+	}
+}
+
+#endif	// !id386
+
+
+/*
+==============
+R_CleanupSpan
+==============
+*/
+void R_CleanupSpan ()
+{
+	surf_t	*surf;
+	int		iu;
+	espan_t	*span;
+
+// now that we've reached the right edge of the screen, we're done with any
+// unfinished surfaces, so emit a span for whatever's on top
+	surf = surfaces[1].next;
+	iu = edge_tail_u_shift20;
+	if (iu > surf->last_u)
+	{
+		span = span_p++;
+		span->u = surf->last_u;
+		span->count = iu - span->u;
+		span->v = current_iv;
+		span->pnext = surf->spans;
+		surf->spans = span;
+	}
+
+// reset spanstate for all surfaces in the surface stack
+	do
+	{
+		surf->spanstate = 0;
+		surf = surf->next;
+	} while (surf != &surfaces[1]);
+}
+
+
+/*
+==============
+R_LeadingEdgeBackwards
+==============
+*/
+static inline void R_LeadingEdgeBackwards (edge_t *edge)
+{
+	espan_t			*span;
+	surf_t			*surf, *surf2;
+	int				iu;
+
+// it's adding a new surface in, so find the correct place
+	surf = &surfaces[edge->surfs[1]];
+
+// don't start a span if this is an inverted span, with the end
+// edge preceding the start edge (that is, we've already seen the
+// end edge)
+	if (++surf->spanstate == 1)
+	{
+		surf2 = surfaces[1].next;
+
+		if (surf->key > surf2->key)
+			goto newtop;
+
+	// if it's two surfaces on the same plane, the one that's already
+	// active is in front, so keep going unless it's a bmodel
+		if (surf->insubmodel && (surf->key == surf2->key))
+		{
+		// must be two bmodels in the same leaf; don't care, because they'll
+		// never be farthest anyway
+			goto newtop;
+		}
+
+continue_search:
+
+		do
+		{
+			surf2 = surf2->next;
+		} while (surf->key < surf2->key);
+
+		if (surf->key == surf2->key)
+		{
+		// if it's two surfaces on the same plane, the one that's already
+		// active is in front, so keep going unless it's a bmodel
+			if (!surf->insubmodel)
+				goto continue_search;
+
+		// must be two bmodels in the same leaf; don't care which is really
+		// in front, because they'll never be farthest anyway
+		}
+
+		goto gotposition;
+
+newtop:
+	// emit a span (obscures current top)
+		iu = edge->u >> 20;
+
+		if (iu > surf2->last_u)
+		{
+			span = span_p++;
+			span->u = surf2->last_u;
+			span->count = iu - span->u;
+			span->v = current_iv;
+			span->pnext = surf2->spans;
+			surf2->spans = span;
+		}
+
+		// set last_u on the new span
+		surf->last_u = iu;
+				
+gotposition:
+	// insert before surf2
+		surf->next = surf2;
+		surf->prev = surf2->prev;
+		surf2->prev->next = surf;
+		surf2->prev = surf;
+	}
+}
+
+
+/*
+==============
+R_TrailingEdge
+==============
+*/
+static inline void R_TrailingEdge (surf_t *surf, edge_t *edge)
+{
+	espan_t			*span;
+	int				iu;
+
+// don't generate a span if this is an inverted span, with the end
+// edge preceding the start edge (that is, we haven't seen the
+// start edge yet)
+	if (--surf->spanstate == 0)
+	{
+		if (surf->insubmodel)
+			r_bmodelactive--;
+
+		if (surf == surfaces[1].next)
+		{
+		// emit a span (current top going away)
+			iu = edge->u >> 20;
+			if (iu > surf->last_u)
+			{
+				span = span_p++;
+				span->u = surf->last_u;
+				span->count = iu - span->u;
+				span->v = current_iv;
+				span->pnext = surf->spans;
+				surf->spans = span;
+			}
+
+		// set last_u on the surface below
+			surf->next->last_u = iu;
+		}
+
+		surf->prev->next = surf->next;
+		surf->next->prev = surf->prev;
+	}
+}
+
+
+#if	!id386
+
+/*
+==============
+R_LeadingEdge
+==============
+*/
+static inline void R_LeadingEdge (edge_t *edge)
+{
+	espan_t			*span;
+	surf_t			*surf, *surf2;
+	int				iu;
+	double			fu, newzi, testzi, newzitop, newzibottom;
+
+	if (edge->surfs[1])
+	{
+	// it's adding a new surface in, so find the correct place
+		surf = &surfaces[edge->surfs[1]];
+
+	// don't start a span if this is an inverted span, with the end
+	// edge preceding the start edge (that is, we've already seen the
+	// end edge)
+		if (++surf->spanstate == 1)
+		{
+			if (surf->insubmodel)
+				r_bmodelactive++;
+
+			surf2 = surfaces[1].next;
+
+			if (surf->key < surf2->key)
+				goto newtop;
+
+		// if it's two surfaces on the same plane, the one that's already
+		// active is in front, so keep going unless it's a bmodel
+			if (surf->insubmodel && (surf->key == surf2->key))
+			{
+			// must be two bmodels in the same leaf; sort on 1/z
+				fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000);
+				newzi = surf->d_ziorigin + fv*surf->d_zistepv +
+						fu*surf->d_zistepu;
+				newzibottom = newzi * 0.99;
+
+				testzi = surf2->d_ziorigin + fv*surf2->d_zistepv +
+						fu*surf2->d_zistepu;
+
+				if (newzibottom >= testzi)
+				{
+					goto newtop;
+				}
+
+				newzitop = newzi * 1.01;
+				if (newzitop >= testzi)
+				{
+					if (surf->d_zistepu >= surf2->d_zistepu)
+					{
+						goto newtop;
+					}
+				}
+			}
+
+continue_search:
+
+			do
+			{
+				surf2 = surf2->next;
+			} while (surf->key > surf2->key);
+
+			if (surf->key == surf2->key)
+			{
+			// if it's two surfaces on the same plane, the one that's already
+			// active is in front, so keep going unless it's a bmodel
+				if (!surf->insubmodel)
+					goto continue_search;
+
+			// must be two bmodels in the same leaf; sort on 1/z
+				fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000);
+				newzi = surf->d_ziorigin + fv*surf->d_zistepv +
+						fu*surf->d_zistepu;
+				newzibottom = newzi * 0.99;
+
+				testzi = surf2->d_ziorigin + fv*surf2->d_zistepv +
+						fu*surf2->d_zistepu;
+
+				if (newzibottom >= testzi)
+				{
+					goto gotposition;
+				}
+
+				newzitop = newzi * 1.01;
+				if (newzitop >= testzi)
+				{
+					if (surf->d_zistepu >= surf2->d_zistepu)
+					{
+						goto gotposition;
+					}
+				}
+
+				goto continue_search;
+			}
+
+			goto gotposition;
+
+newtop:
+		// emit a span (obscures current top)
+			iu = edge->u >> 20;
+
+			if (iu > surf2->last_u)
+			{
+				span = span_p++;
+				span->u = surf2->last_u;
+				span->count = iu - span->u;
+				span->v = current_iv;
+				span->pnext = surf2->spans;
+				surf2->spans = span;
+			}
+
+			// set last_u on the new span
+			surf->last_u = iu;
+				
+gotposition:
+		// insert before surf2
+			surf->next = surf2;
+			surf->prev = surf2->prev;
+			surf2->prev->next = surf;
+			surf2->prev = surf;
+		}
+	}
+}
+
+
+/*
+==============
+R_GenerateSpans
+==============
+*/
+void R_GenerateSpans (void)
+{
+	edge_t			*edge;
+	surf_t			*surf;
+
+	r_bmodelactive = 0;
+
+// clear active surfaces to just the background surface
+	surfaces[1].next = surfaces[1].prev = &surfaces[1];
+	surfaces[1].last_u = edge_head_u_shift20;
+
+// generate spans
+	for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next)
+	{			
+		if (edge->surfs[0])
+		{
+		// it has a left surface, so a surface is going away for this span
+			surf = &surfaces[edge->surfs[0]];
+
+			R_TrailingEdge (surf, edge);
+
+			if (!edge->surfs[1])
+				continue;
+		}
+
+		R_LeadingEdge (edge);
+	}
+
+	R_CleanupSpan ();
+}
+
+#endif	// !id386
+
+
+/*
+==============
+R_GenerateSpansBackward
+==============
+*/
+void R_GenerateSpansBackward (void)
+{
+	edge_t			*edge;
+
+	r_bmodelactive = 0;
+
+// clear active surfaces to just the background surface
+	surfaces[1].next = surfaces[1].prev = &surfaces[1];
+	surfaces[1].last_u = edge_head_u_shift20;
+
+// generate spans
+	for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next)
+	{			
+		if (edge->surfs[0])
+			R_TrailingEdge (&surfaces[edge->surfs[0]], edge);
+
+		if (edge->surfs[1])
+			R_LeadingEdgeBackwards (edge);
+	}
+
+	R_CleanupSpan ();
+}
+
+
+/*
+==============
+R_ScanEdges
+
+Input: 
+newedges[] array
+	this has links to edges, which have links to surfaces
+
+Output:
+Each surface has a linked list of its visible spans
+==============
+*/
+void R_ScanEdges (void)
+{
+	int		iv, bottom;
+	byte	basespans[MAXSPANS*sizeof(espan_t)+CACHE_SIZE];
+	espan_t	*basespan_p;
+	surf_t	*s;
+
+	basespan_p = (espan_t *)
+			((long)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
+	max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width];
+
+	span_p = basespan_p;
+
+// clear active edges to just the background edges around the whole screen
+// FIXME: most of this only needs to be set up once
+	edge_head.u = r_refdef.vrect.x << 20;
+	edge_head_u_shift20 = edge_head.u >> 20;
+	edge_head.u_step = 0;
+	edge_head.prev = NULL;
+	edge_head.next = &edge_tail;
+	edge_head.surfs[0] = 0;
+	edge_head.surfs[1] = 1;
+	
+	edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF;
+	edge_tail_u_shift20 = edge_tail.u >> 20;
+	edge_tail.u_step = 0;
+	edge_tail.prev = &edge_head;
+	edge_tail.next = &edge_aftertail;
+	edge_tail.surfs[0] = 1;
+	edge_tail.surfs[1] = 0;
+	
+	edge_aftertail.u = -1;		// force a move
+	edge_aftertail.u_step = 0;
+	edge_aftertail.next = &edge_sentinel;
+	edge_aftertail.prev = &edge_tail;
+
+// FIXME: do we need this now that we clamp x in r_draw.c?
+	edge_sentinel.u = 2000 << 24;		// make sure nothing sorts past this
+	edge_sentinel.prev = &edge_aftertail;
+
+//	
+// process all scan lines
+//
+	bottom = r_refdef.vrectbottom - 1;
+
+	for (iv=r_refdef.vrect.y ; iv<bottom ; iv++)
+	{
+		current_iv = iv;
+		fv = (float)iv;
+
+	// mark that the head (background start) span is pre-included
+		surfaces[1].spanstate = 1;
+
+		if (newedges[iv])
+		{
+			R_InsertNewEdges (newedges[iv], edge_head.next);
+		}
+
+		(*pdrawfunc) ();
+
+	// flush the span list if we can't be sure we have enough spans left for
+	// the next scan
+		if (span_p >= max_span_p)
+		{
+			VID_UnlockBuffer ();
+			S_ExtraUpdate ();	// don't let sound get messed up if going slow
+			VID_LockBuffer ();
+		
+			if (r_drawculledpolys)
+			{
+				R_DrawCulledPolys ();
+			}
+			else
+			{
+				D_DrawSurfaces ();
+			}
+
+		// clear the surface span pointers
+			for (s = &surfaces[1] ; s<surface_p ; s++)
+				s->spans = NULL;
+
+			span_p = basespan_p;
+		}
+
+		if (removeedges[iv])
+			R_RemoveEdges (removeedges[iv]);
+
+		if (edge_head.next != &edge_tail)
+			R_StepActiveU (edge_head.next);
+	}
+
+// do the last scan (no need to step or sort or remove on the last scan)
+
+	current_iv = iv;
+	fv = (float)iv;
+
+// mark that the head (background start) span is pre-included
+	surfaces[1].spanstate = 1;
+
+	if (newedges[iv])
+		R_InsertNewEdges (newedges[iv], edge_head.next);
+
+	(*pdrawfunc) ();
+
+// draw whatever's left in the span list
+	if (r_drawculledpolys)
+		R_DrawCulledPolys ();
+	else
+		D_DrawSurfaces ();
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/r_efrag.c b/apps/plugins/sdl/progs/quake/r_efrag.c
new file mode 100644
index 0000000..5e4d3f9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_efrag.c
@@ -0,0 +1,276 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_efrag.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+mnode_t	*r_pefragtopnode;
+
+
+//===========================================================================
+
+/*
+===============================================================================
+
+					ENTITY FRAGMENT FUNCTIONS
+
+===============================================================================
+*/
+
+efrag_t		**lastlink;
+
+vec3_t		r_emins, r_emaxs;
+
+entity_t	*r_addent;
+
+
+/*
+================
+R_RemoveEfrags
+
+Call when removing an object from the world or moving it to another position
+================
+*/
+void R_RemoveEfrags (entity_t *ent)
+{
+	efrag_t		*ef, *old, *walk, **prev;
+	
+	ef = ent->efrag;
+	
+	while (ef)
+	{
+		prev = &ef->leaf->efrags;
+		while (1)
+		{
+			walk = *prev;
+			if (!walk)
+				break;
+			if (walk == ef)
+			{	// remove this fragment
+				*prev = ef->leafnext;
+				break;
+			}
+			else
+				prev = &walk->leafnext;
+		}
+				
+		old = ef;
+		ef = ef->entnext;
+		
+	// put it on the free list
+		old->entnext = cl.free_efrags;
+		cl.free_efrags = old;
+	}
+	
+	ent->efrag = NULL; 
+}
+
+/*
+===================
+R_SplitEntityOnNode
+===================
+*/
+void R_SplitEntityOnNode (mnode_t *node)
+{
+	efrag_t		*ef;
+	mplane_t	*splitplane;
+	mleaf_t		*leaf;
+	int			sides;
+	
+	if (node->contents == CONTENTS_SOLID)
+	{
+		return;
+	}
+	
+// add an efrag if the node is a leaf
+
+	if ( node->contents < 0)
+	{
+		if (!r_pefragtopnode)
+			r_pefragtopnode = node;
+
+		leaf = (mleaf_t *)node;
+
+// grab an efrag off the free list
+		ef = cl.free_efrags;
+		if (!ef)
+		{
+			Con_Printf ("Too many efrags!\n");
+			return;		// no free fragments...
+		}
+		cl.free_efrags = cl.free_efrags->entnext;
+
+		ef->entity = r_addent;
+		
+// add the entity link	
+		*lastlink = ef;
+		lastlink = &ef->entnext;
+		ef->entnext = NULL;
+		
+// set the leaf links
+		ef->leaf = leaf;
+		ef->leafnext = leaf->efrags;
+		leaf->efrags = ef;
+			
+		return;
+	}
+	
+// NODE_MIXED
+
+	splitplane = node->plane;
+	sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
+	
+	if (sides == 3)
+	{
+	// split on this plane
+	// if this is the first splitter of this bmodel, remember it
+		if (!r_pefragtopnode)
+			r_pefragtopnode = node;
+	}
+	
+// recurse down the contacted sides
+	if (sides & 1)
+		R_SplitEntityOnNode (node->children[0]);
+		
+	if (sides & 2)
+		R_SplitEntityOnNode (node->children[1]);
+}
+
+
+/*
+===================
+R_SplitEntityOnNode2
+===================
+*/
+void R_SplitEntityOnNode2 (mnode_t *node)
+{
+	mplane_t	*splitplane;
+	int			sides;
+
+	if (node->visframe != r_visframecount)
+		return;
+	
+	if (node->contents < 0)
+	{
+		if (node->contents != CONTENTS_SOLID)
+			r_pefragtopnode = node; // we've reached a non-solid leaf, so it's
+									//  visible and not BSP clipped
+		return;
+	}
+	
+	splitplane = node->plane;
+	sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
+	
+	if (sides == 3)
+	{
+	// remember first splitter
+		r_pefragtopnode = node;
+		return;
+	}
+	
+// not split yet; recurse down the contacted side
+	if (sides & 1)
+		R_SplitEntityOnNode2 (node->children[0]);
+	else
+		R_SplitEntityOnNode2 (node->children[1]);
+}
+
+
+/*
+===========
+R_AddEfrags
+===========
+*/
+void R_AddEfrags (entity_t *ent)
+{
+	model_t		*entmodel;
+	int			i;
+		
+	if (!ent->model)
+		return;
+
+	if (ent == cl_entities)
+		return;		// never add the world
+
+	r_addent = ent;
+			
+	lastlink = &ent->efrag;
+	r_pefragtopnode = NULL;
+	
+	entmodel = ent->model;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		r_emins[i] = ent->origin[i] + entmodel->mins[i];
+		r_emaxs[i] = ent->origin[i] + entmodel->maxs[i];
+	}
+
+	R_SplitEntityOnNode (cl.worldmodel->nodes);
+
+	ent->topnode = r_pefragtopnode;
+}
+
+
+/*
+================
+R_StoreEfrags
+
+// FIXME: a lot of this goes away with edge-based
+================
+*/
+void R_StoreEfrags (efrag_t **ppefrag)
+{
+	entity_t	*pent;
+	model_t		*clmodel;
+	efrag_t		*pefrag;
+
+
+	while ((pefrag = *ppefrag) != NULL)
+	{
+		pent = pefrag->entity;
+		clmodel = pent->model;
+
+		switch (clmodel->type)
+		{
+		case mod_alias:
+		case mod_brush:
+		case mod_sprite:
+			pent = pefrag->entity;
+
+			if ((pent->visframe != r_framecount) &&
+				(cl_numvisedicts < MAX_VISEDICTS))
+			{
+				cl_visedicts[cl_numvisedicts++] = pent;
+
+			// mark that we've recorded this entity for this frame
+				pent->visframe = r_framecount;
+			}
+
+			ppefrag = &pefrag->leafnext;
+			break;
+
+		default:	
+			Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type);
+		}
+	}
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/r_light.c b/apps/plugins/sdl/progs/quake/r_light.c
new file mode 100644
index 0000000..f735d50
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_light.c
@@ -0,0 +1,260 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_light.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+int	r_dlightframecount;
+
+
+/*
+==================
+R_AnimateLight
+==================
+*/
+void R_AnimateLight (void)
+{
+	int			i,j,k;
+	
+//
+// light animations
+// 'm' is normal light, 'a' is no light, 'z' is double bright
+	i = (int)(cl.time*10);
+	for (j=0 ; j<MAX_LIGHTSTYLES ; j++)
+	{
+		if (!cl_lightstyle[j].length)
+		{
+			d_lightstylevalue[j] = 256;
+			continue;
+		}
+		k = i % cl_lightstyle[j].length;
+		k = cl_lightstyle[j].map[k] - 'a';
+		k = k*22;
+		d_lightstylevalue[j] = k;
+	}	
+}
+
+
+/*
+=============================================================================
+
+DYNAMIC LIGHTS
+
+=============================================================================
+*/
+
+/*
+=============
+R_MarkLights
+=============
+*/
+void R_MarkLights (dlight_t *light, int bit, mnode_t *node)
+{
+	mplane_t	*splitplane;
+	float		dist;
+	msurface_t	*surf;
+	int			i;
+	
+	if (node->contents < 0)
+		return;
+
+	splitplane = node->plane;
+	dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
+	
+	if (dist > light->radius)
+	{
+		R_MarkLights (light, bit, node->children[0]);
+		return;
+	}
+	if (dist < -light->radius)
+	{
+		R_MarkLights (light, bit, node->children[1]);
+		return;
+	}
+		
+// mark the polygons
+	surf = cl.worldmodel->surfaces + node->firstsurface;
+	for (i=0 ; i<node->numsurfaces ; i++, surf++)
+	{
+		if (surf->dlightframe != r_dlightframecount)
+		{
+			surf->dlightbits = 0;
+			surf->dlightframe = r_dlightframecount;
+		}
+		surf->dlightbits |= bit;
+	}
+
+	R_MarkLights (light, bit, node->children[0]);
+	R_MarkLights (light, bit, node->children[1]);
+}
+
+
+/*
+=============
+R_PushDlights
+=============
+*/
+void R_PushDlights (void)
+{
+	int		i;
+	dlight_t	*l;
+
+	r_dlightframecount = r_framecount + 1;	// because the count hasn't
+											//  advanced yet for this frame
+	l = cl_dlights;
+
+	for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
+	{
+		if (l->die < cl.time || !l->radius)
+			continue;
+		R_MarkLights ( l, 1<<i, cl.worldmodel->nodes );
+	}
+}
+
+
+/*
+=============================================================================
+
+LIGHT SAMPLING
+
+=============================================================================
+*/
+
+int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end)
+{
+	int			r;
+	float		front, back, frac;
+	int			side;
+	mplane_t	*plane;
+	vec3_t		mid;
+	msurface_t	*surf;
+	int			s, t, ds, dt;
+	int			i;
+	mtexinfo_t	*tex;
+	byte		*lightmap;
+	unsigned	scale;
+	int			maps;
+
+	if (node->contents < 0)
+		return -1;		// didn't hit anything
+	
+// calculate mid point
+
+// FIXME: optimize for axial
+	plane = node->plane;
+	front = DotProduct (start, plane->normal) - plane->dist;
+	back = DotProduct (end, plane->normal) - plane->dist;
+	side = front < 0;
+	
+	if ( (back < 0) == side)
+		return RecursiveLightPoint (node->children[side], start, end);
+	
+	frac = front / (front-back);
+	mid[0] = start[0] + (end[0] - start[0])*frac;
+	mid[1] = start[1] + (end[1] - start[1])*frac;
+	mid[2] = start[2] + (end[2] - start[2])*frac;
+	
+// go down front side	
+	r = RecursiveLightPoint (node->children[side], start, mid);
+	if (r >= 0)
+		return r;		// hit something
+		
+	if ( (back < 0) == side )
+		return -1;		// didn't hit anuthing
+		
+// check for impact on this node
+
+	surf = cl.worldmodel->surfaces + node->firstsurface;
+	for (i=0 ; i<node->numsurfaces ; i++, surf++)
+	{
+		if (surf->flags & SURF_DRAWTILED)
+			continue;	// no lightmaps
+
+		tex = surf->texinfo;
+		
+		s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3];
+		t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];;
+
+		if (s < surf->texturemins[0] ||
+		t < surf->texturemins[1])
+			continue;
+		
+		ds = s - surf->texturemins[0];
+		dt = t - surf->texturemins[1];
+		
+		if ( ds > surf->extents[0] || dt > surf->extents[1] )
+			continue;
+
+		if (!surf->samples)
+			return 0;
+
+		ds >>= 4;
+		dt >>= 4;
+
+		lightmap = surf->samples;
+		r = 0;
+		if (lightmap)
+		{
+
+			lightmap += dt * ((surf->extents[0]>>4)+1) + ds;
+
+			for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
+					maps++)
+			{
+				scale = d_lightstylevalue[surf->styles[maps]];
+				r += *lightmap * scale;
+				lightmap += ((surf->extents[0]>>4)+1) *
+						((surf->extents[1]>>4)+1);
+			}
+			
+			r >>= 8;
+		}
+		
+		return r;
+	}
+
+// go down back side
+	return RecursiveLightPoint (node->children[!side], mid, end);
+}
+
+int R_LightPoint (vec3_t p)
+{
+	vec3_t		end;
+	int			r;
+	
+	if (!cl.worldmodel->lightdata)
+		return 255;
+	
+	end[0] = p[0];
+	end[1] = p[1];
+	end[2] = p[2] - 2048;
+	
+	r = RecursiveLightPoint (cl.worldmodel->nodes, p, end);
+	
+	if (r == -1)
+		r = 0;
+
+	if (r < r_refdef.ambientlight)
+		r = r_refdef.ambientlight;
+
+	return r;
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_local.h b/apps/plugins/sdl/progs/quake/r_local.h
new file mode 100644
index 0000000..910bdbf
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_local.h
@@ -0,0 +1,316 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_local.h -- private refresh defs
+
+#ifndef GLQUAKE
+#include "r_shared.h"
+
+#define ALIAS_BASE_SIZE_RATIO		(1.0 / 11.0)
+					// normalizing factor so player model works out to about
+					//  1 pixel per triangle
+
+#define BMODEL_FULLY_CLIPPED	0x10 // value returned by R_BmodelCheckBBox ()
+									 //  if bbox is trivially rejected
+
+//===========================================================================
+// viewmodel lighting
+
+typedef struct {
+	int			ambientlight;
+	int			shadelight;
+	float		*plightvec;
+} alight_t;
+
+//===========================================================================
+// clipped bmodel edges
+
+typedef struct bedge_s
+{
+	mvertex_t		*v[2];
+	struct bedge_s	*pnext;
+} bedge_t;
+
+typedef struct {
+	float	fv[3];		// viewspace x, y
+} auxvert_t;
+
+//===========================================================================
+
+extern cvar_t	r_draworder;
+extern cvar_t	r_speeds;
+extern cvar_t	r_timegraph;
+extern cvar_t	r_graphheight;
+extern cvar_t	r_clearcolor;
+extern cvar_t	r_waterwarp;
+extern cvar_t	r_fullbright;
+extern cvar_t	r_drawentities;
+extern cvar_t	r_aliasstats;
+extern cvar_t	r_dspeeds;
+extern cvar_t	r_drawflat;
+extern cvar_t	r_ambient;
+extern cvar_t	r_reportsurfout;
+extern cvar_t	r_maxsurfs;
+extern cvar_t	r_numsurfs;
+extern cvar_t	r_reportedgeout;
+extern cvar_t	r_maxedges;
+extern cvar_t	r_numedges;
+
+#define XCENTERING	(1.0 / 2.0)
+#define YCENTERING	(1.0 / 2.0)
+
+#define CLIP_EPSILON		0.001
+
+#define BACKFACE_EPSILON	0.01
+
+//===========================================================================
+
+#define	DIST_NOT_SET	98765
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct clipplane_s
+{
+	vec3_t		normal;
+	float		dist;
+	struct		clipplane_s	*next;
+	byte		leftedge;
+	byte		rightedge;
+	byte		reserved[2];
+} clipplane_t;
+
+extern	clipplane_t	view_clipplanes[4];
+
+//=============================================================================
+
+void R_RenderWorld (void);
+
+//=============================================================================
+
+extern	mplane_t	screenedge[4];
+
+extern	vec3_t	r_origin;
+
+extern	vec3_t	r_entorigin;
+
+extern	float	screenAspect;
+extern	float	verticalFieldOfView;
+extern	float	xOrigin, yOrigin;
+
+extern	int		r_visframecount;
+
+//=============================================================================
+
+extern int	vstartscan;
+
+
+void R_ClearPolyList (void);
+void R_DrawPolyList (void);
+
+//
+// current entity info
+//
+extern	qboolean		insubmodel;
+extern	vec3_t			r_worldmodelorg;
+
+
+void R_DrawSprite (void);
+void R_RenderFace (msurface_t *fa, int clipflags);
+void R_RenderPoly (msurface_t *fa, int clipflags);
+void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf);
+void R_TransformPlane (mplane_t *p, float *normal, float *dist);
+void R_TransformFrustum (void);
+void R_SetSkyFrame (void);
+void R_DrawSurfaceBlock16 (void);
+void R_DrawSurfaceBlock8 (void);
+texture_t *R_TextureAnimation (texture_t *base);
+
+#if	id386
+
+void R_DrawSurfaceBlock8_mip0 (void);
+void R_DrawSurfaceBlock8_mip1 (void);
+void R_DrawSurfaceBlock8_mip2 (void);
+void R_DrawSurfaceBlock8_mip3 (void);
+
+#endif
+
+void R_GenSkyTile (void *pdest);
+void R_GenSkyTile16 (void *pdest);
+void R_Surf8Patch (void);
+void R_Surf16Patch (void);
+void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags);
+void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel);
+
+void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel);
+surf_t *R_GetSurf (void);
+void R_AliasDrawModel (alight_t *plighting);
+void R_BeginEdgeFrame (void);
+void R_ScanEdges (void);
+void D_DrawSurfaces (void);
+void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist);
+void R_StepActiveU (edge_t *pedge);
+void R_RemoveEdges (edge_t *pedge);
+
+extern void R_Surf8Start (void);
+extern void R_Surf8End (void);
+extern void R_Surf16Start (void);
+extern void R_Surf16End (void);
+extern void R_EdgeCodeStart (void);
+extern void R_EdgeCodeEnd (void);
+
+extern void R_RotateBmodel (void);
+
+extern int	c_faceclip;
+extern int	r_polycount;
+extern int	r_wholepolycount;
+
+extern	model_t		*cl_worldmodel;
+
+extern int		*pfrustum_indexes[4];
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+#define	NEAR_CLIP	0.01
+
+extern int			ubasestep, errorterm, erroradjustup, erroradjustdown;
+extern int			vstartscan;
+
+extern fixed16_t	sadjust, tadjust;
+extern fixed16_t	bbextents, bbextentt;
+
+#define MAXBVERTINDEXES	1000	// new clipped vertices when clipping bmodels
+								//  to the world BSP
+extern mvertex_t	*r_ptverts, *r_ptvertsmax;
+
+extern vec3_t			sbaseaxis[3], tbaseaxis[3];
+extern float			entity_rotation[3][3];
+
+extern int		reinit_surfcache;
+
+extern int		r_currentkey;
+extern int		r_currentbkey;
+
+typedef struct btofpoly_s {
+	int			clipflags;
+	msurface_t	*psurf;
+} btofpoly_t;
+
+#define MAX_BTOFPOLYS	5000	// FIXME: tune this
+
+extern int			numbtofpolys;
+extern btofpoly_t	*pbtofpolys;
+
+void	R_InitTurb (void);
+void	R_ZDrawSubmodelPolys (model_t *clmodel);
+
+//=========================================================
+// Alias models
+//=========================================================
+
+#define MAXALIASVERTS		2000	// TODO: tune this
+#define ALIAS_Z_CLIP_PLANE	5
+
+extern int				numverts;
+extern int				a_skinwidth;
+extern mtriangle_t		*ptriangles;
+extern int				numtriangles;
+extern aliashdr_t		*paliashdr;
+extern mdl_t			*pmdl;
+extern float			leftclip, topclip, rightclip, bottomclip;
+extern int				r_acliptype;
+extern finalvert_t		*pfinalverts;
+extern auxvert_t		*pauxverts;
+
+qboolean R_AliasCheckBBox (void);
+
+//=========================================================
+// turbulence stuff
+
+#define	AMP		8*0x10000
+#define	AMP2	3
+#define	SPEED	20
+
+//=========================================================
+// particle stuff
+
+void R_DrawParticles (void);
+void R_InitParticles (void);
+void R_ClearParticles (void);
+void R_ReadPointFile_f (void);
+void R_SurfacePatch (void);
+
+extern int		r_amodels_drawn;
+extern edge_t	*auxedges;
+extern int		r_numallocatededges;
+extern edge_t	*r_edges, *edge_p, *edge_max;
+
+extern	edge_t	*newedges[MAXHEIGHT];
+extern	edge_t	*removeedges[MAXHEIGHT];
+
+extern	int	screenwidth;
+
+// FIXME: make stack vars when debugging done
+extern	edge_t	edge_head;
+extern	edge_t	edge_tail;
+extern	edge_t	edge_aftertail;
+extern int		r_bmodelactive;
+extern vrect_t	*pconupdate;
+
+extern float		aliasxscale, aliasyscale, aliasxcenter, aliasycenter;
+extern float		r_aliastransition, r_resfudge;
+
+extern int		r_outofsurfaces;
+extern int		r_outofedges;
+
+extern mvertex_t	*r_pcurrentvertbase;
+extern int			r_maxvalidedgeoffset;
+
+void R_AliasClipTriangle (mtriangle_t *ptri);
+
+extern float	r_time1;
+extern float	dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2;
+extern float	se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2;
+extern int		r_frustum_indexes[4*6];
+extern int		r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs;
+extern qboolean	r_surfsonstack;
+extern cshift_t	cshift_water;
+extern qboolean	r_dowarpold, r_viewchanged;
+
+extern mleaf_t	*r_viewleaf, *r_oldviewleaf;
+
+extern vec3_t	r_emins, r_emaxs;
+extern mnode_t	*r_pefragtopnode;
+extern int		r_clipflags;
+extern int		r_dlightframecount;
+extern qboolean	r_fov_greater_than_90;
+
+void R_StoreEfrags (efrag_t **ppefrag);
+void R_TimeRefresh_f (void);
+void R_TimeGraph (void);
+void R_PrintAliasStats (void);
+void R_PrintTimes (void);
+void R_PrintDSpeeds (void);
+void R_AnimateLight (void);
+int R_LightPoint (vec3_t p);
+void R_SetupFrame (void);
+void R_cshift_f (void);
+void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1);
+//void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip);
+void R_SplitEntityOnNode2 (mnode_t *node);
+void R_MarkLights (dlight_t *light, int bit, mnode_t *node);
+
+#endif
\ No newline at end of file
diff --git a/apps/plugins/sdl/progs/quake/r_main.c b/apps/plugins/sdl/progs/quake/r_main.c
new file mode 100644
index 0000000..dc785a8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_main.c
@@ -0,0 +1,1089 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_main.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+//define	PASSAGES
+
+void		*colormap;
+vec3_t		viewlightvec;
+alight_t	r_viewlighting = {128, 192, viewlightvec};
+float		r_time1;
+int			r_numallocatededges;
+qboolean	r_drawpolys;
+qboolean	r_drawculledpolys;
+qboolean	r_worldpolysbacktofront;
+qboolean	r_recursiveaffinetriangles = true;
+int			r_pixbytes = 1;
+float		r_aliasuvscale = 1.0;
+int			r_outofsurfaces;
+int			r_outofedges;
+
+qboolean	r_dowarp, r_dowarpold, r_viewchanged;
+
+int			numbtofpolys;
+btofpoly_t	*pbtofpolys;
+mvertex_t	*r_pcurrentvertbase;
+
+int			c_surf;
+int			r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs;
+qboolean	r_surfsonstack;
+int			r_clipflags;
+
+byte		*r_warpbuffer;
+
+byte		*r_stack_start;
+
+qboolean	r_fov_greater_than_90;
+
+//
+// view origin
+//
+vec3_t	vup, base_vup;
+vec3_t	vpn, base_vpn;
+vec3_t	vright, base_vright;
+vec3_t	r_origin;
+
+//
+// screen size info
+//
+refdef_t	r_refdef;
+float		xcenter, ycenter;
+float		xscale, yscale;
+float		xscaleinv, yscaleinv;
+float		xscaleshrink, yscaleshrink;
+float		aliasxscale, aliasyscale, aliasxcenter, aliasycenter;
+
+int		screenwidth;
+
+float	pixelAspect;
+float	screenAspect;
+float	verticalFieldOfView;
+float	xOrigin, yOrigin;
+
+mplane_t	screenedge[4];
+
+//
+// refresh flags
+//
+int		r_framecount = 1;	// so frame counts initialized to 0 don't match
+int		r_visframecount;
+int		d_spanpixcount;
+int		r_polycount;
+int		r_drawnpolycount;
+int		r_wholepolycount;
+
+#define		VIEWMODNAME_LENGTH	256
+char		viewmodname[VIEWMODNAME_LENGTH+1];
+int			modcount;
+
+int			*pfrustum_indexes[4];
+int			r_frustum_indexes[4*6];
+
+int		reinit_surfcache = 1;	// if 1, surface cache is currently empty and
+								// must be reinitialized for current cache size
+
+mleaf_t		*r_viewleaf, *r_oldviewleaf;
+
+texture_t	*r_notexture_mip;
+
+float		r_aliastransition, r_resfudge;
+
+int		d_lightstylevalue[256];	// 8.8 fraction of base light value
+
+float	dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2;
+float	se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2;
+
+void R_MarkLeaves (void);
+
+cvar_t	r_draworder = {"r_draworder","0"};
+cvar_t	r_speeds = {"r_speeds","0"};
+cvar_t	r_timegraph = {"r_timegraph","0"};
+cvar_t	r_graphheight = {"r_graphheight","10"};
+cvar_t	r_clearcolor = {"r_clearcolor","2"};
+cvar_t	r_waterwarp = {"r_waterwarp","1"};
+cvar_t	r_fullbright = {"r_fullbright","0"};
+cvar_t	r_drawentities = {"r_drawentities","1"};
+cvar_t	r_drawviewmodel = {"r_drawviewmodel","1"};
+cvar_t	r_aliasstats = {"r_polymodelstats","0"};
+cvar_t	r_dspeeds = {"r_dspeeds","0"};
+cvar_t	r_drawflat = {"r_drawflat", "0"};
+cvar_t	r_ambient = {"r_ambient", "0"};
+cvar_t	r_reportsurfout = {"r_reportsurfout", "0"};
+cvar_t	r_maxsurfs = {"r_maxsurfs", "0"};
+cvar_t	r_numsurfs = {"r_numsurfs", "0"};
+cvar_t	r_reportedgeout = {"r_reportedgeout", "0"};
+cvar_t	r_maxedges = {"r_maxedges", "0"};
+cvar_t	r_numedges = {"r_numedges", "0"};
+cvar_t	r_aliastransbase = {"r_aliastransbase", "200"};
+cvar_t	r_aliastransadj = {"r_aliastransadj", "100"};
+
+extern cvar_t	scr_fov;
+
+void CreatePassages (void);
+void SetVisibilityByPassages (void);
+
+/*
+==================
+R_InitTextures
+==================
+*/
+void	R_InitTextures (void)
+{
+	int		x,y, m;
+	byte	*dest;
+	
+// create a simple checkerboard texture for the default
+	r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture");
+	
+	r_notexture_mip->width = r_notexture_mip->height = 16;
+	r_notexture_mip->offsets[0] = sizeof(texture_t);
+	r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;
+	r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;
+	r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;
+	
+	for (m=0 ; m<4 ; m++)
+	{
+		dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
+		for (y=0 ; y< (16>>m) ; y++)
+			for (x=0 ; x< (16>>m) ; x++)
+			{
+				if (  (y< (8>>m) ) ^ (x< (8>>m) ) )
+					*dest++ = 0;
+				else
+					*dest++ = 0xff;
+			}
+	}	
+}
+
+/*
+===============
+R_Init
+===============
+*/
+void R_Init (void)
+{
+	int		dummy;
+	
+// get stack position so we can guess if we are going to overflow
+	r_stack_start = (byte *)&dummy;
+	
+	R_InitTurb ();
+	
+	Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);	
+	Cmd_AddCommand ("pointfile", R_ReadPointFile_f);	
+
+	Cvar_RegisterVariable (&r_draworder);
+	Cvar_RegisterVariable (&r_speeds);
+	Cvar_RegisterVariable (&r_timegraph);
+	Cvar_RegisterVariable (&r_graphheight);
+	Cvar_RegisterVariable (&r_drawflat);
+	Cvar_RegisterVariable (&r_ambient);
+	Cvar_RegisterVariable (&r_clearcolor);
+	Cvar_RegisterVariable (&r_waterwarp);
+	Cvar_RegisterVariable (&r_fullbright);
+	Cvar_RegisterVariable (&r_drawentities);
+	Cvar_RegisterVariable (&r_drawviewmodel);
+	Cvar_RegisterVariable (&r_aliasstats);
+	Cvar_RegisterVariable (&r_dspeeds);
+	Cvar_RegisterVariable (&r_reportsurfout);
+	Cvar_RegisterVariable (&r_maxsurfs);
+	Cvar_RegisterVariable (&r_numsurfs);
+	Cvar_RegisterVariable (&r_reportedgeout);
+	Cvar_RegisterVariable (&r_maxedges);
+	Cvar_RegisterVariable (&r_numedges);
+	Cvar_RegisterVariable (&r_aliastransbase);
+	Cvar_RegisterVariable (&r_aliastransadj);
+
+	Cvar_SetValue ("r_maxedges", (float)NUMSTACKEDGES);
+	Cvar_SetValue ("r_maxsurfs", (float)NUMSTACKSURFACES);
+
+	view_clipplanes[0].leftedge = true;
+	view_clipplanes[1].rightedge = true;
+	view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =
+			view_clipplanes[3].leftedge = false;
+	view_clipplanes[0].rightedge = view_clipplanes[2].rightedge =
+			view_clipplanes[3].rightedge = false;
+
+	r_refdef.xOrigin = XCENTERING;
+	r_refdef.yOrigin = YCENTERING;
+
+	R_InitParticles ();
+
+// TODO: collect 386-specific code in one place
+#if	id386
+	Sys_MakeCodeWriteable ((long)R_EdgeCodeStart,
+					     (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart);
+#endif	// id386
+
+	D_Init ();
+}
+
+/*
+===============
+R_NewMap
+===============
+*/
+void R_NewMap (void)
+{
+	int		i;
+	
+// clear out efrags in case the level hasn't been reloaded
+// FIXME: is this one short?
+	for (i=0 ; i<cl.worldmodel->numleafs ; i++)
+		cl.worldmodel->leafs[i].efrags = NULL;
+		 	
+	r_viewleaf = NULL;
+	R_ClearParticles ();
+
+	r_cnumsurfs = r_maxsurfs.value;
+
+	if (r_cnumsurfs <= MINSURFACES)
+		r_cnumsurfs = MINSURFACES;
+
+	if (r_cnumsurfs > NUMSTACKSURFACES)
+	{
+		surfaces = Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces");
+		surface_p = surfaces;
+		surf_max = &surfaces[r_cnumsurfs];
+		r_surfsonstack = false;
+	// surface 0 doesn't really exist; it's just a dummy because index 0
+	// is used to indicate no edge attached to surface
+		surfaces--;
+		R_SurfacePatch ();
+	}
+	else
+	{
+		r_surfsonstack = true;
+	}
+
+	r_maxedgesseen = 0;
+	r_maxsurfsseen = 0;
+
+	r_numallocatededges = r_maxedges.value;
+
+	if (r_numallocatededges < MINEDGES)
+		r_numallocatededges = MINEDGES;
+
+	if (r_numallocatededges <= NUMSTACKEDGES)
+	{
+		auxedges = NULL;
+	}
+	else
+	{
+		auxedges = Hunk_AllocName (r_numallocatededges * sizeof(edge_t),
+								   "edges");
+	}
+
+	r_dowarpold = false;
+	r_viewchanged = false;
+#ifdef PASSAGES
+CreatePassages ();
+#endif
+}
+
+
+/*
+===============
+R_SetVrect
+===============
+*/
+void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj)
+{
+	int		h;
+	float	size;
+
+	size = scr_viewsize.value > 100 ? 100 : scr_viewsize.value;
+	if (cl.intermission)
+	{
+		size = 100;
+		lineadj = 0;
+	}
+	size /= 100;
+
+	h = pvrectin->height - lineadj;
+	pvrect->width = pvrectin->width * size;
+	if (pvrect->width < 96)
+	{
+		size = 96.0 / pvrectin->width;
+		pvrect->width = 96;	// min for icons
+	}
+	pvrect->width &= ~7;
+	pvrect->height = pvrectin->height * size;
+	if (pvrect->height > pvrectin->height - lineadj)
+		pvrect->height = pvrectin->height - lineadj;
+
+	pvrect->height &= ~1;
+
+	pvrect->x = (pvrectin->width - pvrect->width)/2;
+	pvrect->y = (h - pvrect->height)/2;
+
+	{
+		if (lcd_x.value)
+		{
+			pvrect->y >>= 1;
+			pvrect->height >>= 1;
+		}
+	}
+}
+
+
+/*
+===============
+R_ViewChanged
+
+Called every time the vid structure or r_refdef changes.
+Guaranteed to be called before the first refresh
+===============
+*/
+void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect)
+{
+	int		i;
+	float	res_scale;
+
+	r_viewchanged = true;
+
+	R_SetVrect (pvrect, &r_refdef.vrect, lineadj);
+
+	r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/360*M_PI);
+	r_refdef.fvrectx = (float)r_refdef.vrect.x;
+	r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5;
+	r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1;
+	r_refdef.fvrecty = (float)r_refdef.vrect.y;
+	r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5;
+	r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width;
+	r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1;
+	r_refdef.fvrectright = (float)r_refdef.vrectright;
+	r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5;
+	r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99;
+	r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height;
+	r_refdef.fvrectbottom = (float)r_refdef.vrectbottom;
+	r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5;
+
+	r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale);
+	r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale);
+	r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale);
+	r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale);
+	r_refdef.aliasvrectright = r_refdef.aliasvrect.x +
+			r_refdef.aliasvrect.width;
+	r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y +
+			r_refdef.aliasvrect.height;
+
+	pixelAspect = aspect;
+	xOrigin = r_refdef.xOrigin;
+	yOrigin = r_refdef.yOrigin;
+	
+	screenAspect = r_refdef.vrect.width*pixelAspect /
+			r_refdef.vrect.height;
+// 320*200 1.0 pixelAspect = 1.6 screenAspect
+// 320*240 1.0 pixelAspect = 1.3333 screenAspect
+// proper 320*200 pixelAspect = 0.8333333
+
+	verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect;
+
+// values for perspective projection
+// if math were exact, the values would range from 0.5 to to range+0.5
+// hopefully they wll be in the 0.000001 to range+.999999 and truncate
+// the polygon rasterization will never render in the first row or column
+// but will definately render in the [range] row and column, so adjust the
+// buffer origin to get an exact edge to edge fill
+	xcenter = ((float)r_refdef.vrect.width * XCENTERING) +
+			r_refdef.vrect.x - 0.5;
+	aliasxcenter = xcenter * r_aliasuvscale;
+	ycenter = ((float)r_refdef.vrect.height * YCENTERING) +
+			r_refdef.vrect.y - 0.5;
+	aliasycenter = ycenter * r_aliasuvscale;
+
+	xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView;
+	aliasxscale = xscale * r_aliasuvscale;
+	xscaleinv = 1.0 / xscale;
+	yscale = xscale * pixelAspect;
+	aliasyscale = yscale * r_aliasuvscale;
+	yscaleinv = 1.0 / yscale;
+	xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView;
+	yscaleshrink = xscaleshrink*pixelAspect;
+
+// left side clip
+	screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView);
+	screenedge[0].normal[1] = 0;
+	screenedge[0].normal[2] = 1;
+	screenedge[0].type = PLANE_ANYZ;
+	
+// right side clip
+	screenedge[1].normal[0] =
+			1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView);
+	screenedge[1].normal[1] = 0;
+	screenedge[1].normal[2] = 1;
+	screenedge[1].type = PLANE_ANYZ;
+	
+// top side clip
+	screenedge[2].normal[0] = 0;
+	screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView);
+	screenedge[2].normal[2] = 1;
+	screenedge[2].type = PLANE_ANYZ;
+	
+// bottom side clip
+	screenedge[3].normal[0] = 0;
+	screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView);
+	screenedge[3].normal[2] = 1;	
+	screenedge[3].type = PLANE_ANYZ;
+	
+	for (i=0 ; i<4 ; i++)
+		VectorNormalizeNoRet (screenedge[i].normal);
+
+	res_scale = sqrt ((double)(r_refdef.vrect.width * r_refdef.vrect.height) /
+			          (320.0 * 152.0)) *
+			(2.0 / r_refdef.horizontalFieldOfView);
+	r_aliastransition = r_aliastransbase.value * res_scale;
+	r_resfudge = r_aliastransadj.value * res_scale;
+
+	if (scr_fov.value <= 90.0)
+		r_fov_greater_than_90 = false;
+	else
+		r_fov_greater_than_90 = true;
+
+// TODO: collect 386-specific code in one place
+#if	id386
+	if (r_pixbytes == 1)
+	{
+		Sys_MakeCodeWriteable ((long)R_Surf8Start,
+						     (long)R_Surf8End - (long)R_Surf8Start);
+		colormap = vid.colormap;
+		R_Surf8Patch ();
+	}
+	else
+	{
+		Sys_MakeCodeWriteable ((long)R_Surf16Start,
+						     (long)R_Surf16End - (long)R_Surf16Start);
+		colormap = vid.colormap16;
+		R_Surf16Patch ();
+	}
+#endif	// id386
+
+	D_ViewChanged ();
+}
+
+
+/*
+===============
+R_MarkLeaves
+===============
+*/
+void R_MarkLeaves (void)
+{
+	byte	*vis;
+	mnode_t	*node;
+	int		i;
+
+	if (r_oldviewleaf == r_viewleaf)
+		return;
+	
+	r_visframecount++;
+	r_oldviewleaf = r_viewleaf;
+
+	vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);
+		
+	for (i=0 ; i<cl.worldmodel->numleafs ; i++)
+	{
+		if (vis[i>>3] & (1<<(i&7)))
+		{
+			node = (mnode_t *)&cl.worldmodel->leafs[i+1];
+			do
+			{
+				if (node->visframe == r_visframecount)
+					break;
+				node->visframe = r_visframecount;
+				node = node->parent;
+			} while (node);
+		}
+	}
+}
+
+
+/*
+=============
+R_DrawEntitiesOnList
+=============
+*/
+void R_DrawEntitiesOnList (void)
+{
+	int			i, j;
+	int			lnum;
+	alight_t	lighting;
+// FIXME: remove and do real lighting
+	float		lightvec[3] = {-1, 0, 0};
+	vec3_t		dist;
+	float		add;
+
+	if (!r_drawentities.value)
+		return;
+
+	for (i=0 ; i<cl_numvisedicts ; i++)
+	{
+		currententity = cl_visedicts[i];
+
+		if (currententity == &cl_entities[cl.viewentity])
+			continue;	// don't draw the player
+
+		switch (currententity->model->type)
+		{
+		case mod_sprite:
+			VectorCopy (currententity->origin, r_entorigin);
+			VectorSubtract (r_origin, r_entorigin, modelorg);
+			R_DrawSprite ();
+			break;
+
+		case mod_alias:
+			VectorCopy (currententity->origin, r_entorigin);
+			VectorSubtract (r_origin, r_entorigin, modelorg);
+
+		// see if the bounding box lets us trivially reject, also sets
+		// trivial accept status
+			if (R_AliasCheckBBox ())
+			{
+				j = R_LightPoint (currententity->origin);
+	
+				lighting.ambientlight = j;
+				lighting.shadelight = j;
+
+				lighting.plightvec = lightvec;
+
+				for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
+				{
+					if (cl_dlights[lnum].die >= cl.time)
+					{
+						VectorSubtract (currententity->origin,
+										cl_dlights[lnum].origin,
+										dist);
+						add = cl_dlights[lnum].radius - Length(dist);
+	
+						if (add > 0)
+							lighting.ambientlight += add;
+					}
+				}
+	
+			// clamp lighting so it doesn't overbright as much
+				if (lighting.ambientlight > 128)
+					lighting.ambientlight = 128;
+				if (lighting.ambientlight + lighting.shadelight > 192)
+					lighting.shadelight = 192 - lighting.ambientlight;
+
+				R_AliasDrawModel (&lighting);
+			}
+
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+/*
+=============
+R_DrawViewModel
+=============
+*/
+void R_DrawViewModel (void)
+{
+// FIXME: remove and do real lighting
+	float		lightvec[3] = {-1, 0, 0};
+	int			j;
+	int			lnum;
+	vec3_t		dist;
+	float		add;
+	dlight_t	*dl;
+	
+	if (!r_drawviewmodel.value || r_fov_greater_than_90)
+		return;
+
+	if (cl.items & IT_INVISIBILITY)
+		return;
+
+	if (cl.stats[STAT_HEALTH] <= 0)
+		return;
+
+	currententity = &cl.viewent;
+	if (!currententity->model)
+		return;
+
+	VectorCopy (currententity->origin, r_entorigin);
+	VectorSubtract (r_origin, r_entorigin, modelorg);
+
+	VectorCopy (vup, viewlightvec);
+	VectorInverse (viewlightvec);
+
+	j = R_LightPoint (currententity->origin);
+
+	if (j < 24)
+		j = 24;		// allways give some light on gun
+	r_viewlighting.ambientlight = j;
+	r_viewlighting.shadelight = j;
+
+// add dynamic lights		
+	for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
+	{
+		dl = &cl_dlights[lnum];
+		if (!dl->radius)
+			continue;
+		if (!dl->radius)
+			continue;
+		if (dl->die < cl.time)
+			continue;
+
+		VectorSubtract (currententity->origin, dl->origin, dist);
+		add = dl->radius - Length(dist);
+		if (add > 0)
+			r_viewlighting.ambientlight += add;
+	}
+
+// clamp lighting so it doesn't overbright as much
+	if (r_viewlighting.ambientlight > 128)
+		r_viewlighting.ambientlight = 128;
+	if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192)
+		r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight;
+
+	r_viewlighting.plightvec = lightvec;
+
+#ifdef QUAKE2
+	cl.light_level = r_viewlighting.ambientlight;
+#endif
+
+	R_AliasDrawModel (&r_viewlighting);
+}
+
+
+/*
+=============
+R_BmodelCheckBBox
+=============
+*/
+int R_BmodelCheckBBox (model_t *clmodel, float *minmaxs)
+{
+	int			i, *pindex, clipflags;
+	vec3_t		acceptpt, rejectpt;
+	double		d;
+
+	clipflags = 0;
+
+	if (currententity->angles[0] || currententity->angles[1]
+		|| currententity->angles[2])
+	{
+		for (i=0 ; i<4 ; i++)
+		{
+			d = DotProduct (currententity->origin, view_clipplanes[i].normal);
+			d -= view_clipplanes[i].dist;
+
+			if (d <= -clmodel->radius)
+				return BMODEL_FULLY_CLIPPED;
+
+			if (d <= clmodel->radius)
+				clipflags |= (1<<i);
+		}
+	}
+	else
+	{
+		for (i=0 ; i<4 ; i++)
+		{
+		// generate accept and reject points
+		// FIXME: do with fast look-ups or integer tests based on the sign bit
+		// of the floating point values
+
+			pindex = pfrustum_indexes[i];
+
+			rejectpt[0] = minmaxs[pindex[0]];
+			rejectpt[1] = minmaxs[pindex[1]];
+			rejectpt[2] = minmaxs[pindex[2]];
+			
+			d = DotProduct (rejectpt, view_clipplanes[i].normal);
+			d -= view_clipplanes[i].dist;
+
+			if (d <= 0)
+				return BMODEL_FULLY_CLIPPED;
+
+			acceptpt[0] = minmaxs[pindex[3+0]];
+			acceptpt[1] = minmaxs[pindex[3+1]];
+			acceptpt[2] = minmaxs[pindex[3+2]];
+
+			d = DotProduct (acceptpt, view_clipplanes[i].normal);
+			d -= view_clipplanes[i].dist;
+
+			if (d <= 0)
+				clipflags |= (1<<i);
+		}
+	}
+
+	return clipflags;
+}
+
+
+/*
+=============
+R_DrawBEntitiesOnList
+=============
+*/
+void R_DrawBEntitiesOnList (void)
+{
+	int			i, j, k, clipflags;
+	vec3_t		oldorigin;
+	model_t		*clmodel;
+	float		minmaxs[6];
+
+	if (!r_drawentities.value)
+		return;
+
+	VectorCopy (modelorg, oldorigin);
+	insubmodel = true;
+	r_dlightframecount = r_framecount;
+
+	for (i=0 ; i<cl_numvisedicts ; i++)
+	{
+		currententity = cl_visedicts[i];
+
+		switch (currententity->model->type)
+		{
+		case mod_brush:
+
+			clmodel = currententity->model;
+
+		// see if the bounding box lets us trivially reject, also sets
+		// trivial accept status
+			for (j=0 ; j<3 ; j++)
+			{
+				minmaxs[j] = currententity->origin[j] +
+						clmodel->mins[j];
+				minmaxs[3+j] = currententity->origin[j] +
+						clmodel->maxs[j];
+			}
+
+			clipflags = R_BmodelCheckBBox (clmodel, minmaxs);
+
+			if (clipflags != BMODEL_FULLY_CLIPPED)
+			{
+				VectorCopy (currententity->origin, r_entorigin);
+				VectorSubtract (r_origin, r_entorigin, modelorg);
+			// FIXME: is this needed?
+				VectorCopy (modelorg, r_worldmodelorg);
+		
+				r_pcurrentvertbase = clmodel->vertexes;
+		
+			// FIXME: stop transforming twice
+				R_RotateBmodel ();
+
+			// calculate dynamic lighting for bmodel if it's not an
+			// instanced model
+				if (clmodel->firstmodelsurface != 0)
+				{
+					for (k=0 ; k<MAX_DLIGHTS ; k++)
+					{
+						if ((cl_dlights[k].die < cl.time) ||
+							(!cl_dlights[k].radius))
+						{
+							continue;
+						}
+
+						R_MarkLights (&cl_dlights[k], 1<<k,
+							clmodel->nodes + clmodel->hulls[0].firstclipnode);
+					}
+				}
+
+			// if the driver wants polygons, deliver those. Z-buffering is on
+			// at this point, so no clipping to the world tree is needed, just
+			// frustum clipping
+				if (r_drawpolys | r_drawculledpolys)
+				{
+					R_ZDrawSubmodelPolys (clmodel);
+				}
+				else
+				{
+					r_pefragtopnode = NULL;
+
+					for (j=0 ; j<3 ; j++)
+					{
+						r_emins[j] = minmaxs[j];
+						r_emaxs[j] = minmaxs[3+j];
+					}
+
+					R_SplitEntityOnNode2 (cl.worldmodel->nodes);
+
+					if (r_pefragtopnode)
+					{
+						currententity->topnode = r_pefragtopnode;
+	
+						if (r_pefragtopnode->contents >= 0)
+						{
+						// not a leaf; has to be clipped to the world BSP
+							r_clipflags = clipflags;
+							R_DrawSolidClippedSubmodelPolygons (clmodel);
+						}
+						else
+						{
+						// falls entirely in one leaf, so we just put all the
+						// edges in the edge list and let 1/z sorting handle
+						// drawing order
+							R_DrawSubmodelPolygons (clmodel, clipflags);
+						}
+	
+						currententity->topnode = NULL;
+					}
+				}
+
+			// put back world rotation and frustum clipping		
+			// FIXME: R_RotateBmodel should just work off base_vxx
+				VectorCopy (base_vpn, vpn);
+				VectorCopy (base_vup, vup);
+				VectorCopy (base_vright, vright);
+				VectorCopy (base_modelorg, modelorg);
+				VectorCopy (oldorigin, modelorg);
+				R_TransformFrustum ();
+			}
+
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	insubmodel = false;
+}
+
+
+/*
+================
+R_EdgeDrawing
+================
+*/
+void R_EdgeDrawing (void)
+{
+	edge_t	ledges[NUMSTACKEDGES +
+				((CACHE_SIZE - 1) / sizeof(edge_t)) + 1];
+	surf_t	lsurfs[NUMSTACKSURFACES +
+				((CACHE_SIZE - 1) / sizeof(surf_t)) + 1];
+
+	if (auxedges)
+	{
+		r_edges = auxedges;
+	}
+	else
+	{
+		r_edges =  (edge_t *)
+				(((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
+	}
+
+	if (r_surfsonstack)
+	{
+		surfaces =  (surf_t *)
+				(((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
+		surf_max = &surfaces[r_cnumsurfs];
+	// surface 0 doesn't really exist; it's just a dummy because index 0
+	// is used to indicate no edge attached to surface
+		surfaces--;
+		R_SurfacePatch ();
+	}
+
+	R_BeginEdgeFrame ();
+
+	if (r_dspeeds.value)
+	{
+		rw_time1 = Sys_FloatTime ();
+	}
+
+	R_RenderWorld ();
+
+	if (r_drawculledpolys)
+		R_ScanEdges ();
+
+// only the world can be drawn back to front with no z reads or compares, just
+// z writes, so have the driver turn z compares on now
+	D_TurnZOn ();
+
+	if (r_dspeeds.value)
+	{
+		rw_time2 = Sys_FloatTime ();
+		db_time1 = rw_time2;
+	}
+
+	R_DrawBEntitiesOnList ();
+
+	if (r_dspeeds.value)
+	{
+		db_time2 = Sys_FloatTime ();
+		se_time1 = db_time2;
+	}
+
+	if (!r_dspeeds.value)
+	{
+		VID_UnlockBuffer ();
+		S_ExtraUpdate ();	// don't let sound get messed up if going slow
+		VID_LockBuffer ();
+	}
+	
+	if (!(r_drawpolys | r_drawculledpolys))
+		R_ScanEdges ();
+}
+
+
+/*
+================
+R_RenderView
+
+r_refdef must be set before the first call
+================
+*/
+void R_RenderView_ (void)
+{
+	byte	warpbuffer[WARP_WIDTH * WARP_HEIGHT];
+
+	r_warpbuffer = warpbuffer;
+
+	if (r_timegraph.value || r_speeds.value || r_dspeeds.value)
+		r_time1 = Sys_FloatTime ();
+
+	R_SetupFrame ();
+
+#ifdef PASSAGES
+SetVisibilityByPassages ();
+#else
+	R_MarkLeaves ();	// done here so we know if we're in water
+#endif
+
+// make FDIV fast. This reduces timing precision after we've been running for a
+// while, so we don't do it globally.  This also sets chop mode, and we do it
+// here so that setup stuff like the refresh area calculations match what's
+// done in screen.c
+	Sys_LowFPPrecision ();
+
+	if (!cl_entities[0].model || !cl.worldmodel)
+		Sys_Error ("R_RenderView: NULL worldmodel");
+		
+	if (!r_dspeeds.value)
+	{
+		VID_UnlockBuffer ();
+		S_ExtraUpdate ();	// don't let sound get messed up if going slow
+		VID_LockBuffer ();
+	}
+	
+	R_EdgeDrawing ();
+
+        rb->yield(); // let sound run
+        
+	if (!r_dspeeds.value)
+	{
+		VID_UnlockBuffer ();
+		S_ExtraUpdate ();	// don't let sound get messed up if going slow
+		VID_LockBuffer ();
+	}
+	
+	if (r_dspeeds.value)
+	{
+		se_time2 = Sys_FloatTime ();
+		de_time1 = se_time2;
+	}
+
+	R_DrawEntitiesOnList ();
+
+	if (r_dspeeds.value)
+	{
+		de_time2 = Sys_FloatTime ();
+		dv_time1 = de_time2;
+	}
+
+	R_DrawViewModel ();
+
+	if (r_dspeeds.value)
+	{
+		dv_time2 = Sys_FloatTime ();
+		dp_time1 = Sys_FloatTime ();
+	}
+
+	R_DrawParticles ();
+
+        rb->yield(); // let sound run
+
+	if (r_dspeeds.value)
+		dp_time2 = Sys_FloatTime ();
+
+	if (r_dowarp)
+		D_WarpScreen ();
+
+	V_SetContentsColor (r_viewleaf->contents);
+
+	if (r_timegraph.value)
+		R_TimeGraph ();
+
+	if (r_aliasstats.value)
+		R_PrintAliasStats ();
+		
+	if (r_speeds.value)
+		R_PrintTimes ();
+
+	if (r_dspeeds.value)
+		R_PrintDSpeeds ();
+
+	if (r_reportsurfout.value && r_outofsurfaces)
+		Con_Printf ("Short %d surfaces\n", r_outofsurfaces);
+
+	if (r_reportedgeout.value && r_outofedges)
+		Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3);
+
+// back to high floating-point precision
+	Sys_HighFPPrecision ();
+}
+
+void R_RenderView (void)
+{
+	int		dummy;
+	int		delta;
+	
+	delta = (byte *)&dummy - r_stack_start;
+	if (delta < -10000 || delta > 10000)
+		Sys_Error ("R_RenderView: called without enough stack");
+
+	if ( Hunk_LowMark() & 3 )
+		Sys_Error ("Hunk is missaligned");
+
+	if ( (long)(&dummy) & 3 )
+		Sys_Error ("Stack is missaligned");
+
+	if ( (long)(&r_warpbuffer) & 3 )
+		Sys_Error ("Globals are missaligned");
+
+	R_RenderView_ ();
+}
+
+/*
+================
+R_InitTurb
+================
+*/
+void R_InitTurb (void)
+{
+	int		i;
+	
+	for (i=0 ; i<(SIN_BUFFER_SIZE) ; i++)
+	{
+		sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP;
+		intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2;	// AMP2, not 20
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_misc.c b/apps/plugins/sdl/progs/quake/r_misc.c
new file mode 100644
index 0000000..c0aa7e9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_misc.c
@@ -0,0 +1,523 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_misc.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+
+/*
+===============
+R_CheckVariables
+===============
+*/
+void R_CheckVariables (void)
+{
+	static float	oldbright;
+
+	if (r_fullbright.value != oldbright)
+	{
+		oldbright = r_fullbright.value;
+		D_FlushCaches ();	// so all lighting changes
+	}
+}
+
+
+/*
+============
+Show
+
+Debugging use
+============
+*/
+void Show (void)
+{
+	vrect_t	vr;
+
+	vr.x = vr.y = 0;
+	vr.width = vid.width;
+	vr.height = vid.height;
+	vr.pnext = NULL;
+	VID_Update (&vr);
+}
+
+
+/*
+====================
+R_TimeRefresh_f
+
+For program optimization
+====================
+*/
+void R_TimeRefresh_f (void)
+{
+	int			i;
+	float		start, stop, time;
+	int			startangle;
+	vrect_t		vr;
+
+	startangle = r_refdef.viewangles[1];
+	
+	start = Sys_FloatTime ();
+	for (i=0 ; i<128 ; i++)
+	{
+		r_refdef.viewangles[1] = i/128.0*360.0;
+
+		VID_LockBuffer ();
+
+		R_RenderView ();
+
+		VID_UnlockBuffer ();
+
+		vr.x = r_refdef.vrect.x;
+		vr.y = r_refdef.vrect.y;
+		vr.width = r_refdef.vrect.width;
+		vr.height = r_refdef.vrect.height;
+		vr.pnext = NULL;
+		VID_Update (&vr);
+	}
+	stop = Sys_FloatTime ();
+	time = stop-start;
+	Con_Printf ("%f seconds (%f fps)\n", time, 128/time);
+	
+	r_refdef.viewangles[1] = startangle;
+}
+
+
+/*
+================
+R_LineGraph
+
+Only called by R_DisplayTime
+================
+*/
+void R_LineGraph (int x, int y, int h)
+{
+	int		i;
+	byte	*dest;
+	int		s;
+
+// FIXME: should be disabled on no-buffer adapters, or should be in the driver
+	
+	x += r_refdef.vrect.x;
+	y += r_refdef.vrect.y;
+	
+	dest = vid.buffer + vid.rowbytes*y + x;
+	
+	s = r_graphheight.value;
+	
+	if (h>s)
+		h = s;
+		
+	for (i=0 ; i<h ; i++, dest -= vid.rowbytes*2)
+	{
+		dest[0] = 0xff;
+		*(dest-vid.rowbytes) = 0x30;
+	}
+	for ( ; i<s ; i++, dest -= vid.rowbytes*2)
+	{
+		dest[0] = 0x30;
+		*(dest-vid.rowbytes) = 0x30;
+	}
+}
+
+/*
+==============
+R_TimeGraph
+
+Performance monitoring tool
+==============
+*/
+#define	MAX_TIMINGS		100
+extern float mouse_x, mouse_y;
+void R_TimeGraph (void)
+{
+	static	int		timex;
+	int		a;
+	float	r_time2;
+	static byte	r_timings[MAX_TIMINGS];
+	int		x;
+	
+	r_time2 = Sys_FloatTime ();
+
+	a = (r_time2-r_time1)/0.01;
+//a = fabs(mouse_y * 0.05);
+//a = (int)((r_refdef.vieworg[2] + 1024)/1)%(int)r_graphheight.value;
+//a = fabs(velocity[0])/20;
+//a = ((int)fabs(origin[0])/8)%20;
+//a = (cl.idealpitch + 30)/5;
+	r_timings[timex] = a;
+	a = timex;
+
+	if (r_refdef.vrect.width <= MAX_TIMINGS)
+		x = r_refdef.vrect.width-1;
+	else
+		x = r_refdef.vrect.width -
+				(r_refdef.vrect.width - MAX_TIMINGS)/2;
+	do
+	{
+		R_LineGraph (x, r_refdef.vrect.height-2, r_timings[a]);
+		if (x==0)
+			break;		// screen too small to hold entire thing
+		x--;
+		a--;
+		if (a == -1)
+			a = MAX_TIMINGS-1;
+	} while (a != timex);
+
+	timex = (timex+1)%MAX_TIMINGS;
+}
+
+
+/*
+=============
+R_PrintTimes
+=============
+*/
+void R_PrintTimes (void)
+{
+	float	r_time2;
+	float		ms;
+
+	r_time2 = Sys_FloatTime ();
+
+	ms = 1000* (r_time2 - r_time1);
+	
+	Con_Printf ("%5.1f ms %3i/%3i/%3i poly %3i surf\n",
+				ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf);
+	c_surf = 0;
+}
+
+
+/*
+=============
+R_PrintDSpeeds
+=============
+*/
+void R_PrintDSpeeds (void)
+{
+	float	ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, dv_time;
+
+	r_time2 = Sys_FloatTime ();
+
+	dp_time = (dp_time2 - dp_time1) * 1000;
+	rw_time = (rw_time2 - rw_time1) * 1000;
+	db_time = (db_time2 - db_time1) * 1000;
+	se_time = (se_time2 - se_time1) * 1000;
+	de_time = (de_time2 - de_time1) * 1000;
+	dv_time = (dv_time2 - dv_time1) * 1000;
+	ms = (r_time2 - r_time1) * 1000;
+
+	Con_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n",
+				(int)ms, dp_time, (int)rw_time, db_time, (int)se_time, de_time,
+				dv_time);
+}
+
+
+/*
+=============
+R_PrintAliasStats
+=============
+*/
+void R_PrintAliasStats (void)
+{
+	Con_Printf ("%3i polygon model drawn\n", r_amodels_drawn);
+}
+
+
+void WarpPalette (void)
+{
+	int		i,j;
+	byte	newpalette[768];
+	int		basecolor[3];
+	
+	basecolor[0] = 130;
+	basecolor[1] = 80;
+	basecolor[2] = 50;
+
+// pull the colors halfway to bright brown
+	for (i=0 ; i<256 ; i++)
+	{
+		for (j=0 ; j<3 ; j++)
+		{
+			newpalette[i*3+j] = (host_basepal[i*3+j] + basecolor[j])/2;
+		}
+	}
+	
+	VID_ShiftPalette (newpalette);
+}
+
+
+/*
+===================
+R_TransformFrustum
+===================
+*/
+void R_TransformFrustum (void)
+{
+	int		i;
+	vec3_t	v, v2;
+	
+	for (i=0 ; i<4 ; i++)
+	{
+		v[0] = screenedge[i].normal[2];
+		v[1] = -screenedge[i].normal[0];
+		v[2] = screenedge[i].normal[1];
+
+		v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0];
+		v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1];
+		v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2];
+
+		VectorCopy (v2, view_clipplanes[i].normal);
+
+		view_clipplanes[i].dist = DotProduct (modelorg, v2);
+	}
+}
+
+
+#if	!id386
+
+/*
+================
+TransformVector
+================
+*/
+void TransformVector (vec3_t in, vec3_t out)
+{
+	out[0] = DotProduct(in,vright);
+	out[1] = DotProduct(in,vup);
+	out[2] = DotProduct(in,vpn);		
+}
+
+#endif
+
+
+/*
+================
+R_TransformPlane
+================
+*/
+void R_TransformPlane (mplane_t *p, float *normal, float *dist)
+{
+	float	d;
+	
+	d = DotProduct (r_origin, p->normal);
+	*dist = p->dist - d;
+// TODO: when we have rotating entities, this will need to use the view matrix
+	TransformVector (p->normal, normal);
+}
+
+
+/*
+===============
+R_SetUpFrustumIndexes
+===============
+*/
+void R_SetUpFrustumIndexes (void)
+{
+	int		i, j, *pindex;
+
+	pindex = r_frustum_indexes;
+
+	for (i=0 ; i<4 ; i++)
+	{
+		for (j=0 ; j<3 ; j++)
+		{
+			if (view_clipplanes[i].normal[j] < 0)
+			{
+				pindex[j] = j;
+				pindex[j+3] = j+3;
+			}
+			else
+			{
+				pindex[j] = j+3;
+				pindex[j+3] = j;
+			}
+		}
+
+	// FIXME: do just once at start
+		pfrustum_indexes[i] = pindex;
+		pindex += 6;
+	}
+}
+
+
+/*
+===============
+R_SetupFrame
+===============
+*/
+void R_SetupFrame (void)
+{
+	int				edgecount;
+	vrect_t			vrect;
+	float			w, h;
+
+// don't allow cheats in multiplayer
+	if (cl.maxclients > 1)
+	{
+		Cvar_Set ("r_draworder", "0");
+		Cvar_Set ("r_fullbright", "0");
+		Cvar_Set ("r_ambient", "0");
+		Cvar_Set ("r_drawflat", "0");
+	}
+
+	if (r_numsurfs.value)
+	{
+		if ((surface_p - surfaces) > r_maxsurfsseen)
+			r_maxsurfsseen = surface_p - surfaces;
+
+		Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces,
+				surf_max - surfaces, r_maxsurfsseen);
+	}
+
+	if (r_numedges.value)
+	{
+		edgecount = edge_p - r_edges;
+
+		if (edgecount > r_maxedgesseen)
+			r_maxedgesseen = edgecount;
+
+		Con_Printf ("Used %d of %d edges; %d max\n", edgecount,
+				r_numallocatededges, r_maxedgesseen);
+	}
+
+	r_refdef.ambientlight = r_ambient.value;
+
+	if (r_refdef.ambientlight < 0)
+		r_refdef.ambientlight = 0;
+
+	if (!sv.active)
+		r_draworder.value = 0;	// don't let cheaters look behind walls
+		
+	R_CheckVariables ();
+	
+	R_AnimateLight ();
+
+	r_framecount++;
+
+	numbtofpolys = 0;
+
+// debugging
+#if 0
+r_refdef.vieworg[0]=  80;
+r_refdef.vieworg[1]=      64;
+r_refdef.vieworg[2]=      40;
+r_refdef.viewangles[0]=    0;
+r_refdef.viewangles[1]=    46.763641357;
+r_refdef.viewangles[2]=    0;
+#endif
+
+// build the transformation matrix for the given view angles
+	VectorCopy (r_refdef.vieworg, modelorg);
+	VectorCopy (r_refdef.vieworg, r_origin);
+
+	AngleVectors (r_refdef.viewangles, vpn, vright, vup);
+
+// current viewleaf
+	r_oldviewleaf = r_viewleaf;
+	r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel);
+
+	r_dowarpold = r_dowarp;
+	r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER);
+
+	if ((r_dowarp != r_dowarpold) || r_viewchanged || lcd_x.value)
+	{
+		if (r_dowarp)
+		{
+			if ((vid.width <= vid.maxwarpwidth) &&
+				(vid.height <= vid.maxwarpheight))
+			{
+				vrect.x = 0;
+				vrect.y = 0;
+				vrect.width = vid.width;
+				vrect.height = vid.height;
+
+				R_ViewChanged (&vrect, sb_lines, vid.aspect);
+			}
+			else
+			{
+				w = vid.width;
+				h = vid.height;
+
+				if (w > vid.maxwarpwidth)
+				{
+					h *= (float)vid.maxwarpwidth / w;
+					w = vid.maxwarpwidth;
+				}
+
+				if (h > vid.maxwarpheight)
+				{
+					h = vid.maxwarpheight;
+					w *= (float)vid.maxwarpheight / h;
+				}
+
+				vrect.x = 0;
+				vrect.y = 0;
+				vrect.width = (int)w;
+				vrect.height = (int)h;
+
+				R_ViewChanged (&vrect,
+							   (int)((float)sb_lines * (h/(float)vid.height)),
+							   vid.aspect * (h / w) *
+								 ((float)vid.width / (float)vid.height));
+			}
+		}
+		else
+		{
+			vrect.x = 0;
+			vrect.y = 0;
+			vrect.width = vid.width;
+			vrect.height = vid.height;
+
+			R_ViewChanged (&vrect, sb_lines, vid.aspect);
+		}
+
+		r_viewchanged = false;
+	}
+
+// start off with just the four screen edge clip planes
+	R_TransformFrustum ();
+
+// save base values
+	VectorCopy (vpn, base_vpn);
+	VectorCopy (vright, base_vright);
+	VectorCopy (vup, base_vup);
+	VectorCopy (modelorg, base_modelorg);
+
+	R_SetSkyFrame ();
+
+	R_SetUpFrustumIndexes ();
+
+	r_cache_thrash = false;
+
+// clear frame counts
+	c_faceclip = 0;
+	d_spanpixcount = 0;
+	r_polycount = 0;
+	r_drawnpolycount = 0;
+	r_wholepolycount = 0;
+	r_amodels_drawn = 0;
+	r_outofsurfaces = 0;
+	r_outofedges = 0;
+
+	D_SetupFrame ();
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_part.c b/apps/plugins/sdl/progs/quake/r_part.c
new file mode 100644
index 0000000..8c0dcff
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_part.c
@@ -0,0 +1,802 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 "quakedef.h"
+#include "r_local.h"
+
+#define MAX_PARTICLES			2048	// default max # of particles at one
+										//  time
+#define ABSOLUTE_MIN_PARTICLES	512		// no fewer than this no matter what's
+										//  on the command line
+
+int		ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
+int		ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
+int		ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
+
+particle_t	*active_particles, *free_particles;
+
+particle_t	*particles;
+int			r_numparticles;
+
+vec3_t			r_pright, r_pup, r_ppn;
+
+
+/*
+===============
+R_InitParticles
+===============
+*/
+void R_InitParticles (void)
+{
+	int		i;
+
+	i = COM_CheckParm ("-particles");
+
+	if (i)
+	{
+		r_numparticles = (int)(Q_atoi(com_argv[i+1]));
+		if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
+			r_numparticles = ABSOLUTE_MIN_PARTICLES;
+	}
+	else
+	{
+		r_numparticles = MAX_PARTICLES;
+	}
+
+	particles = (particle_t *)
+			Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles");
+}
+
+#ifdef QUAKE2
+void R_DarkFieldParticles (entity_t *ent)
+{
+	int			i, j, k;
+	particle_t	*p;
+	float		vel;
+	vec3_t		dir;
+	vec3_t		org;
+
+	org[0] = ent->origin[0];
+	org[1] = ent->origin[1];
+	org[2] = ent->origin[2];
+	for (i=-16 ; i<16 ; i+=8)
+		for (j=-16 ; j<16 ; j+=8)
+			for (k=0 ; k<32 ; k+=8)
+			{
+				if (!free_particles)
+					return;
+				p = free_particles;
+				free_particles = p->next;
+				p->next = active_particles;
+				active_particles = p;
+		
+				p->die = cl.time + 0.2 + (rand()&7) * 0.02;
+				p->color = 150 + rand()%6;
+				p->type = pt_slowgrav;
+				
+				dir[0] = j*8;
+				dir[1] = i*8;
+				dir[2] = k*8;
+	
+				p->org[0] = org[0] + i + (rand()&3);
+				p->org[1] = org[1] + j + (rand()&3);
+				p->org[2] = org[2] + k + (rand()&3);
+	
+				VectorNormalizeNoRet (dir);						
+				vel = 50 + (rand()&63);
+				VectorScale (dir, vel, p->vel);
+			}
+}
+#endif
+
+
+/*
+===============
+R_EntityParticles
+===============
+*/
+
+#define NUMVERTEXNORMALS	162
+extern	float	r_avertexnormals[NUMVERTEXNORMALS][3];
+vec3_t	avelocities[NUMVERTEXNORMALS];
+float	beamlength = 16;
+vec3_t	avelocity = {23, 7, 3};
+float	partstep = 0.01;
+float	timescale = 0.01;
+
+void R_EntityParticles (entity_t *ent)
+{
+	int			count;
+	int			i;
+	particle_t	*p;
+	float		angle;
+	float		sr, sp, sy, cr, cp, cy;
+	vec3_t		forward;
+	float		dist;
+	
+	dist = 64;
+	count = 50;
+
+if (!avelocities[0][0])
+{
+for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
+avelocities[0][i] = (rand()&255) * 0.01;
+}
+
+
+	for (i=0 ; i<NUMVERTEXNORMALS ; i++)
+	{
+		angle = cl.time * avelocities[i][0];
+		sy = sin(angle);
+		cy = cos(angle);
+		angle = cl.time * avelocities[i][1];
+		sp = sin(angle);
+		cp = cos(angle);
+		angle = cl.time * avelocities[i][2];
+		sr = sin(angle);
+		cr = cos(angle);
+	
+		forward[0] = cp*cy;
+		forward[1] = cp*sy;
+		forward[2] = -sp;
+
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+
+		p->die = cl.time + 0.01;
+		p->color = 0x6f;
+		p->type = pt_explode;
+		
+		p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength;			
+		p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength;			
+		p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength;			
+	}
+}
+
+
+/*
+===============
+R_ClearParticles
+===============
+*/
+void R_ClearParticles (void)
+{
+	int		i;
+	
+	free_particles = &particles[0];
+	active_particles = NULL;
+
+	for (i=0 ;i<r_numparticles ; i++)
+		particles[i].next = &particles[i+1];
+	particles[r_numparticles-1].next = NULL;
+}
+
+
+void R_ReadPointFile_f (void)
+{
+	FILE	*f;
+	vec3_t	org;
+	int		r;
+	int		c;
+	particle_t	*p;
+	char	name[MAX_OSPATH];
+	
+	sprintf (name,"maps/%s.pts", sv.name);
+
+	COM_FOpenFile (name, &f);
+	if (!f)
+	{
+		Con_Printf ("couldn't open %s\n", name);
+		return;
+	}
+	
+	Con_Printf ("Reading %s...\n", name);
+	c = 0;
+	for ( ;; )
+	{
+            r = fscanf (f,"%f ", &org[0]);
+            r += fscanf (f,"%f ", &org[1]);
+            r += fscanf (f,"%f\n", &org[2]);
+		if (r != 3)
+			break;
+		c++;
+		
+		if (!free_particles)
+		{
+			Con_Printf ("Not enough free particles\n");
+			break;
+		}
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+		
+		p->die = 99999;
+		p->color = (-c)&15;
+		p->type = pt_static;
+		VectorCopy (vec3_origin, p->vel);
+		VectorCopy (org, p->org);
+	}
+
+	fclose (f);
+	Con_Printf ("%i points read\n", c);
+}
+
+/*
+===============
+R_ParseParticleEffect
+
+Parse an effect out of the server message
+===============
+*/
+void R_ParseParticleEffect (void)
+{
+	vec3_t		org, dir;
+	int			i, count, msgcount, color;
+	
+	for (i=0 ; i<3 ; i++)
+		org[i] = MSG_ReadCoord ();
+	for (i=0 ; i<3 ; i++)
+		dir[i] = MSG_ReadChar () * (1.0/16);
+	msgcount = MSG_ReadByte ();
+	color = MSG_ReadByte ();
+
+if (msgcount == 255)
+	count = 1024;
+else
+	count = msgcount;
+	
+	R_RunParticleEffect (org, dir, color, count);
+}
+	
+/*
+===============
+R_ParticleExplosion
+
+===============
+*/
+void R_ParticleExplosion (vec3_t org)
+{
+	int			i, j;
+	particle_t	*p;
+	
+	for (i=0 ; i<1024 ; i++)
+	{
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+
+		p->die = cl.time + 5;
+		p->color = ramp1[0];
+		p->ramp = rand()&3;
+		if (i & 1)
+		{
+			p->type = pt_explode;
+			for (j=0 ; j<3 ; j++)
+			{
+				p->org[j] = org[j] + ((rand()%32)-16);
+				p->vel[j] = (rand()%512)-256;
+			}
+		}
+		else
+		{
+			p->type = pt_explode2;
+			for (j=0 ; j<3 ; j++)
+			{
+				p->org[j] = org[j] + ((rand()%32)-16);
+				p->vel[j] = (rand()%512)-256;
+			}
+		}
+	}
+}
+
+/*
+===============
+R_ParticleExplosion2
+
+===============
+*/
+void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
+{
+	int			i, j;
+	particle_t	*p;
+	int			colorMod = 0;
+
+	for (i=0; i<512; i++)
+	{
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+
+		p->die = cl.time + 0.3;
+		p->color = colorStart + (colorMod % colorLength);
+		colorMod++;
+
+		p->type = pt_blob;
+		for (j=0 ; j<3 ; j++)
+		{
+			p->org[j] = org[j] + ((rand()%32)-16);
+			p->vel[j] = (rand()%512)-256;
+		}
+	}
+}
+
+/*
+===============
+R_BlobExplosion
+
+===============
+*/
+void R_BlobExplosion (vec3_t org)
+{
+	int			i, j;
+	particle_t	*p;
+	
+	for (i=0 ; i<1024 ; i++)
+	{
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+
+		p->die = cl.time + 1 + (rand()&8)*0.05;
+
+		if (i & 1)
+		{
+			p->type = pt_blob;
+			p->color = 66 + rand()%6;
+			for (j=0 ; j<3 ; j++)
+			{
+				p->org[j] = org[j] + ((rand()%32)-16);
+				p->vel[j] = (rand()%512)-256;
+			}
+		}
+		else
+		{
+			p->type = pt_blob2;
+			p->color = 150 + rand()%6;
+			for (j=0 ; j<3 ; j++)
+			{
+				p->org[j] = org[j] + ((rand()%32)-16);
+				p->vel[j] = (rand()%512)-256;
+			}
+		}
+	}
+}
+
+/*
+===============
+R_RunParticleEffect
+
+===============
+*/
+void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
+{
+	int			i, j;
+	particle_t	*p;
+	
+	for (i=0 ; i<count ; i++)
+	{
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+
+		if (count == 1024)
+		{	// rocket explosion
+			p->die = cl.time + 5;
+			p->color = ramp1[0];
+			p->ramp = rand()&3;
+			if (i & 1)
+			{
+				p->type = pt_explode;
+				for (j=0 ; j<3 ; j++)
+				{
+					p->org[j] = org[j] + ((rand()%32)-16);
+					p->vel[j] = (rand()%512)-256;
+				}
+			}
+			else
+			{
+				p->type = pt_explode2;
+				for (j=0 ; j<3 ; j++)
+				{
+					p->org[j] = org[j] + ((rand()%32)-16);
+					p->vel[j] = (rand()%512)-256;
+				}
+			}
+		}
+		else
+		{
+			p->die = cl.time + 0.1*(rand()%5);
+			p->color = (color&~7) + (rand()&7);
+			p->type = pt_slowgrav;
+			for (j=0 ; j<3 ; j++)
+			{
+				p->org[j] = org[j] + ((rand()&15)-8);
+				p->vel[j] = dir[j]*15;// + (rand()%300)-150;
+			}
+		}
+	}
+}
+
+
+/*
+===============
+R_LavaSplash
+
+===============
+*/
+void R_LavaSplash (vec3_t org)
+{
+	int			i, j, k;
+	particle_t	*p;
+	float		vel;
+	vec3_t		dir;
+
+	for (i=-16 ; i<16 ; i++)
+		for (j=-16 ; j<16 ; j++)
+			for (k=0 ; k<1 ; k++)
+			{
+				if (!free_particles)
+					return;
+				p = free_particles;
+				free_particles = p->next;
+				p->next = active_particles;
+				active_particles = p;
+		
+				p->die = cl.time + 2 + (rand()&31) * 0.02;
+				p->color = 224 + (rand()&7);
+				p->type = pt_slowgrav;
+				
+				dir[0] = j*8 + (rand()&7);
+				dir[1] = i*8 + (rand()&7);
+				dir[2] = 256;
+	
+				p->org[0] = org[0] + dir[0];
+				p->org[1] = org[1] + dir[1];
+				p->org[2] = org[2] + (rand()&63);
+	
+				VectorNormalizeNoRet (dir);						
+				vel = 50 + (rand()&63);
+				VectorScale (dir, vel, p->vel);
+			}
+}
+
+/*
+===============
+R_TeleportSplash
+
+===============
+*/
+void R_TeleportSplash (vec3_t org)
+{
+	int			i, j, k;
+	particle_t	*p;
+	float		vel;
+	vec3_t		dir;
+
+	for (i=-16 ; i<16 ; i+=4)
+		for (j=-16 ; j<16 ; j+=4)
+			for (k=-24 ; k<32 ; k+=4)
+			{
+				if (!free_particles)
+					return;
+				p = free_particles;
+				free_particles = p->next;
+				p->next = active_particles;
+				active_particles = p;
+		
+				p->die = cl.time + 0.2 + (rand()&7) * 0.02;
+				p->color = 7 + (rand()&7);
+				p->type = pt_slowgrav;
+				
+				dir[0] = j*8;
+				dir[1] = i*8;
+				dir[2] = k*8;
+	
+				p->org[0] = org[0] + i + (rand()&3);
+				p->org[1] = org[1] + j + (rand()&3);
+				p->org[2] = org[2] + k + (rand()&3);
+	
+				VectorNormalizeNoRet (dir);						
+				vel = 50 + (rand()&63);
+				VectorScale (dir, vel, p->vel);
+			}
+}
+
+void R_RocketTrail (vec3_t start, vec3_t end, int type)
+{
+	vec3_t		vec;
+	float		len;
+	int			j;
+	particle_t	*p;
+	int			dec;
+	static int	tracercount;
+
+	VectorSubtract (end, start, vec);
+	len = VectorNormalize (vec);
+	if (type < 128)
+		dec = 3;
+	else
+	{
+		dec = 1;
+		type -= 128;
+	}
+
+	while (len > 0)
+	{
+		len -= dec;
+
+		if (!free_particles)
+			return;
+		p = free_particles;
+		free_particles = p->next;
+		p->next = active_particles;
+		active_particles = p;
+		
+		VectorCopy (vec3_origin, p->vel);
+		p->die = cl.time + 2;
+
+		switch (type)
+		{
+			case 0:	// rocket trail
+				p->ramp = (rand()&3);
+				p->color = ramp3[(int)p->ramp];
+				p->type = pt_fire;
+				for (j=0 ; j<3 ; j++)
+					p->org[j] = start[j] + ((rand()%6)-3);
+				break;
+
+			case 1:	// smoke smoke
+				p->ramp = (rand()&3) + 2;
+				p->color = ramp3[(int)p->ramp];
+				p->type = pt_fire;
+				for (j=0 ; j<3 ; j++)
+					p->org[j] = start[j] + ((rand()%6)-3);
+				break;
+
+			case 2:	// blood
+				p->type = pt_grav;
+				p->color = 67 + (rand()&3);
+				for (j=0 ; j<3 ; j++)
+					p->org[j] = start[j] + ((rand()%6)-3);
+				break;
+
+			case 3:
+			case 5:	// tracer
+				p->die = cl.time + 0.5;
+				p->type = pt_static;
+				if (type == 3)
+					p->color = 52 + ((tracercount&4)<<1);
+				else
+					p->color = 230 + ((tracercount&4)<<1);
+			
+				tracercount++;
+
+				VectorCopy (start, p->org);
+				if (tracercount & 1)
+				{
+					p->vel[0] = 30*vec[1];
+					p->vel[1] = 30*-vec[0];
+				}
+				else
+				{
+					p->vel[0] = 30*-vec[1];
+					p->vel[1] = 30*vec[0];
+				}
+				break;
+
+			case 4:	// slight blood
+				p->type = pt_grav;
+				p->color = 67 + (rand()&3);
+				for (j=0 ; j<3 ; j++)
+					p->org[j] = start[j] + ((rand()%6)-3);
+				len -= 3;
+				break;
+
+			case 6:	// voor trail
+				p->color = 9*16 + 8 + (rand()&3);
+				p->type = pt_static;
+				p->die = cl.time + 0.3;
+				for (j=0 ; j<3 ; j++)
+					p->org[j] = start[j] + ((rand()&15)-8);
+				break;
+		}
+		
+
+		VectorAdd (start, vec, start);
+	}
+}
+
+
+/*
+===============
+R_DrawParticles
+===============
+*/
+extern	cvar_t	sv_gravity;
+
+void R_DrawParticles (void)
+{
+	particle_t		*p, *kill;
+	float			grav;
+	int				i;
+	float			time2, time3;
+	float			time1;
+	float			dvel;
+	float			frametime;
+	
+#ifdef GLQUAKE
+	vec3_t			up, right;
+	float			scale;
+
+    GL_Bind(particletexture);
+	glEnable (GL_BLEND);
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glBegin (GL_TRIANGLES);
+
+	VectorScale (vup, 1.5, up);
+	VectorScale (vright, 1.5, right);
+#else
+	D_StartParticles ();
+
+	VectorScale (vright, xscaleshrink, r_pright);
+	VectorScale (vup, yscaleshrink, r_pup);
+	VectorCopy (vpn, r_ppn);
+#endif
+	frametime = cl.time - cl.oldtime;
+	time3 = frametime * 15;
+	time2 = frametime * 10; // 15;
+	time1 = frametime * 5;
+	grav = frametime * sv_gravity.value * 0.05;
+	dvel = 4*frametime;
+	
+	for ( ;; ) 
+	{
+		kill = active_particles;
+		if (kill && kill->die < cl.time)
+		{
+			active_particles = kill->next;
+			kill->next = free_particles;
+			free_particles = kill;
+			continue;
+		}
+		break;
+	}
+
+	for (p=active_particles ; p ; p=p->next)
+	{
+		for ( ;; )
+		{
+			kill = p->next;
+			if (kill && kill->die < cl.time)
+			{
+				p->next = kill->next;
+				kill->next = free_particles;
+				free_particles = kill;
+				continue;
+			}
+			break;
+		}
+
+#ifdef GLQUAKE
+		// hack a scale up to keep particles from disapearing
+		scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]
+			+ (p->org[2] - r_origin[2])*vpn[2];
+		if (scale < 20)
+			scale = 1;
+		else
+			scale = 1 + scale * 0.004;
+		glColor3ubv ((byte *)&d_8to24table[(int)p->color]);
+		glTexCoord2f (0,0);
+		glVertex3fv (p->org);
+		glTexCoord2f (1,0);
+		glVertex3f (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale);
+		glTexCoord2f (0,1);
+		glVertex3f (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale);
+#else
+		D_DrawParticle (p);
+#endif
+		p->org[0] += p->vel[0]*frametime;
+		p->org[1] += p->vel[1]*frametime;
+		p->org[2] += p->vel[2]*frametime;
+		
+		switch (p->type)
+		{
+		case pt_static:
+			break;
+		case pt_fire:
+			p->ramp += time1;
+			if (p->ramp >= 6)
+				p->die = -1;
+			else
+				p->color = ramp3[(int)p->ramp];
+			p->vel[2] += grav;
+			break;
+
+		case pt_explode:
+			p->ramp += time2;
+			if (p->ramp >=8)
+				p->die = -1;
+			else
+				p->color = ramp1[(int)p->ramp];
+			for (i=0 ; i<3 ; i++)
+				p->vel[i] += p->vel[i]*dvel;
+			p->vel[2] -= grav;
+			break;
+
+		case pt_explode2:
+			p->ramp += time3;
+			if (p->ramp >=8)
+				p->die = -1;
+			else
+				p->color = ramp2[(int)p->ramp];
+			for (i=0 ; i<3 ; i++)
+				p->vel[i] -= p->vel[i]*frametime;
+			p->vel[2] -= grav;
+			break;
+
+		case pt_blob:
+			for (i=0 ; i<3 ; i++)
+				p->vel[i] += p->vel[i]*dvel;
+			p->vel[2] -= grav;
+			break;
+
+		case pt_blob2:
+			for (i=0 ; i<2 ; i++)
+				p->vel[i] -= p->vel[i]*dvel;
+			p->vel[2] -= grav;
+			break;
+
+		case pt_grav:
+#ifdef QUAKE2
+			p->vel[2] -= grav * 20;
+			break;
+#endif
+		case pt_slowgrav:
+			p->vel[2] -= grav;
+			break;
+		}
+	}
+
+#ifdef GLQUAKE
+	glEnd ();
+	glDisable (GL_BLEND);
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+#else
+	D_EndParticles ();
+#endif
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_shared.h b/apps/plugins/sdl/progs/quake/r_shared.h
new file mode 100644
index 0000000..f2a6db3
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_shared.h
@@ -0,0 +1,157 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 GLQUAKE
+// r_shared.h: general refresh-related stuff shared between the refresh and the
+// driver
+
+// FIXME: clean up and move into d_iface.h
+
+#ifndef _R_SHARED_H_
+#define _R_SHARED_H_
+
+#define	MAXVERTS	16					// max points in a surface polygon
+#define MAXWORKINGVERTS	(MAXVERTS+4)	// max points in an intermediate
+										//  polygon (while processing)
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define	MAXHEIGHT		1024
+#define	MAXWIDTH		1280
+#define MAXDIMENSION	((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH)
+
+#define SIN_BUFFER_SIZE	(MAXDIMENSION+CYCLE)
+
+#define INFINITE_DISTANCE	0x10000		// distance that's always guaranteed to
+										//  be farther away than anything in
+										//  the scene
+
+//===================================================================
+
+extern void	R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1);
+
+extern int		cachewidth;
+extern pixel_t	*cacheblock;
+extern int		screenwidth;
+
+extern	float	pixelAspect;
+
+extern int		r_drawnpolycount;
+
+extern cvar_t	r_clearcolor;
+
+extern int	sintable[SIN_BUFFER_SIZE];
+extern int	intsintable[SIN_BUFFER_SIZE];
+
+extern	vec3_t	vup, base_vup;
+extern	vec3_t	vpn, base_vpn;
+extern	vec3_t	vright, base_vright;
+extern	entity_t		*currententity;
+
+#define NUMSTACKEDGES		2400
+#define	MINEDGES			NUMSTACKEDGES
+#define NUMSTACKSURFACES	800
+#define MINSURFACES			NUMSTACKSURFACES
+#define	MAXSPANS			3000
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct espan_s
+{
+	int				u, v, count;
+	struct espan_s	*pnext;
+} espan_t;
+
+// FIXME: compress, make a union if that will help
+// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte
+typedef struct surf_s
+{
+	struct surf_s	*next;			// active surface stack in r_edge.c
+	struct surf_s	*prev;			// used in r_edge.c for active surf stack
+	struct espan_s	*spans;			// pointer to linked list of spans to draw
+	int			key;				// sorting key (BSP order)
+	int			last_u;				// set during tracing
+	int			spanstate;			// 0 = not in span
+									// 1 = in span
+									// -1 = in inverted span (end before
+									//  start)
+	int			flags;				// currentface flags
+	void		*data;				// associated data like msurface_t
+	entity_t	*entity;
+	float		nearzi;				// nearest 1/z on surface, for mipmapping
+	qboolean	insubmodel;
+	float		d_ziorigin, d_zistepu, d_zistepv;
+
+	int			pad[2];				// to 64 bytes
+} surf_t;
+
+extern	surf_t	*surfaces, *surface_p, *surf_max;
+
+// surfaces are generated in back to front order by the bsp, so if a surf
+// pointer is greater than another one, it should be drawn in front
+// surfaces[1] is the background, and is used as the active surface stack.
+// surfaces[0] is a dummy, because index 0 is used to indicate no surface
+//  attached to an edge_t
+
+//===================================================================
+
+extern vec3_t	sxformaxis[4];	// s axis transformed into viewspace
+extern vec3_t	txformaxis[4];	// t axis transformed into viewspac
+
+extern vec3_t	modelorg, base_modelorg;
+
+extern	float	xcenter, ycenter;
+extern	float	xscale, yscale;
+extern	float	xscaleinv, yscaleinv;
+extern	float	xscaleshrink, yscaleshrink;
+
+extern	int d_lightstylevalue[256]; // 8.8 frac of base light value
+
+extern void TransformVector (vec3_t in, vec3_t out);
+extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv,
+	fixed8_t endvertu, fixed8_t endvertv);
+
+extern int	r_skymade;
+extern void R_MakeSky (void);
+
+extern int	ubasestep, errorterm, erroradjustup, erroradjustdown;
+
+// flags in finalvert_t.flags
+#define ALIAS_LEFT_CLIP				0x0001
+#define ALIAS_TOP_CLIP				0x0002
+#define ALIAS_RIGHT_CLIP			0x0004
+#define ALIAS_BOTTOM_CLIP			0x0008
+#define ALIAS_Z_CLIP				0x0010
+// !!! if this is changed, it must be changed in d_ifacea.h too !!!
+#define ALIAS_ONSEAM				0x0020	// also defined in modelgen.h;
+											//  must be kept in sync
+#define ALIAS_XY_CLIP_MASK			0x000F
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct edge_s
+{
+	fixed16_t		u;
+	fixed16_t		u_step;
+	struct edge_s	*prev, *next;
+	unsigned short	surfs[2];
+	struct edge_s	*nextremove;
+	float			nearzi;
+	medge_t			*owner;
+} edge_t;
+
+#endif	// _R_SHARED_H_
+
+#endif	// GLQUAKE
diff --git a/apps/plugins/sdl/progs/quake/r_sky.c b/apps/plugins/sdl/progs/quake/r_sky.c
new file mode 100644
index 0000000..c8bfffd
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_sky.c
@@ -0,0 +1,280 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_sky.c
+
+#include "quakedef.h"
+#include "r_local.h"
+#include "d_local.h"
+
+
+int		iskyspeed = 8;
+int		iskyspeed2 = 2;
+float	skyspeed, skyspeed2;
+
+float		skytime;
+
+byte		*r_skysource;
+
+int r_skymade;
+int r_skydirect;		// not used?
+
+
+// TODO: clean up these routines
+
+byte	bottomsky[128*131];
+byte	bottommask[128*131];
+byte	newsky[128*256];	// newsky and topsky both pack in here, 128 bytes
+							//  of newsky on the left of each scan, 128 bytes
+							//  of topsky on the right, because the low-level
+							//  drawers need 256-byte scan widths
+
+
+/*
+=============
+R_InitSky
+
+A sky texture is 256*128, with the right side being a masked overlay
+==============
+*/
+void R_InitSky (texture_t *mt)
+{
+	int			i, j;
+	byte		*src;
+
+	src = (byte *)mt + mt->offsets[0];
+
+	for (i=0 ; i<128 ; i++)
+	{
+		for (j=0 ; j<128 ; j++)
+		{
+			newsky[(i*256) + j + 128] = src[i*256 + j + 128];
+		}
+	}
+
+	for (i=0 ; i<128 ; i++)
+	{
+		for (j=0 ; j<131 ; j++)
+		{
+			if (src[i*256 + (j & 0x7F)])
+			{
+				bottomsky[(i*131) + j] = src[i*256 + (j & 0x7F)];
+				bottommask[(i*131) + j] = 0;
+			}
+			else
+			{
+				bottomsky[(i*131) + j] = 0;
+				bottommask[(i*131) + j] = 0xff;
+			}
+		}
+	}
+	
+	r_skysource = newsky;
+}
+
+
+/*
+=================
+R_MakeSky
+=================
+*/
+void R_MakeSky (void)
+{
+	int			x, y;
+	int			ofs, baseofs;
+	int			xshift, yshift;
+	unsigned	*pnewsky;
+	static int	xlast = -1, ylast = -1;
+
+	xshift = skytime*skyspeed;
+	yshift = skytime*skyspeed;
+
+	if ((xshift == xlast) && (yshift == ylast))
+		return;
+
+	xlast = xshift;
+	ylast = yshift;
+	
+	pnewsky = (unsigned *)&newsky[0];
+
+	for (y=0 ; y<SKYSIZE ; y++)
+	{
+		baseofs = ((y+yshift) & SKYMASK) * 131;
+
+// FIXME: clean this up
+#if UNALIGNED_OK
+
+		for (x=0 ; x<SKYSIZE ; x += 4)
+		{
+			ofs = baseofs + ((x+xshift) & SKYMASK);
+
+		// PORT: unaligned dword access to bottommask and bottomsky
+
+			*pnewsky = (*(pnewsky + (128 / sizeof (unsigned))) &
+						*(unsigned *)&bottommask[ofs]) |
+						*(unsigned *)&bottomsky[ofs];
+			pnewsky++;
+		}
+
+#else
+
+		for (x=0 ; x<SKYSIZE ; x++)
+		{
+			ofs = baseofs + ((x+xshift) & SKYMASK);
+
+			*(byte *)pnewsky = (*((byte *)pnewsky + 128) &
+						*(byte *)&bottommask[ofs]) |
+						*(byte *)&bottomsky[ofs];
+			pnewsky = (unsigned *)((byte *)pnewsky + 1);
+		}
+
+#endif
+
+		pnewsky += 128 / sizeof (unsigned);
+	}
+
+	r_skymade = 1;
+}
+
+
+/*
+=================
+R_GenSkyTile
+=================
+*/
+void R_GenSkyTile (void *pdest)
+{
+	int			x, y;
+	int			ofs, baseofs;
+	int			xshift, yshift;
+	unsigned	*pnewsky;
+	unsigned	*pd;
+
+	xshift = skytime*skyspeed;
+	yshift = skytime*skyspeed;
+
+	pnewsky = (unsigned *)&newsky[0];
+	pd = (unsigned *)pdest;
+
+	for (y=0 ; y<SKYSIZE ; y++)
+	{
+		baseofs = ((y+yshift) & SKYMASK) * 131;
+
+// FIXME: clean this up
+#if UNALIGNED_OK
+
+		for (x=0 ; x<SKYSIZE ; x += 4)
+		{
+			ofs = baseofs + ((x+xshift) & SKYMASK);
+
+		// PORT: unaligned dword access to bottommask and bottomsky
+
+			*pd = (*(pnewsky + (128 / sizeof (unsigned))) &
+				   *(unsigned *)&bottommask[ofs]) |
+				   *(unsigned *)&bottomsky[ofs];
+			pnewsky++;
+			pd++;
+		}
+
+#else
+
+		for (x=0 ; x<SKYSIZE ; x++)
+		{
+			ofs = baseofs + ((x+xshift) & SKYMASK);
+
+			*(byte *)pd = (*((byte *)pnewsky + 128) &
+						*(byte *)&bottommask[ofs]) |
+						*(byte *)&bottomsky[ofs];
+			pnewsky = (unsigned *)((byte *)pnewsky + 1);
+			pd = (unsigned *)((byte *)pd + 1);
+		}
+
+#endif
+
+		pnewsky += 128 / sizeof (unsigned);
+	}
+}
+
+
+/*
+=================
+R_GenSkyTile16
+=================
+*/
+void R_GenSkyTile16 (void *pdest)
+{
+	int				x, y;
+	int				ofs, baseofs;
+	int				xshift, yshift;
+	byte			*pnewsky;
+	unsigned short	*pd;
+
+	xshift = skytime * skyspeed;
+	yshift = skytime * skyspeed;
+
+	pnewsky = (byte *)&newsky[0];
+	pd = (unsigned short *)pdest;
+
+	for (y=0 ; y<SKYSIZE ; y++)
+	{
+		baseofs = ((y+yshift) & SKYMASK) * 131;
+
+// FIXME: clean this up
+// FIXME: do faster unaligned version?
+		for (x=0 ; x<SKYSIZE ; x++)
+		{
+			ofs = baseofs + ((x+xshift) & SKYMASK);
+
+			*pd = d_8to16table[(*(pnewsky + 128) &
+					*(byte *)&bottommask[ofs]) |
+					*(byte *)&bottomsky[ofs]];
+			pnewsky++;
+			pd++;
+		}
+
+		pnewsky += TILE_SIZE;
+	}
+}
+
+
+/*
+=============
+R_SetSkyFrame
+==============
+*/
+void R_SetSkyFrame (void)
+{
+	int		g, s1, s2;
+	float	temp;
+
+	skyspeed = iskyspeed;
+	skyspeed2 = iskyspeed2;
+
+	g = GreatestCommonDivisor (iskyspeed, iskyspeed2);
+	s1 = iskyspeed / g;
+	s2 = iskyspeed2 / g;
+	temp = SKYSIZE * s1 * s2;
+
+	skytime = cl.time - ((int)(cl.time / temp) * temp);
+	
+
+	r_skymade = 0;
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/r_sprite.c b/apps/plugins/sdl/progs/quake/r_sprite.c
new file mode 100644
index 0000000..4ab8e0e
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_sprite.c
@@ -0,0 +1,401 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_sprite.c
+
+#include "quakedef.h"
+#include "r_local.h"
+
+static int				clip_current;
+static vec5_t			clip_verts[2][MAXWORKINGVERTS];
+static int				sprite_width, sprite_height;
+
+spritedesc_t			r_spritedesc;
+	
+
+/*
+================
+R_RotateSprite
+================
+*/
+void R_RotateSprite (float beamlength)
+{
+	vec3_t	vec;
+	
+	if (beamlength == 0.0)
+		return;
+
+	VectorScale (r_spritedesc.vpn, -beamlength, vec);
+	VectorAdd (r_entorigin, vec, r_entorigin);
+	VectorSubtract (modelorg, vec, modelorg);
+}
+
+
+/*
+=============
+R_ClipSpriteFace
+
+Clips the winding at clip_verts[clip_current] and changes clip_current
+Throws out the back side
+==============
+*/
+int R_ClipSpriteFace (int nump, clipplane_t *pclipplane)
+{
+	int		i, outcount;
+	float	dists[MAXWORKINGVERTS+1];
+	float	frac, clipdist, *pclipnormal;
+	float	*in, *instep, *outstep, *vert2;
+
+	clipdist = pclipplane->dist;
+	pclipnormal = pclipplane->normal;
+	
+// calc dists
+	if (clip_current)
+	{
+		in = clip_verts[1][0];
+		outstep = clip_verts[0][0];
+		clip_current = 0;
+	}
+	else
+	{
+		in = clip_verts[0][0];
+		outstep = clip_verts[1][0];
+		clip_current = 1;
+	}
+	
+	instep = in;
+	for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
+	{
+		dists[i] = DotProduct (instep, pclipnormal) - clipdist;
+	}
+	
+// handle wraparound case
+	dists[nump] = dists[0];
+	Q_memcpy (instep, in, sizeof (vec5_t));
+
+
+// clip the winding
+	instep = in;
+	outcount = 0;
+
+	for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
+	{
+		if (dists[i] >= 0)
+		{
+			Q_memcpy (outstep, instep, sizeof (vec5_t));
+			outstep += sizeof (vec5_t) / sizeof (float);
+			outcount++;
+		}
+
+		if (dists[i] == 0 || dists[i+1] == 0)
+			continue;
+
+		if ( (dists[i] > 0) == (dists[i+1] > 0) )
+			continue;
+			
+	// split it into a new vertex
+		frac = dists[i] / (dists[i] - dists[i+1]);
+			
+		vert2 = instep + sizeof (vec5_t) / sizeof (float);
+		
+		outstep[0] = instep[0] + frac*(vert2[0] - instep[0]);
+		outstep[1] = instep[1] + frac*(vert2[1] - instep[1]);
+		outstep[2] = instep[2] + frac*(vert2[2] - instep[2]);
+		outstep[3] = instep[3] + frac*(vert2[3] - instep[3]);
+		outstep[4] = instep[4] + frac*(vert2[4] - instep[4]);
+
+		outstep += sizeof (vec5_t) / sizeof (float);
+		outcount++;
+	}	
+	
+	return outcount;
+}
+
+
+/*
+================
+R_SetupAndDrawSprite
+================
+*/
+void R_SetupAndDrawSprite ()
+{
+	int			i, nump;
+	float		dot, scale, *pv;
+	vec5_t		*pverts;
+	vec3_t		left, up, right, down, transformed, local;
+	emitpoint_t	outverts[MAXWORKINGVERTS+1], *pout;
+
+	dot = DotProduct (r_spritedesc.vpn, modelorg);
+
+// backface cull
+	if (dot >= 0)
+		return;
+
+// build the sprite poster in worldspace
+	VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
+	VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
+	VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
+	VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
+
+	pverts = clip_verts[0];
+
+	pverts[0][0] = r_entorigin[0] + up[0] + left[0];
+	pverts[0][1] = r_entorigin[1] + up[1] + left[1];
+	pverts[0][2] = r_entorigin[2] + up[2] + left[2];
+	pverts[0][3] = 0;
+	pverts[0][4] = 0;
+
+	pverts[1][0] = r_entorigin[0] + up[0] + right[0];
+	pverts[1][1] = r_entorigin[1] + up[1] + right[1];
+	pverts[1][2] = r_entorigin[2] + up[2] + right[2];
+	pverts[1][3] = sprite_width;
+	pverts[1][4] = 0;
+
+	pverts[2][0] = r_entorigin[0] + down[0] + right[0];
+	pverts[2][1] = r_entorigin[1] + down[1] + right[1];
+	pverts[2][2] = r_entorigin[2] + down[2] + right[2];
+	pverts[2][3] = sprite_width;
+	pverts[2][4] = sprite_height;
+
+	pverts[3][0] = r_entorigin[0] + down[0] + left[0];
+	pverts[3][1] = r_entorigin[1] + down[1] + left[1];
+	pverts[3][2] = r_entorigin[2] + down[2] + left[2];
+	pverts[3][3] = 0;
+	pverts[3][4] = sprite_height;
+
+// clip to the frustum in worldspace
+	nump = 4;
+	clip_current = 0;
+
+	for (i=0 ; i<4 ; i++)
+	{
+		nump = R_ClipSpriteFace (nump, &view_clipplanes[i]);
+		if (nump < 3)
+			return;
+		if (nump >= MAXWORKINGVERTS)
+			Sys_Error("R_SetupAndDrawSprite: too many points");
+	}
+
+// transform vertices into viewspace and project
+	pv = &clip_verts[clip_current][0][0];
+	r_spritedesc.nearzi = -999999;
+
+	for (i=0 ; i<nump ; i++)
+	{
+		VectorSubtract (pv, r_origin, local);
+		TransformVector (local, transformed);
+
+		if (transformed[2] < NEAR_CLIP)
+			transformed[2] = NEAR_CLIP;
+
+		pout = &outverts[i];
+		pout->zi = 1.0 / transformed[2];
+		if (pout->zi > r_spritedesc.nearzi)
+			r_spritedesc.nearzi = pout->zi;
+
+		pout->s = pv[3];
+		pout->t = pv[4];
+		
+		scale = xscale * pout->zi;
+		pout->u = (xcenter + scale * transformed[0]);
+
+		scale = yscale * pout->zi;
+		pout->v = (ycenter - scale * transformed[1]);
+
+		pv += sizeof (vec5_t) / sizeof (*pv);
+	}
+
+// draw it
+	r_spritedesc.nump = nump;
+	r_spritedesc.pverts = outverts;
+	D_DrawSprite ();
+}
+
+
+/*
+================
+R_GetSpriteframe
+================
+*/
+mspriteframe_t *R_GetSpriteframe (msprite_t *psprite)
+{
+	mspritegroup_t	*pspritegroup;
+	mspriteframe_t	*pspriteframe;
+	int				i, numframes, frame;
+	float			*pintervals, fullinterval, targettime, time;
+
+	frame = currententity->frame;
+
+	if ((frame >= psprite->numframes) || (frame < 0))
+	{
+		Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
+		frame = 0;
+	}
+
+	if (psprite->frames[frame].type == SPR_SINGLE)
+	{
+		pspriteframe = psprite->frames[frame].frameptr;
+	}
+	else
+	{
+		pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
+		pintervals = pspritegroup->intervals;
+		numframes = pspritegroup->numframes;
+		fullinterval = pintervals[numframes-1];
+
+		time = cl.time + currententity->syncbase;
+
+	// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
+	// are positive, so we don't have to worry about division by 0
+		targettime = time - ((int)(time / fullinterval)) * fullinterval;
+
+		for (i=0 ; i<(numframes-1) ; i++)
+		{
+			if (pintervals[i] > targettime)
+				break;
+		}
+
+		pspriteframe = pspritegroup->frames[i];
+	}
+
+	return pspriteframe;
+}
+
+
+/*
+================
+R_DrawSprite
+================
+*/
+void R_DrawSprite (void)
+{
+	int				i;
+	msprite_t		*psprite;
+	vec3_t			tvec;
+	float			dot, angle, sr, cr;
+
+	psprite = currententity->model->cache.data;
+
+	r_spritedesc.pspriteframe = R_GetSpriteframe (psprite);
+
+	sprite_width = r_spritedesc.pspriteframe->width;
+	sprite_height = r_spritedesc.pspriteframe->height;
+
+// TODO: make this caller-selectable
+	if (psprite->type == SPR_FACING_UPRIGHT)
+	{
+	// generate the sprite's axes, with vup straight up in worldspace, and
+	// r_spritedesc.vright perpendicular to modelorg.
+	// This will not work if the view direction is very close to straight up or
+	// down, because the cross product will be between two nearly parallel
+	// vectors and starts to approach an undefined state, so we don't draw if
+	// the two vectors are less than 1 degree apart
+		tvec[0] = -modelorg[0];
+		tvec[1] = -modelorg[1];
+		tvec[2] = -modelorg[2];
+		VectorNormalizeNoRet (tvec);
+		dot = tvec[2];	// same as DotProduct (tvec, r_spritedesc.vup) because
+						//  r_spritedesc.vup is 0, 0, 1
+		if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
+			return;
+		r_spritedesc.vup[0] = 0;
+		r_spritedesc.vup[1] = 0;
+		r_spritedesc.vup[2] = 1;
+		r_spritedesc.vright[0] = tvec[1];
+								// CrossProduct(r_spritedesc.vup, -modelorg,
+		r_spritedesc.vright[1] = -tvec[0];
+								//              r_spritedesc.vright)
+		r_spritedesc.vright[2] = 0;
+		VectorNormalizeNoRet (r_spritedesc.vright);
+		r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
+		r_spritedesc.vpn[1] = r_spritedesc.vright[0];
+		r_spritedesc.vpn[2] = 0;
+					// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
+					//  r_spritedesc.vpn)
+	}
+	else if (psprite->type == SPR_VP_PARALLEL)
+	{
+	// generate the sprite's axes, completely parallel to the viewplane. There
+	// are no problem situations, because the sprite is always in the same
+	// position relative to the viewer
+		for (i=0 ; i<3 ; i++)
+		{
+			r_spritedesc.vup[i] = vup[i];
+			r_spritedesc.vright[i] = vright[i];
+			r_spritedesc.vpn[i] = vpn[i];
+		}
+	}
+	else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
+	{
+	// generate the sprite's axes, with vup straight up in worldspace, and
+	// r_spritedesc.vright parallel to the viewplane.
+	// This will not work if the view direction is very close to straight up or
+	// down, because the cross product will be between two nearly parallel
+	// vectors and starts to approach an undefined state, so we don't draw if
+	// the two vectors are less than 1 degree apart
+		dot = vpn[2];	// same as DotProduct (vpn, r_spritedesc.vup) because
+						//  r_spritedesc.vup is 0, 0, 1
+		if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
+			return;
+		r_spritedesc.vup[0] = 0;
+		r_spritedesc.vup[1] = 0;
+		r_spritedesc.vup[2] = 1;
+		r_spritedesc.vright[0] = vpn[1];
+										// CrossProduct (r_spritedesc.vup, vpn,
+		r_spritedesc.vright[1] = -vpn[0];	//  r_spritedesc.vright)
+		r_spritedesc.vright[2] = 0;
+		VectorNormalizeNoRet (r_spritedesc.vright);
+		r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
+		r_spritedesc.vpn[1] = r_spritedesc.vright[0];
+		r_spritedesc.vpn[2] = 0;
+					// CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
+					//  r_spritedesc.vpn)
+	}
+	else if (psprite->type == SPR_ORIENTED)
+	{
+	// generate the sprite's axes, according to the sprite's world orientation
+		AngleVectors (currententity->angles, r_spritedesc.vpn,
+					  r_spritedesc.vright, r_spritedesc.vup);
+	}
+	else if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
+	{
+	// generate the sprite's axes, parallel to the viewplane, but rotated in
+	// that plane around the center according to the sprite entity's roll
+	// angle. So vpn stays the same, but vright and vup rotate
+		angle = currententity->angles[ROLL] * (M_PI*2 / 360);
+		sr = sin(angle);
+		cr = cos(angle);
+
+		for (i=0 ; i<3 ; i++)
+		{
+			r_spritedesc.vpn[i] = vpn[i];
+			r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
+			r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
+		}
+	}
+	else
+	{
+		Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
+	}
+
+	R_RotateSprite (psprite->beamlength);
+
+	R_SetupAndDrawSprite ();
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_surf.c b/apps/plugins/sdl/progs/quake/r_surf.c
new file mode 100644
index 0000000..a2436d5
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_surf.c
@@ -0,0 +1,678 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_surf.c: surface-related refresh code
+
+#include "quakedef.h"
+#include "r_local.h"
+
+drawsurf_t	r_drawsurf;
+
+int				lightleft, sourcesstep, blocksize, sourcetstep;
+int				lightdelta, lightdeltastep;
+int				lightright, lightleftstep, lightrightstep, blockdivshift;
+unsigned		blockdivmask;
+void			*prowdestbase;
+unsigned char	*pbasesource;
+int				surfrowbytes;	// used by ASM files
+unsigned		*r_lightptr;
+int				r_stepback;
+int				r_lightwidth;
+int				r_numhblocks, r_numvblocks;
+unsigned char	*r_source, *r_sourcemax;
+
+void R_DrawSurfaceBlock8_mip0 (void);
+void R_DrawSurfaceBlock8_mip1 (void);
+void R_DrawSurfaceBlock8_mip2 (void);
+void R_DrawSurfaceBlock8_mip3 (void);
+
+static void	(*surfmiptable[4])(void) = {
+	R_DrawSurfaceBlock8_mip0,
+	R_DrawSurfaceBlock8_mip1,
+	R_DrawSurfaceBlock8_mip2,
+	R_DrawSurfaceBlock8_mip3
+};
+
+
+
+unsigned		blocklights[18*18];
+
+/*
+===============
+R_AddDynamicLights
+===============
+*/
+void R_AddDynamicLights (void)
+{
+	msurface_t *surf;
+	int			lnum;
+	int			sd, td;
+	float		dist, rad, minlight;
+	vec3_t		impact, local;
+	int			s, t;
+	int			i;
+	int			smax, tmax;
+	mtexinfo_t	*tex;
+
+	surf = r_drawsurf.surf;
+	smax = (surf->extents[0]>>4)+1;
+	tmax = (surf->extents[1]>>4)+1;
+	tex = surf->texinfo;
+
+	for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
+	{
+		if ( !(surf->dlightbits & (1<<lnum) ) )
+			continue;		// not lit by this light
+
+		rad = cl_dlights[lnum].radius;
+		dist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) -
+				surf->plane->dist;
+		rad -= fabs(dist);
+		minlight = cl_dlights[lnum].minlight;
+		if (rad < minlight)
+			continue;
+		minlight = rad - minlight;
+
+		for (i=0 ; i<3 ; i++)
+		{
+			impact[i] = cl_dlights[lnum].origin[i] -
+					surf->plane->normal[i]*dist;
+		}
+
+		local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3];
+		local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3];
+
+		local[0] -= surf->texturemins[0];
+		local[1] -= surf->texturemins[1];
+		
+		for (t = 0 ; t<tmax ; t++)
+		{
+			td = local[1] - t*16;
+			if (td < 0)
+				td = -td;
+			for (s=0 ; s<smax ; s++)
+			{
+				sd = local[0] - s*16;
+				if (sd < 0)
+					sd = -sd;
+				if (sd > td)
+					dist = sd + (td>>1);
+				else
+					dist = td + (sd>>1);
+				if (dist < minlight)
+#ifdef QUAKE2
+				{
+					unsigned temp;
+					temp = (rad - dist)*256;
+					i = t*smax + s;
+					if (!cl_dlights[lnum].dark)
+						blocklights[i] += temp;
+					else
+					{
+						if (blocklights[i] > temp)
+							blocklights[i] -= temp;
+						else
+							blocklights[i] = 0;
+					}
+				}
+#else
+					blocklights[t*smax + s] += (rad - dist)*256;
+#endif
+			}
+		}
+	}
+}
+
+/*
+===============
+R_BuildLightMap
+
+Combine and scale multiple lightmaps into the 8.8 format in blocklights
+===============
+*/
+void R_BuildLightMap (void)
+{
+	int			smax, tmax;
+	int			t;
+	int			i, size;
+	byte		*lightmap;
+	unsigned	scale;
+	int			maps;
+	msurface_t	*surf;
+
+	surf = r_drawsurf.surf;
+
+	smax = (surf->extents[0]>>4)+1;
+	tmax = (surf->extents[1]>>4)+1;
+	size = smax*tmax;
+	lightmap = surf->samples;
+
+	if (r_fullbright.value || !cl.worldmodel->lightdata)
+	{
+		for (i=0 ; i<size ; i++)
+			blocklights[i] = 0;
+		return;
+	}
+
+// clear to ambient
+	for (i=0 ; i<size ; i++)
+		blocklights[i] = r_refdef.ambientlight<<8;
+
+
+// add all the lightmaps
+	if (lightmap)
+		for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
+			 maps++)
+		{
+			scale = r_drawsurf.lightadj[maps];	// 8.8 fraction		
+			for (i=0 ; i<size ; i++)
+				blocklights[i] += lightmap[i] * scale;
+			lightmap += size;	// skip to next lightmap
+		}
+
+// add all the dynamic lights
+	if (surf->dlightframe == r_framecount)
+		R_AddDynamicLights ();
+
+// bound, invert, and shift
+	for (i=0 ; i<size ; i++)
+	{
+		t = (255*256 - (int)blocklights[i]) >> (8 - VID_CBITS);
+
+		if (t < (1 << 6))
+			t = (1 << 6);
+
+		blocklights[i] = t;
+	}
+}
+
+
+/*
+===============
+R_TextureAnimation
+
+Returns the proper texture for a given time and base texture
+===============
+*/
+texture_t *R_TextureAnimation (texture_t *base)
+{
+	int		reletive;
+	int		count;
+
+	if (currententity->frame)
+	{
+		if (base->alternate_anims)
+			base = base->alternate_anims;
+	}
+	
+	if (!base->anim_total)
+		return base;
+
+	reletive = (int)(cl.time*10) % base->anim_total;
+
+	count = 0;	
+	while (base->anim_min > reletive || base->anim_max <= reletive)
+	{
+		base = base->anim_next;
+		if (!base)
+			Sys_Error ("R_TextureAnimation: broken cycle");
+		if (++count > 100)
+			Sys_Error ("R_TextureAnimation: infinite cycle");
+	}
+
+	return base;
+}
+
+
+/*
+===============
+R_DrawSurface
+===============
+*/
+void R_DrawSurface (void)
+{
+	unsigned char	*basetptr;
+	int				smax, tmax, twidth;
+	int				u;
+	int				soffset, basetoffset, texwidth;
+	int				horzblockstep;
+	unsigned char	*pcolumndest;
+	void			(*pblockdrawer)(void);
+	texture_t		*mt;
+
+// calculate the lightings
+	R_BuildLightMap ();
+	
+	surfrowbytes = r_drawsurf.rowbytes;
+
+	mt = r_drawsurf.texture;
+	
+	r_source = (byte *)mt + mt->offsets[r_drawsurf.surfmip];
+	
+// the fractional light values should range from 0 to (VID_GRADES - 1) << 16
+// from a source range of 0 - 255
+	
+	texwidth = mt->width >> r_drawsurf.surfmip;
+
+	blocksize = 16 >> r_drawsurf.surfmip;
+	blockdivshift = 4 - r_drawsurf.surfmip;
+	blockdivmask = (1 << blockdivshift) - 1;
+	
+	r_lightwidth = (r_drawsurf.surf->extents[0]>>4)+1;
+
+	r_numhblocks = r_drawsurf.surfwidth >> blockdivshift;
+	r_numvblocks = r_drawsurf.surfheight >> blockdivshift;
+
+//==============================
+
+	if (r_pixbytes == 1)
+	{
+		pblockdrawer = surfmiptable[r_drawsurf.surfmip];
+	// TODO: only needs to be set when there is a display settings change
+		horzblockstep = blocksize;
+	}
+	else
+	{
+		pblockdrawer = R_DrawSurfaceBlock16;
+	// TODO: only needs to be set when there is a display settings change
+		horzblockstep = blocksize << 1;
+	}
+
+	smax = mt->width >> r_drawsurf.surfmip;
+	twidth = texwidth;
+	tmax = mt->height >> r_drawsurf.surfmip;
+	sourcetstep = texwidth;
+	r_stepback = tmax * twidth;
+
+	r_sourcemax = r_source + (tmax * smax);
+
+	soffset = r_drawsurf.surf->texturemins[0];
+	basetoffset = r_drawsurf.surf->texturemins[1];
+
+// << 16 components are to guarantee positive values for %
+	soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax;
+	basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) 
+		+ (tmax << 16)) % tmax) * twidth)];
+
+	pcolumndest = r_drawsurf.surfdat;
+
+	for (u=0 ; u<r_numhblocks; u++)
+	{
+		r_lightptr = blocklights + u;
+
+		prowdestbase = pcolumndest;
+
+		pbasesource = basetptr + soffset;
+
+		(*pblockdrawer)();
+
+		soffset = soffset + blocksize;
+		if (soffset >= smax)
+			soffset = 0;
+
+		pcolumndest += horzblockstep;
+	}
+}
+
+
+//=============================================================================
+
+#if	!id386
+
+/*
+================
+R_DrawSurfaceBlock8_mip0
+================
+*/
+void R_DrawSurfaceBlock8_mip0 (void)
+{
+	int				v, i, b, lightstep, lighttemp, light;
+	unsigned char	pix, *psource, *prowdest;
+
+	psource = pbasesource;
+	prowdest = prowdestbase;
+
+	for (v=0 ; v<r_numvblocks ; v++)
+	{
+	// FIXME: make these locals?
+	// FIXME: use delta rather than both right and left, like ASM?
+		lightleft = r_lightptr[0];
+		lightright = r_lightptr[1];
+		r_lightptr += r_lightwidth;
+		lightleftstep = (r_lightptr[0] - lightleft) >> 4;
+		lightrightstep = (r_lightptr[1] - lightright) >> 4;
+
+		for (i=0 ; i<16 ; i++)
+		{
+			lighttemp = lightleft - lightright;
+			lightstep = lighttemp >> 4;
+
+			light = lightright;
+
+			for (b=15; b>=0; b--)
+			{
+				pix = psource[b];
+				prowdest[b] = ((unsigned char *)vid.colormap)
+						[(light & 0xFF00) + pix];
+				light += lightstep;
+			}
+	
+			psource += sourcetstep;
+			lightright += lightrightstep;
+			lightleft += lightleftstep;
+			prowdest += surfrowbytes;
+		}
+
+		if (psource >= r_sourcemax)
+			psource -= r_stepback;
+	}
+}
+
+
+/*
+================
+R_DrawSurfaceBlock8_mip1
+================
+*/
+void R_DrawSurfaceBlock8_mip1 (void)
+{
+	int				v, i, b, lightstep, lighttemp, light;
+	unsigned char	pix, *psource, *prowdest;
+
+	psource = pbasesource;
+	prowdest = prowdestbase;
+
+	for (v=0 ; v<r_numvblocks ; v++)
+	{
+	// FIXME: make these locals?
+	// FIXME: use delta rather than both right and left, like ASM?
+		lightleft = r_lightptr[0];
+		lightright = r_lightptr[1];
+		r_lightptr += r_lightwidth;
+		lightleftstep = (r_lightptr[0] - lightleft) >> 3;
+		lightrightstep = (r_lightptr[1] - lightright) >> 3;
+
+		for (i=0 ; i<8 ; i++)
+		{
+			lighttemp = lightleft - lightright;
+			lightstep = lighttemp >> 3;
+
+			light = lightright;
+
+			for (b=7; b>=0; b--)
+			{
+				pix = psource[b];
+				prowdest[b] = ((unsigned char *)vid.colormap)
+						[(light & 0xFF00) + pix];
+				light += lightstep;
+			}
+	
+			psource += sourcetstep;
+			lightright += lightrightstep;
+			lightleft += lightleftstep;
+			prowdest += surfrowbytes;
+		}
+
+		if (psource >= r_sourcemax)
+			psource -= r_stepback;
+	}
+}
+
+
+/*
+================
+R_DrawSurfaceBlock8_mip2
+================
+*/
+void R_DrawSurfaceBlock8_mip2 (void)
+{
+	int				v, i, b, lightstep, lighttemp, light;
+	unsigned char	pix, *psource, *prowdest;
+
+	psource = pbasesource;
+	prowdest = prowdestbase;
+
+	for (v=0 ; v<r_numvblocks ; v++)
+	{
+	// FIXME: make these locals?
+	// FIXME: use delta rather than both right and left, like ASM?
+		lightleft = r_lightptr[0];
+		lightright = r_lightptr[1];
+		r_lightptr += r_lightwidth;
+		lightleftstep = (r_lightptr[0] - lightleft) >> 2;
+		lightrightstep = (r_lightptr[1] - lightright) >> 2;
+
+		for (i=0 ; i<4 ; i++)
+		{
+			lighttemp = lightleft - lightright;
+			lightstep = lighttemp >> 2;
+
+			light = lightright;
+
+			for (b=3; b>=0; b--)
+			{
+				pix = psource[b];
+				prowdest[b] = ((unsigned char *)vid.colormap)
+						[(light & 0xFF00) + pix];
+				light += lightstep;
+			}
+	
+			psource += sourcetstep;
+			lightright += lightrightstep;
+			lightleft += lightleftstep;
+			prowdest += surfrowbytes;
+		}
+
+		if (psource >= r_sourcemax)
+			psource -= r_stepback;
+	}
+}
+
+
+/*
+================
+R_DrawSurfaceBlock8_mip3
+================
+*/
+void R_DrawSurfaceBlock8_mip3 (void)
+{
+	int				v, i, b, lightstep, lighttemp, light;
+	unsigned char	pix, *psource, *prowdest;
+
+	psource = pbasesource;
+	prowdest = prowdestbase;
+
+	for (v=0 ; v<r_numvblocks ; v++)
+	{
+	// FIXME: make these locals?
+	// FIXME: use delta rather than both right and left, like ASM?
+		lightleft = r_lightptr[0];
+		lightright = r_lightptr[1];
+		r_lightptr += r_lightwidth;
+		lightleftstep = (r_lightptr[0] - lightleft) >> 1;
+		lightrightstep = (r_lightptr[1] - lightright) >> 1;
+
+		for (i=0 ; i<2 ; i++)
+		{
+			lighttemp = lightleft - lightright;
+			lightstep = lighttemp >> 1;
+
+			light = lightright;
+
+			for (b=1; b>=0; b--)
+			{
+				pix = psource[b];
+				prowdest[b] = ((unsigned char *)vid.colormap)
+						[(light & 0xFF00) + pix];
+				light += lightstep;
+			}
+	
+			psource += sourcetstep;
+			lightright += lightrightstep;
+			lightleft += lightleftstep;
+			prowdest += surfrowbytes;
+		}
+
+		if (psource >= r_sourcemax)
+			psource -= r_stepback;
+	}
+}
+
+
+/*
+================
+R_DrawSurfaceBlock16
+
+FIXME: make this work
+================
+*/
+void R_DrawSurfaceBlock16 (void)
+{
+	int				k;
+	unsigned char	*psource;
+	int				lighttemp, lightstep, light;
+	unsigned short	*prowdest;
+
+	prowdest = (unsigned short *)prowdestbase;
+
+	for (k=0 ; k<blocksize ; k++)
+	{
+		unsigned short	*pdest;
+		unsigned char	pix;
+		int				b;
+
+		psource = pbasesource;
+		lighttemp = lightright - lightleft;
+		lightstep = lighttemp >> blockdivshift;
+
+		light = lightleft;
+		pdest = prowdest;
+
+		for (b=0; b<blocksize; b++)
+		{
+			pix = *psource;
+			*pdest = vid.colormap16[(light & 0xFF00) + pix];
+			psource += sourcesstep;
+			pdest++;
+			light += lightstep;
+		}
+
+		pbasesource += sourcetstep;
+		lightright += lightrightstep;
+		lightleft += lightleftstep;
+		prowdest = (unsigned short *)((long)prowdest + surfrowbytes);
+	}
+
+	prowdestbase = prowdest;
+}
+
+#endif
+
+
+//============================================================================
+
+/*
+================
+R_GenTurbTile
+================
+*/
+void R_GenTurbTile (pixel_t *pbasetex, void *pdest)
+{
+	int		*turb;
+	int		i, j, s, t;
+	byte	*pd;
+	
+	turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1));
+	pd = (byte *)pdest;
+
+	for (i=0 ; i<TILE_SIZE ; i++)
+	{
+		for (j=0 ; j<TILE_SIZE ; j++)
+		{	
+			s = (((j << 16) + turb[i & (CYCLE-1)]) >> 16) & 63;
+			t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63;
+			*pd++ = *(pbasetex + (t<<6) + s);
+		}
+	}
+}
+
+
+/*
+================
+R_GenTurbTile16
+================
+*/
+void R_GenTurbTile16 (pixel_t *pbasetex, void *pdest)
+{
+	int				*turb;
+	int				i, j, s, t;
+	unsigned short	*pd;
+
+	turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1));
+	pd = (unsigned short *)pdest;
+
+	for (i=0 ; i<TILE_SIZE ; i++)
+	{
+		for (j=0 ; j<TILE_SIZE ; j++)
+		{	
+			s = (((j << 16) + turb[i & (CYCLE-1)]) >> 16) & 63;
+			t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63;
+			*pd++ = d_8to16table[*(pbasetex + (t<<6) + s)];
+		}
+	}
+}
+
+
+/*
+================
+R_GenTile
+================
+*/
+void R_GenTile (msurface_t *psurf, void *pdest)
+{
+	if (psurf->flags & SURF_DRAWTURB)
+	{
+		if (r_pixbytes == 1)
+		{
+			R_GenTurbTile ((pixel_t *)
+				((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest);
+		}
+		else
+		{
+			R_GenTurbTile16 ((pixel_t *)
+				((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest);
+		}
+	}
+	else if (psurf->flags & SURF_DRAWSKY)
+	{
+		if (r_pixbytes == 1)
+		{
+			R_GenSkyTile (pdest);
+		}
+		else
+		{
+			R_GenSkyTile16 (pdest);
+		}
+	}
+	else
+	{
+		Sys_Error ("Unknown tile type");
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/r_vars.c b/apps/plugins/sdl/progs/quake/r_vars.c
new file mode 100644
index 0000000..eb41290
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_vars.c
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// r_vars.c: global refresh variables
+
+#include	"quakedef.h"
+
+#if	!id386
+
+// all global and static refresh variables are collected in a contiguous block
+// to avoid cache conflicts.
+
+//-------------------------------------------------------
+// global refresh variables
+//-------------------------------------------------------
+
+// FIXME: make into one big structure, like cl or sv
+// FIXME: do separately for refresh engine and driver
+
+int	r_bmodelactive;
+
+#endif	// !id386
+
diff --git a/apps/plugins/sdl/progs/quake/r_varsa.S b/apps/plugins/sdl/progs/quake/r_varsa.S
new file mode 100644
index 0000000..2c3f9e5
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/r_varsa.S
@@ -0,0 +1,64 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// r_varsa.s
+//
+
+#include "asm_i386.h"
+#include "quakeasm.h"
+#include "asm_draw.h"
+#include "d_ifacea.h"
+
+#if id386
+
+	.data
+
+//-------------------------------------------------------
+// ASM-only variables
+//-------------------------------------------------------
+.globl	float_1, float_particle_z_clip, float_point5
+.globl	float_minus_1, float_0
+float_0:		.single	0.0
+float_1:		.single	1.0
+float_minus_1:	.single	-1.0
+float_particle_z_clip:	.single	PARTICLE_Z_CLIP
+float_point5:	.single	0.5
+
+.globl	fp_16, fp_64k, fp_1m, fp_64kx64k
+.globl	fp_1m_minus_1
+.globl	fp_8 
+fp_1m:			.single	1048576.0
+fp_1m_minus_1:	.single	1048575.0
+fp_64k:			.single	65536.0
+fp_8:			.single	8.0
+fp_16:			.single	16.0
+fp_64kx64k:		.long	0x4f000000	// (float)0x8000*0x10000
+
+
+.globl	FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd
+FloatZero:				.long	0
+Float2ToThe31nd:		.long	0x4f000000
+FloatMinus2ToThe31nd:	.long	0xcf000000
+
+.globl	C(r_bmodelactive)
+C(r_bmodelactive):	.long	0
+
+#endif	// id386
+
diff --git a/apps/plugins/sdl/progs/quake/render.h b/apps/plugins/sdl/progs/quake/render.h
new file mode 100644
index 0000000..b5f8382
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/render.h
@@ -0,0 +1,158 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// refresh.h -- public interface to refresh functions
+
+#define	MAXCLIPPLANES	11
+
+#define	TOP_RANGE		16			// soldier uniform colors
+#define	BOTTOM_RANGE	96
+
+//=============================================================================
+
+typedef struct efrag_s
+{
+	struct mleaf_s		*leaf;
+	struct efrag_s		*leafnext;
+	struct entity_s		*entity;
+	struct efrag_s		*entnext;
+} efrag_t;
+
+
+typedef struct entity_s
+{
+	qboolean				forcelink;		// model changed
+
+	int						update_type;
+
+	entity_state_t			baseline;		// to fill in defaults in updates
+
+	double					msgtime;		// time of last update
+	vec3_t					msg_origins[2];	// last two updates (0 is newest)	
+	vec3_t					origin;
+	vec3_t					msg_angles[2];	// last two updates (0 is newest)
+	vec3_t					angles;	
+	struct model_s			*model;			// NULL = no model
+	struct efrag_s			*efrag;			// linked list of efrags
+	int						frame;
+	float					syncbase;		// for client-side animations
+	byte					*colormap;
+	int						effects;		// light, particals, etc
+	int						skinnum;		// for Alias models
+	int						visframe;		// last frame this entity was
+											//  found in an active leaf
+											
+	int						dlightframe;	// dynamic lighting
+	int						dlightbits;
+	
+// FIXME: could turn these into a union
+	int						trivial_accept;
+	struct mnode_s			*topnode;		// for bmodels, first world node
+											//  that splits bmodel, or NULL if
+											//  not split
+} entity_t;
+
+// !!! if this is changed, it must be changed in asm_draw.h too !!!
+typedef struct
+{
+	vrect_t		vrect;				// subwindow in video for refresh
+									// FIXME: not need vrect next field here?
+	vrect_t		aliasvrect;			// scaled Alias version
+	int			vrectright, vrectbottom;	// right & bottom screen coords
+	int			aliasvrectright, aliasvrectbottom;	// scaled Alias versions
+	float		vrectrightedge;			// rightmost right edge we care about,
+										//  for use in edge list
+	float		fvrectx, fvrecty;		// for floating-point compares
+	float		fvrectx_adj, fvrecty_adj; // left and top edges, for clamping
+	int			vrect_x_adj_shift20;	// (vrect.x + 0.5 - epsilon) << 20
+	int			vrectright_adj_shift20;	// (vrectright + 0.5 - epsilon) << 20
+	float		fvrectright_adj, fvrectbottom_adj;
+										// right and bottom edges, for clamping
+	float		fvrectright;			// rightmost edge, for Alias clamping
+	float		fvrectbottom;			// bottommost edge, for Alias clamping
+	float		horizontalFieldOfView;	// at Z = 1.0, this many X is visible 
+										// 2.0 = 90 degrees
+	float		xOrigin;			// should probably allways be 0.5
+	float		yOrigin;			// between be around 0.3 to 0.5
+
+	vec3_t		vieworg;
+	vec3_t		viewangles;
+	
+	float		fov_x, fov_y;
+
+	int			ambientlight;
+} refdef_t;
+
+
+//
+// refresh
+//
+extern	int		reinit_surfcache;
+
+
+extern	refdef_t	r_refdef;
+extern vec3_t	r_origin, vpn, vright, vup;
+
+extern	struct texture_s	*r_notexture_mip;
+
+
+void R_Init (void);
+void R_InitTextures (void);
+void R_InitEfrags (void);
+void R_RenderView (void);		// must set r_refdef first
+void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect);
+								// called whenever r_refdef or vid change
+void R_InitSky (struct texture_s *mt);	// called at level load
+
+void R_AddEfrags (entity_t *ent);
+void R_RemoveEfrags (entity_t *ent);
+
+void R_NewMap (void);
+
+
+void R_ParseParticleEffect (void);
+void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
+void R_RocketTrail (vec3_t start, vec3_t end, int type);
+
+#ifdef QUAKE2
+void R_DarkFieldParticles (entity_t *ent);
+#endif
+void R_EntityParticles (entity_t *ent);
+void R_BlobExplosion (vec3_t org);
+void R_ParticleExplosion (vec3_t org);
+void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength);
+void R_LavaSplash (vec3_t org);
+void R_TeleportSplash (vec3_t org);
+
+void R_PushDlights (void);
+
+
+//
+// surface cache related
+//
+extern	int		reinit_surfcache;	// if 1, surface cache is currently empty and
+extern qboolean	r_cache_thrash;	// set if thrashing the surface cache
+
+int	D_SurfaceCacheForRes (int width, int height);
+void D_FlushCaches (void);
+void D_DeleteSurfaceCache (void);
+void D_InitCaches (void *buffer, int size);
+void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj);
+
diff --git a/apps/plugins/sdl/progs/quake/resource.h b/apps/plugins/sdl/progs/quake/resource.h
new file mode 100644
index 0000000..afabb2e
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/resource.h
@@ -0,0 +1,20 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by winquake.rc
+//
+#define IDS_STRING1                     1
+#define IDI_ICON2                       1
+#define IDD_DIALOG1                     108
+#define IDD_PROGRESS                    109
+#define IDC_PROGRESS                    1000
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        111
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1004
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/apps/plugins/sdl/progs/quake/sbar.c b/apps/plugins/sdl/progs/quake/sbar.c
new file mode 100644
index 0000000..be9ec9c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sbar.c
@@ -0,0 +1,1323 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sbar.c -- status bar code
+
+#include "quakedef.h"
+
+
+int			sb_updates;		// if >= vid.numpages, no update needed
+
+#define STAT_MINUS		10	// num frame for '-' stats digit
+qpic_t		*sb_nums[2][11];
+qpic_t		*sb_colon, *sb_slash;
+qpic_t		*sb_ibar;
+qpic_t		*sb_sbar;
+qpic_t		*sb_scorebar;
+
+qpic_t      *sb_weapons[7][8];   // 0 is active, 1 is owned, 2-5 are flashes
+qpic_t      *sb_ammo[4];
+qpic_t		*sb_sigil[4];
+qpic_t		*sb_armor[3];
+qpic_t		*sb_items[32];
+
+qpic_t	*sb_faces[7][2];		// 0 is gibbed, 1 is dead, 2-6 are alive
+							// 0 is static, 1 is temporary animation
+qpic_t	*sb_face_invis;
+qpic_t	*sb_face_quad;
+qpic_t	*sb_face_invuln;
+qpic_t	*sb_face_invis_invuln;
+
+qboolean	sb_showscores;
+
+int			sb_lines;			// scan lines to draw
+
+qpic_t      *rsb_invbar[2];
+qpic_t      *rsb_weapons[5];
+qpic_t      *rsb_items[2];
+qpic_t      *rsb_ammo[3];
+qpic_t      *rsb_teambord;		// PGM 01/19/97 - team color border
+
+//MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
+qpic_t      *hsb_weapons[7][5];   // 0 is active, 1 is owned, 2-5 are flashes
+//MED 01/04/97 added array to simplify weapon parsing
+int         hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
+//MED 01/04/97 added hipnotic items array
+qpic_t      *hsb_items[2];
+
+void Sbar_MiniDeathmatchOverlay (void);
+void Sbar_DeathmatchOverlay (void);
+void M_DrawPic (int x, int y, qpic_t *pic);
+
+/*
+===============
+Sbar_ShowScores
+
+Tab key down
+===============
+*/
+void Sbar_ShowScores (void)
+{
+	if (sb_showscores)
+		return;
+	sb_showscores = true;
+	sb_updates = 0;
+}
+
+/*
+===============
+Sbar_DontShowScores
+
+Tab key up
+===============
+*/
+void Sbar_DontShowScores (void)
+{
+	sb_showscores = false;
+	sb_updates = 0;
+}
+
+/*
+===============
+Sbar_Changed
+===============
+*/
+void Sbar_Changed (void)
+{
+	sb_updates = 0;	// update next frame
+}
+
+/*
+===============
+Sbar_Init
+===============
+*/
+void Sbar_Init (void)
+{
+	int		i;
+
+	for (i=0 ; i<10 ; i++)
+	{
+		sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i));
+		sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i));
+	}
+
+	sb_nums[0][10] = Draw_PicFromWad ("num_minus");
+	sb_nums[1][10] = Draw_PicFromWad ("anum_minus");
+
+	sb_colon = Draw_PicFromWad ("num_colon");
+	sb_slash = Draw_PicFromWad ("num_slash");
+
+	sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun");
+	sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun");
+	sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun");
+	sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun");
+	sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch");
+	sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch");
+	sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng");
+
+	sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun");
+	sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun");
+	sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun");
+	sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun");
+	sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch");
+	sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch");
+	sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng");
+
+	for (i=0 ; i<5 ; i++)
+	{
+		sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1));
+		sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1));
+		sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1));
+		sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1));
+		sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1));
+		sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1));
+		sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1));
+	}
+
+	sb_ammo[0] = Draw_PicFromWad ("sb_shells");
+	sb_ammo[1] = Draw_PicFromWad ("sb_nails");
+	sb_ammo[2] = Draw_PicFromWad ("sb_rocket");
+	sb_ammo[3] = Draw_PicFromWad ("sb_cells");
+
+	sb_armor[0] = Draw_PicFromWad ("sb_armor1");
+	sb_armor[1] = Draw_PicFromWad ("sb_armor2");
+	sb_armor[2] = Draw_PicFromWad ("sb_armor3");
+
+	sb_items[0] = Draw_PicFromWad ("sb_key1");
+	sb_items[1] = Draw_PicFromWad ("sb_key2");
+	sb_items[2] = Draw_PicFromWad ("sb_invis");
+	sb_items[3] = Draw_PicFromWad ("sb_invuln");
+	sb_items[4] = Draw_PicFromWad ("sb_suit");
+	sb_items[5] = Draw_PicFromWad ("sb_quad");
+
+	sb_sigil[0] = Draw_PicFromWad ("sb_sigil1");
+	sb_sigil[1] = Draw_PicFromWad ("sb_sigil2");
+	sb_sigil[2] = Draw_PicFromWad ("sb_sigil3");
+	sb_sigil[3] = Draw_PicFromWad ("sb_sigil4");
+
+	sb_faces[4][0] = Draw_PicFromWad ("face1");
+	sb_faces[4][1] = Draw_PicFromWad ("face_p1");
+	sb_faces[3][0] = Draw_PicFromWad ("face2");
+	sb_faces[3][1] = Draw_PicFromWad ("face_p2");
+	sb_faces[2][0] = Draw_PicFromWad ("face3");
+	sb_faces[2][1] = Draw_PicFromWad ("face_p3");
+	sb_faces[1][0] = Draw_PicFromWad ("face4");
+	sb_faces[1][1] = Draw_PicFromWad ("face_p4");
+	sb_faces[0][0] = Draw_PicFromWad ("face5");
+	sb_faces[0][1] = Draw_PicFromWad ("face_p5");
+
+	sb_face_invis = Draw_PicFromWad ("face_invis");
+	sb_face_invuln = Draw_PicFromWad ("face_invul2");
+	sb_face_invis_invuln = Draw_PicFromWad ("face_inv2");
+	sb_face_quad = Draw_PicFromWad ("face_quad");
+
+	Cmd_AddCommand ("+showscores", Sbar_ShowScores);
+	Cmd_AddCommand ("-showscores", Sbar_DontShowScores);
+
+	sb_sbar = Draw_PicFromWad ("sbar");
+	sb_ibar = Draw_PicFromWad ("ibar");
+	sb_scorebar = Draw_PicFromWad ("scorebar");
+
+//MED 01/04/97 added new hipnotic weapons
+	if (hipnotic)
+	{
+	  hsb_weapons[0][0] = Draw_PicFromWad ("inv_laser");
+	  hsb_weapons[0][1] = Draw_PicFromWad ("inv_mjolnir");
+	  hsb_weapons[0][2] = Draw_PicFromWad ("inv_gren_prox");
+	  hsb_weapons[0][3] = Draw_PicFromWad ("inv_prox_gren");
+	  hsb_weapons[0][4] = Draw_PicFromWad ("inv_prox");
+
+	  hsb_weapons[1][0] = Draw_PicFromWad ("inv2_laser");
+	  hsb_weapons[1][1] = Draw_PicFromWad ("inv2_mjolnir");
+	  hsb_weapons[1][2] = Draw_PicFromWad ("inv2_gren_prox");
+	  hsb_weapons[1][3] = Draw_PicFromWad ("inv2_prox_gren");
+	  hsb_weapons[1][4] = Draw_PicFromWad ("inv2_prox");
+
+	  for (i=0 ; i<5 ; i++)
+	  {
+		 hsb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_laser",i+1));
+		 hsb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_mjolnir",i+1));
+		 hsb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_gren_prox",i+1));
+		 hsb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_prox_gren",i+1));
+		 hsb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_prox",i+1));
+	  }
+
+	  hsb_items[0] = Draw_PicFromWad ("sb_wsuit");
+	  hsb_items[1] = Draw_PicFromWad ("sb_eshld");
+	}
+
+	if (rogue)
+	{
+		rsb_invbar[0] = Draw_PicFromWad ("r_invbar1");
+		rsb_invbar[1] = Draw_PicFromWad ("r_invbar2");
+
+		rsb_weapons[0] = Draw_PicFromWad ("r_lava");
+		rsb_weapons[1] = Draw_PicFromWad ("r_superlava");
+		rsb_weapons[2] = Draw_PicFromWad ("r_gren");
+		rsb_weapons[3] = Draw_PicFromWad ("r_multirock");
+		rsb_weapons[4] = Draw_PicFromWad ("r_plasma");
+
+		rsb_items[0] = Draw_PicFromWad ("r_shield1");
+        rsb_items[1] = Draw_PicFromWad ("r_agrav1");
+
+// PGM 01/19/97 - team color border
+        rsb_teambord = Draw_PicFromWad ("r_teambord");
+// PGM 01/19/97 - team color border
+
+		rsb_ammo[0] = Draw_PicFromWad ("r_ammolava");
+		rsb_ammo[1] = Draw_PicFromWad ("r_ammomulti");
+		rsb_ammo[2] = Draw_PicFromWad ("r_ammoplasma");
+	}
+}
+
+
+//=============================================================================
+
+// drawing routines are relative to the status bar location
+
+/*
+=============
+Sbar_DrawPic
+=============
+*/
+void Sbar_DrawPic (int x, int y, qpic_t *pic)
+{
+	if (cl.gametype == GAME_DEATHMATCH)
+		Draw_Pic (x /* + ((vid.width - 320)>>1)*/, y + (vid.height-SBAR_HEIGHT), pic);
+	else
+		Draw_Pic (x + ((vid.width - 320)>>1), y + (vid.height-SBAR_HEIGHT), pic);
+}
+
+/*
+=============
+Sbar_DrawTransPic
+=============
+*/
+void Sbar_DrawTransPic (int x, int y, qpic_t *pic)
+{
+	if (cl.gametype == GAME_DEATHMATCH)
+		Draw_TransPic (x /*+ ((vid.width - 320)>>1)*/, y + (vid.height-SBAR_HEIGHT), pic);
+	else
+		Draw_TransPic (x + ((vid.width - 320)>>1), y + (vid.height-SBAR_HEIGHT), pic);
+}
+
+/*
+================
+Sbar_DrawCharacter
+
+Draws one solid graphics character
+================
+*/
+void Sbar_DrawCharacter (int x, int y, int num)
+{
+	if (cl.gametype == GAME_DEATHMATCH)
+		Draw_Character ( x /*+ ((vid.width - 320)>>1) */ + 4 , y + vid.height-SBAR_HEIGHT, num);
+	else
+		Draw_Character ( x + ((vid.width - 320)>>1) + 4 , y + vid.height-SBAR_HEIGHT, num);
+}
+
+/*
+================
+Sbar_DrawString
+================
+*/
+void Sbar_DrawString (int x, int y, char *str)
+{
+	if (cl.gametype == GAME_DEATHMATCH)
+		Draw_String (x /*+ ((vid.width - 320)>>1)*/, y+ vid.height-SBAR_HEIGHT, str);
+	else
+		Draw_String (x + ((vid.width - 320)>>1), y+ vid.height-SBAR_HEIGHT, str);
+}
+
+/*
+=============
+Sbar_itoa
+=============
+*/
+int Sbar_itoa (int num, char *buf)
+{
+	char	*str;
+	int		pow10;
+	int		dig;
+
+	str = buf;
+
+	if (num < 0)
+	{
+		*str++ = '-';
+		num = -num;
+	}
+
+	for (pow10 = 10 ; num >= pow10 ; pow10 *= 10)
+	;
+
+	do
+	{
+		pow10 /= 10;
+		dig = num/pow10;
+		*str++ = '0'+dig;
+		num -= dig*pow10;
+	} while (pow10 != 1);
+
+	*str = 0;
+
+	return str-buf;
+}
+
+
+/*
+=============
+Sbar_DrawNum
+=============
+*/
+void Sbar_DrawNum (int x, int y, int num, int digits, int color)
+{
+	char			str[12];
+	char			*ptr;
+	int				l, frame;
+
+	l = Sbar_itoa (num, str);
+	ptr = str;
+	if (l > digits)
+		ptr += (l-digits);
+	if (l < digits)
+		x += (digits-l)*24;
+
+	while (*ptr)
+	{
+		if (*ptr == '-')
+			frame = STAT_MINUS;
+		else
+			frame = *ptr -'0';
+
+		Sbar_DrawTransPic (x,y,sb_nums[color][frame]);
+		x += 24;
+		ptr++;
+	}
+}
+
+//=============================================================================
+
+int		fragsort[MAX_SCOREBOARD];
+
+char	scoreboardtext[MAX_SCOREBOARD][20];
+int		scoreboardtop[MAX_SCOREBOARD];
+int		scoreboardbottom[MAX_SCOREBOARD];
+int		scoreboardcount[MAX_SCOREBOARD];
+int		scoreboardlines;
+
+/*
+===============
+Sbar_SortFrags
+===============
+*/
+void Sbar_SortFrags (void)
+{
+	int		i, j, k;
+
+// sort by frags
+	scoreboardlines = 0;
+	for (i=0 ; i<cl.maxclients ; i++)
+	{
+		if (cl.scores[i].name[0])
+		{
+			fragsort[scoreboardlines] = i;
+			scoreboardlines++;
+		}
+	}
+
+	for (i=0 ; i<scoreboardlines ; i++)
+		for (j=0 ; j<scoreboardlines-1-i ; j++)
+			if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
+			{
+				k = fragsort[j];
+				fragsort[j] = fragsort[j+1];
+				fragsort[j+1] = k;
+			}
+}
+
+int	Sbar_ColorForMap (int m)
+{
+	return m < 128 ? m + 8 : m + 8;
+}
+
+/*
+===============
+Sbar_UpdateScoreboard
+===============
+*/
+void Sbar_UpdateScoreboard (void)
+{
+	int		i, k;
+	int		top, bottom;
+	scoreboard_t	*s;
+
+	Sbar_SortFrags ();
+
+// draw the text
+	memset (scoreboardtext, 0, sizeof(scoreboardtext));
+
+	for (i=0 ; i<scoreboardlines; i++)
+	{
+		k = fragsort[i];
+		s = &cl.scores[k];
+		sprintf (&scoreboardtext[i][1], "%3i %s", s->frags, s->name);
+
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15) <<4;
+		scoreboardtop[i] = Sbar_ColorForMap (top);
+		scoreboardbottom[i] = Sbar_ColorForMap (bottom);
+	}
+}
+
+
+
+/*
+===============
+Sbar_SoloScoreboard
+===============
+*/
+void Sbar_SoloScoreboard (void)
+{
+	char	str[80];
+	int		minutes, seconds, tens, units;
+	int		l;
+
+	sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
+	Sbar_DrawString (8, 4, str);
+
+	sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
+	Sbar_DrawString (8, 12, str);
+
+// time
+	minutes = cl.time / 60;
+	seconds = cl.time - 60*minutes;
+	tens = seconds / 10;
+	units = seconds - 10*tens;
+	sprintf (str,"Time :%3i:%i%i", minutes, tens, units);
+	Sbar_DrawString (184, 4, str);
+
+// draw level name
+	l = strlen (cl.levelname);
+	Sbar_DrawString (232 - l*4, 12, cl.levelname);
+}
+
+/*
+===============
+Sbar_DrawScoreboard
+===============
+*/
+void Sbar_DrawScoreboard (void)
+{
+	Sbar_SoloScoreboard ();
+	if (cl.gametype == GAME_DEATHMATCH)
+		Sbar_DeathmatchOverlay ();
+#if 0
+	int		i, j, c;
+	int		x, y;
+	int		l;
+	int		top, bottom;
+	scoreboard_t	*s;
+
+	if (cl.gametype != GAME_DEATHMATCH)
+	{
+		Sbar_SoloScoreboard ();
+		return;
+	}
+
+	Sbar_UpdateScoreboard ();
+
+	l = scoreboardlines <= 6 ? scoreboardlines : 6;
+
+	for (i=0 ; i<l ; i++)
+	{
+		x = 20*(i&1);
+		y = i/2 * 8;
+
+		s = &cl.scores[fragsort[i]];
+		if (!s->name[0])
+			continue;
+
+	// draw background
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15)<<4;
+		top = Sbar_ColorForMap (top);
+		bottom = Sbar_ColorForMap (bottom);
+
+		Draw_Fill ( x*8+10 + ((vid.width - 320)>>1), y + vid.height - SBAR_HEIGHT, 28, 4, top);
+		Draw_Fill ( x*8+10 + ((vid.width - 320)>>1), y+4 + vid.height - SBAR_HEIGHT, 28, 4, bottom);
+
+	// draw text
+		for (j=0 ; j<20 ; j++)
+		{
+			c = scoreboardtext[i][j];
+			if (c == 0 || c == ' ')
+				continue;
+			Sbar_DrawCharacter ( (x+j)*8, y, c);
+		}
+	}
+#endif
+}
+
+//=============================================================================
+
+/*
+===============
+Sbar_DrawInventory
+===============
+*/
+void Sbar_DrawInventory (void)
+{
+	int		i;
+	char	num[6];
+	float	time;
+	int		flashon;
+
+	if (rogue)
+	{
+		if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
+			Sbar_DrawPic (0, -24, rsb_invbar[0]);
+		else
+			Sbar_DrawPic (0, -24, rsb_invbar[1]);
+	}
+	else
+	{
+		Sbar_DrawPic (0, -24, sb_ibar);
+	}
+
+// weapons
+	for (i=0 ; i<7 ; i++)
+	{
+		if (cl.items & (IT_SHOTGUN<<i) )
+		{
+			time = cl.item_gettime[i];
+			flashon = (int)((cl.time - time)*10);
+			if (flashon >= 10)
+			{
+				if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )
+					flashon = 1;
+				else
+					flashon = 0;
+			}
+			else
+				flashon = (flashon%5) + 2;
+
+         Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]);
+
+			if (flashon > 1)
+				sb_updates = 0;		// force update to remove flash
+		}
+	}
+
+// MED 01/04/97
+// hipnotic weapons
+    if (hipnotic)
+    {
+      int grenadeflashing=0;
+      for (i=0 ; i<4 ; i++)
+      {
+         if (cl.items & (1<<hipweapons[i]) )
+         {
+            time = cl.item_gettime[hipweapons[i]];
+            flashon = (int)((cl.time - time)*10);
+            if (flashon >= 10)
+            {
+               if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])  )
+                  flashon = 1;
+               else
+                  flashon = 0;
+            }
+            else
+               flashon = (flashon%5) + 2;
+
+            // check grenade launcher
+            if (i==2)
+            {
+               if (cl.items & HIT_PROXIMITY_GUN)
+               {
+                  if (flashon)
+                  {
+                     grenadeflashing = 1;
+                     Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
+                  }
+               }
+            }
+            else if (i==3)
+            {
+               if (cl.items & (IT_SHOTGUN<<4))
+               {
+                  if (flashon && !grenadeflashing)
+                  {
+                     Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
+                  }
+                  else if (!grenadeflashing)
+                  {
+                     Sbar_DrawPic (96, -16, hsb_weapons[0][3]);
+                  }
+               }
+               else
+                  Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
+            }
+            else
+               Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
+            if (flashon > 1)
+               sb_updates = 0;      // force update to remove flash
+         }
+      }
+    }
+
+	if (rogue)
+	{
+    // check for powered up weapon.
+		if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
+		{
+			for (i=0;i<5;i++)
+			{
+				if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
+				{
+					Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
+				}
+			}
+		}
+	}
+
+// ammo counts
+	for (i=0 ; i<4 ; i++)
+	{
+		sprintf (num, "%3i",cl.stats[STAT_SHELLS+i] );
+		if (num[0] != ' ')
+			Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[0] - '0');
+		if (num[1] != ' ')
+			Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[1] - '0');
+		if (num[2] != ' ')
+			Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[2] - '0');
+	}
+
+	flashon = 0;
+   // items
+   for (i=0 ; i<6 ; i++)
+      if (cl.items & (1<<(17+i)))
+      {
+         time = cl.item_gettime[17+i];
+         if (time && time > cl.time - 2 && flashon )
+         {  // flash frame
+            sb_updates = 0;
+         }
+         else
+         {
+         //MED 01/04/97 changed keys
+            if (!hipnotic || (i>1))
+            {
+               Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
+            }
+         }
+         if (time && time > cl.time - 2)
+            sb_updates = 0;
+      }
+   //MED 01/04/97 added hipnotic items
+   // hipnotic items
+   if (hipnotic)
+   {
+      for (i=0 ; i<2 ; i++)
+         if (cl.items & (1<<(24+i)))
+         {
+            time = cl.item_gettime[24+i];
+            if (time && time > cl.time - 2 && flashon )
+            {  // flash frame
+               sb_updates = 0;
+            }
+            else
+            {
+               Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
+            }
+            if (time && time > cl.time - 2)
+               sb_updates = 0;
+         }
+   }
+
+	if (rogue)
+	{
+	// new rogue items
+		for (i=0 ; i<2 ; i++)
+		{
+			if (cl.items & (1<<(29+i)))
+			{
+				time = cl.item_gettime[29+i];
+
+				if (time &&	time > cl.time - 2 && flashon )
+				{	// flash frame
+					sb_updates = 0;
+				}
+				else
+				{
+					Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
+				}
+
+				if (time &&	time > cl.time - 2)
+					sb_updates = 0;
+			}
+		}
+	}
+	else
+	{
+	// sigils
+		for (i=0 ; i<4 ; i++)
+		{
+			if (cl.items & (1<<(28+i)))
+			{
+				time = cl.item_gettime[28+i];
+				if (time &&	time > cl.time - 2 && flashon )
+				{	// flash frame
+					sb_updates = 0;
+				}
+				else
+					Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
+				if (time &&	time > cl.time - 2)
+					sb_updates = 0;
+			}
+		}
+	}
+}
+
+//=============================================================================
+
+/*
+===============
+Sbar_DrawFrags
+===============
+*/
+void Sbar_DrawFrags (void)
+{
+	int				i, k, l;
+	int				top, bottom;
+	int				x, y, f;
+	int				xofs;
+	char			num[12];
+	scoreboard_t	*s;
+
+	Sbar_SortFrags ();
+
+// draw the text
+	l = scoreboardlines <= 4 ? scoreboardlines : 4;
+
+	x = 23;
+	if (cl.gametype == GAME_DEATHMATCH)
+		xofs = 0;
+	else
+		xofs = (vid.width - 320)>>1;
+	y = vid.height - SBAR_HEIGHT - 23;
+
+	for (i=0 ; i<l ; i++)
+	{
+		k = fragsort[i];
+		s = &cl.scores[k];
+		if (!s->name[0])
+			continue;
+
+	// draw background
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15)<<4;
+		top = Sbar_ColorForMap (top);
+		bottom = Sbar_ColorForMap (bottom);
+
+		Draw_Fill (xofs + x*8 + 10, y, 28, 4, top);
+		Draw_Fill (xofs + x*8 + 10, y+4, 28, 3, bottom);
+
+	// draw number
+		f = s->frags;
+		sprintf (num, "%3i",f);
+
+		Sbar_DrawCharacter ( (x+1)*8 , -24, num[0]);
+		Sbar_DrawCharacter ( (x+2)*8 , -24, num[1]);
+		Sbar_DrawCharacter ( (x+3)*8 , -24, num[2]);
+
+		if (k == cl.viewentity - 1)
+		{
+			Sbar_DrawCharacter (x*8+2, -24, 16);
+			Sbar_DrawCharacter ( (x+4)*8-4, -24, 17);
+		}
+		x+=4;
+	}
+}
+
+//=============================================================================
+
+
+/*
+===============
+Sbar_DrawFace
+===============
+*/
+void Sbar_DrawFace (void)
+{
+	int		f, anim;
+
+// PGM 01/19/97 - team color drawing
+// PGM 03/02/97 - fixed so color swatch only appears in CTF modes
+	if (rogue &&
+        (cl.maxclients != 1) &&
+        (teamplay.value>3) &&
+        (teamplay.value<7))
+	{
+		int				top, bottom;
+		int				xofs;
+		char			num[12];
+		scoreboard_t	*s;
+		
+		s = &cl.scores[cl.viewentity - 1];
+		// draw background
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15)<<4;
+		top = Sbar_ColorForMap (top);
+		bottom = Sbar_ColorForMap (bottom);
+
+		if (cl.gametype == GAME_DEATHMATCH)
+			xofs = 113;
+		else
+			xofs = ((vid.width - 320)>>1) + 113;
+
+		Sbar_DrawPic (112, 0, rsb_teambord);
+		Draw_Fill (xofs, vid.height-SBAR_HEIGHT+3, 22, 9, top);
+		Draw_Fill (xofs, vid.height-SBAR_HEIGHT+12, 22, 9, bottom);
+
+		// draw number
+		f = s->frags;
+		sprintf (num, "%3i",f);
+
+		if (top==8)
+		{
+			if (num[0] != ' ')
+				Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
+			if (num[1] != ' ')
+				Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
+			if (num[2] != ' ')
+				Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
+		}
+		else
+		{
+			Sbar_DrawCharacter ( 109, 3, num[0]);
+			Sbar_DrawCharacter ( 116, 3, num[1]);
+			Sbar_DrawCharacter ( 123, 3, num[2]);
+		}
+		
+		return;
+	}
+// PGM 01/19/97 - team color drawing
+
+	if ( (cl.items & (IT_INVISIBILITY | IT_INVULNERABILITY) )
+	== (IT_INVISIBILITY | IT_INVULNERABILITY) )
+	{
+		Sbar_DrawPic (112, 0, sb_face_invis_invuln);
+		return;
+	}
+	if (cl.items & IT_QUAD)
+	{
+		Sbar_DrawPic (112, 0, sb_face_quad );
+		return;
+	}
+	if (cl.items & IT_INVISIBILITY)
+	{
+		Sbar_DrawPic (112, 0, sb_face_invis );
+		return;
+	}
+	if (cl.items & IT_INVULNERABILITY)
+	{
+		Sbar_DrawPic (112, 0, sb_face_invuln);
+		return;
+	}
+
+	if (cl.stats[STAT_HEALTH] >= 100)
+		f = 4;
+	else
+		f = cl.stats[STAT_HEALTH] / 20;
+
+	if (cl.time <= cl.faceanimtime)
+	{
+		anim = 1;
+		sb_updates = 0;		// make sure the anim gets drawn over
+	}
+	else
+		anim = 0;
+	Sbar_DrawPic (112, 0, sb_faces[f][anim]);
+}
+
+/*
+===============
+Sbar_Draw
+===============
+*/
+void Sbar_Draw (void)
+{
+	if (scr_con_current == vid.height)
+		return;		// console is full screen
+
+	if (sb_updates >= vid.numpages)
+		return;
+
+	scr_copyeverything = 1;
+
+	sb_updates++;
+
+	if (sb_lines && vid.width > 320) 
+		Draw_TileClear (0, vid.height - sb_lines, vid.width, sb_lines);
+
+	if (sb_lines > 24)
+	{
+		Sbar_DrawInventory ();
+		if (cl.maxclients != 1)
+			Sbar_DrawFrags ();
+	}
+
+	if (sb_showscores || cl.stats[STAT_HEALTH] <= 0)
+	{
+		Sbar_DrawPic (0, 0, sb_scorebar);
+		Sbar_DrawScoreboard ();
+		sb_updates = 0;
+	}
+	else if (sb_lines)
+	{
+		Sbar_DrawPic (0, 0, sb_sbar);
+
+   // keys (hipnotic only)
+      //MED 01/04/97 moved keys here so they would not be overwritten
+      if (hipnotic)
+      {
+         if (cl.items & IT_KEY1)
+            Sbar_DrawPic (209, 3, sb_items[0]);
+         if (cl.items & IT_KEY2)
+            Sbar_DrawPic (209, 12, sb_items[1]);
+      }
+   // armor
+		if (cl.items & IT_INVULNERABILITY)
+		{
+			Sbar_DrawNum (24, 0, 666, 3, 1);
+			Sbar_DrawPic (0, 0, draw_disc);
+		}
+		else
+		{
+			if (rogue)
+			{
+				Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3,
+								cl.stats[STAT_ARMOR] <= 25);
+				if (cl.items & RIT_ARMOR3)
+					Sbar_DrawPic (0, 0, sb_armor[2]);
+				else if (cl.items & RIT_ARMOR2)
+					Sbar_DrawPic (0, 0, sb_armor[1]);
+				else if (cl.items & RIT_ARMOR1)
+					Sbar_DrawPic (0, 0, sb_armor[0]);
+			}
+			else
+			{
+				Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3
+				, cl.stats[STAT_ARMOR] <= 25);
+				if (cl.items & IT_ARMOR3)
+					Sbar_DrawPic (0, 0, sb_armor[2]);
+				else if (cl.items & IT_ARMOR2)
+					Sbar_DrawPic (0, 0, sb_armor[1]);
+				else if (cl.items & IT_ARMOR1)
+					Sbar_DrawPic (0, 0, sb_armor[0]);
+			}
+		}
+
+	// face
+		Sbar_DrawFace ();
+
+	// health
+		Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3
+		, cl.stats[STAT_HEALTH] <= 25);
+
+	// ammo icon
+		if (rogue)
+		{
+			if (cl.items & RIT_SHELLS)
+				Sbar_DrawPic (224, 0, sb_ammo[0]);
+			else if (cl.items & RIT_NAILS)
+				Sbar_DrawPic (224, 0, sb_ammo[1]);
+			else if (cl.items & RIT_ROCKETS)
+				Sbar_DrawPic (224, 0, sb_ammo[2]);
+			else if (cl.items & RIT_CELLS)
+				Sbar_DrawPic (224, 0, sb_ammo[3]);
+			else if (cl.items & RIT_LAVA_NAILS)
+				Sbar_DrawPic (224, 0, rsb_ammo[0]);
+			else if (cl.items & RIT_PLASMA_AMMO)
+				Sbar_DrawPic (224, 0, rsb_ammo[1]);
+			else if (cl.items & RIT_MULTI_ROCKETS)
+				Sbar_DrawPic (224, 0, rsb_ammo[2]);
+		}
+		else
+		{
+			if (cl.items & IT_SHELLS)
+				Sbar_DrawPic (224, 0, sb_ammo[0]);
+			else if (cl.items & IT_NAILS)
+				Sbar_DrawPic (224, 0, sb_ammo[1]);
+			else if (cl.items & IT_ROCKETS)
+				Sbar_DrawPic (224, 0, sb_ammo[2]);
+			else if (cl.items & IT_CELLS)
+				Sbar_DrawPic (224, 0, sb_ammo[3]);
+		}
+
+		Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3,
+					  cl.stats[STAT_AMMO] <= 10);
+	}
+
+	if (vid.width > 320) {
+		if (cl.gametype == GAME_DEATHMATCH)
+			Sbar_MiniDeathmatchOverlay ();
+	}
+}
+
+//=============================================================================
+
+/*
+==================
+Sbar_IntermissionNumber
+
+==================
+*/
+void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color)
+{
+	char			str[12];
+	char			*ptr;
+	int				l, frame;
+
+	l = Sbar_itoa (num, str);
+	ptr = str;
+	if (l > digits)
+		ptr += (l-digits);
+	if (l < digits)
+		x += (digits-l)*24;
+
+	while (*ptr)
+	{
+		if (*ptr == '-')
+			frame = STAT_MINUS;
+		else
+			frame = *ptr -'0';
+
+		Draw_TransPic (x,y,sb_nums[color][frame]);
+		x += 24;
+		ptr++;
+	}
+}
+
+/*
+==================
+Sbar_DeathmatchOverlay
+
+==================
+*/
+void Sbar_DeathmatchOverlay (void)
+{
+	qpic_t			*pic;
+	int				i, k, l;
+	int				top, bottom;
+	int				x, y, f;
+	char			num[12];
+	scoreboard_t	*s;
+
+	scr_copyeverything = 1;
+	scr_fullupdate = 0;
+
+	pic = Draw_CachePic ("gfx/ranking.lmp");
+	M_DrawPic ((320-pic->width)/2, 8, pic);
+
+// scores
+	Sbar_SortFrags ();
+
+// draw the text
+	l = scoreboardlines;
+
+	x = 80 + ((vid.width - 320)>>1);
+	y = 40;
+	for (i=0 ; i<l ; i++)
+	{
+		k = fragsort[i];
+		s = &cl.scores[k];
+		if (!s->name[0])
+			continue;
+
+	// draw background
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15)<<4;
+		top = Sbar_ColorForMap (top);
+		bottom = Sbar_ColorForMap (bottom);
+
+		Draw_Fill ( x, y, 40, 4, top);
+		Draw_Fill ( x, y+4, 40, 4, bottom);
+
+	// draw number
+		f = s->frags;
+		sprintf (num, "%3i",f);
+
+		Draw_Character ( x+8 , y, num[0]);
+		Draw_Character ( x+16 , y, num[1]);
+		Draw_Character ( x+24 , y, num[2]);
+
+		if (k == cl.viewentity - 1)
+			Draw_Character ( x - 8, y, 12);
+
+#if 0
+{
+	int				total;
+	int				n, minutes, tens, units;
+
+	// draw time
+		total = cl.completed_time - s->entertime;
+		minutes = (int)total/60;
+		n = total - minutes*60;
+		tens = n/10;
+		units = n%10;
+
+		sprintf (num, "%3i:%i%i", minutes, tens, units);
+
+		Draw_String ( x+48 , y, num);
+}
+#endif
+
+	// draw name
+		Draw_String (x+64, y, s->name);
+
+		y += 10;
+	}
+}
+
+/*
+==================
+Sbar_DeathmatchOverlay
+
+==================
+*/
+void Sbar_MiniDeathmatchOverlay (void)
+{
+	qpic_t			*pic;
+	int				i, k, l;
+	int				top, bottom;
+	int				x, y, f;
+	char			num[12];
+	scoreboard_t	*s;
+	int				numlines;
+
+	if (vid.width < 512 || !sb_lines)
+		return;
+
+	scr_copyeverything = 1;
+	scr_fullupdate = 0;
+
+// scores
+	Sbar_SortFrags ();
+
+// draw the text
+	l = scoreboardlines;
+	y = vid.height - sb_lines;
+	numlines = sb_lines/8;
+	if (numlines < 3)
+		return;
+
+	//find us
+	for (i = 0; i < scoreboardlines; i++)
+		if (fragsort[i] == cl.viewentity - 1)
+			break;
+
+    if (i == scoreboardlines) // we're not there
+            i = 0;
+    else // figure out start
+            i = i - numlines/2;
+
+    if (i > scoreboardlines - numlines)
+            i = scoreboardlines - numlines;
+    if (i < 0)
+            i = 0;
+
+	x = 324;
+	for (/* */; i < scoreboardlines && y < vid.height - 8 ; i++)
+	{
+		k = fragsort[i];
+		s = &cl.scores[k];
+		if (!s->name[0])
+			continue;
+
+	// draw background
+		top = s->colors & 0xf0;
+		bottom = (s->colors & 15)<<4;
+		top = Sbar_ColorForMap (top);
+		bottom = Sbar_ColorForMap (bottom);
+
+		Draw_Fill ( x, y+1, 40, 3, top);
+		Draw_Fill ( x, y+4, 40, 4, bottom);
+
+	// draw number
+		f = s->frags;
+		sprintf (num, "%3i",f);
+
+		Draw_Character ( x+8 , y, num[0]);
+		Draw_Character ( x+16 , y, num[1]);
+		Draw_Character ( x+24 , y, num[2]);
+
+		if (k == cl.viewentity - 1) {
+			Draw_Character ( x, y, 16);
+			Draw_Character ( x + 32, y, 17);
+		}
+
+#if 0
+{
+	int				total;
+	int				n, minutes, tens, units;
+
+	// draw time
+		total = cl.completed_time - s->entertime;
+		minutes = (int)total/60;
+		n = total - minutes*60;
+		tens = n/10;
+		units = n%10;
+
+		sprintf (num, "%3i:%i%i", minutes, tens, units);
+
+		Draw_String ( x+48 , y, num);
+}
+#endif
+
+	// draw name
+		Draw_String (x+48, y, s->name);
+
+		y += 8;
+	}
+}
+
+/*
+==================
+Sbar_IntermissionOverlay
+
+==================
+*/
+void Sbar_IntermissionOverlay (void)
+{
+	qpic_t	*pic;
+	int		dig;
+	int		num;
+
+	scr_copyeverything = 1;
+	scr_fullupdate = 0;
+
+	if (cl.gametype == GAME_DEATHMATCH)
+	{
+		Sbar_DeathmatchOverlay ();
+		return;
+	}
+
+	pic = Draw_CachePic ("gfx/complete.lmp");
+	Draw_Pic (64, 24, pic);
+
+	pic = Draw_CachePic ("gfx/inter.lmp");
+	Draw_TransPic (0, 56, pic);
+
+// time
+	dig = cl.completed_time/60;
+	Sbar_IntermissionNumber (160, 64, dig, 3, 0);
+	num = cl.completed_time - dig*60;
+	Draw_TransPic (234,64,sb_colon);
+	Draw_TransPic (246,64,sb_nums[0][num/10]);
+	Draw_TransPic (266,64,sb_nums[0][num%10]);
+
+	Sbar_IntermissionNumber (160, 104, cl.stats[STAT_SECRETS], 3, 0);
+	Draw_TransPic (232,104,sb_slash);
+	Sbar_IntermissionNumber (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
+
+	Sbar_IntermissionNumber (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
+	Draw_TransPic (232,144,sb_slash);
+	Sbar_IntermissionNumber (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
+
+}
+
+
+/*
+==================
+Sbar_FinaleOverlay
+
+==================
+*/
+void Sbar_FinaleOverlay (void)
+{
+	qpic_t	*pic;
+
+	scr_copyeverything = 1;
+
+	pic = Draw_CachePic ("gfx/finale.lmp");
+	Draw_TransPic ( (vid.width-pic->width)/2, 16, pic);
+}
diff --git a/apps/plugins/sdl/progs/quake/sbar.h b/apps/plugins/sdl/progs/quake/sbar.h
new file mode 100644
index 0000000..286b3b6
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sbar.h
@@ -0,0 +1,39 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+
+// the status bar is only redrawn if something has changed, but if anything
+// does, the entire thing will be redrawn for the next vid.numpages frames.
+
+#define	SBAR_HEIGHT		24
+
+extern	int			sb_lines;			// scan lines to draw
+
+void Sbar_Init (void);
+
+void Sbar_Changed (void);
+// call whenever any of the client stats represented on the sbar changes
+
+void Sbar_Draw (void);
+// called every frame by screen
+
+void Sbar_IntermissionOverlay (void);
+// called each frame after the level has been completed
+
+void Sbar_FinaleOverlay (void);
diff --git a/apps/plugins/sdl/progs/quake/screen.c b/apps/plugins/sdl/progs/quake/screen.c
new file mode 100644
index 0000000..8c623c9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/screen.c
@@ -0,0 +1,994 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// screen.c -- master for refresh, status bar, console, chat, notify, etc
+
+#include "quakedef.h"
+#include "r_local.h"
+
+// only the refresh window will be updated unless these variables are flagged 
+int			scr_copytop;
+int			scr_copyeverything;
+
+float		scr_con_current;
+float		scr_conlines;		// lines of console to display
+
+float		oldscreensize, oldfov;
+cvar_t		scr_viewsize = {"viewsize","100", true};
+cvar_t		scr_fov = {"fov","90"};	// 10 - 170
+cvar_t		scr_conspeed = {"scr_conspeed","300"};
+cvar_t		scr_centertime = {"scr_centertime","2"};
+cvar_t		scr_showram = {"showram","1"};
+cvar_t		scr_showturtle = {"showturtle","0"};
+cvar_t		scr_showpause = {"showpause","1"};
+cvar_t		scr_printspeed = {"scr_printspeed","8"};
+
+qboolean	scr_initialized;		// ready to draw
+
+qpic_t		*scr_ram;
+qpic_t		*scr_net;
+qpic_t		*scr_turtle;
+
+int			scr_fullupdate;
+
+int			clearconsole;
+int			clearnotify;
+
+viddef_t	vid;				// global video state
+
+vrect_t		*pconupdate;
+vrect_t		scr_vrect;
+
+qboolean	scr_disabled_for_loading;
+qboolean	scr_drawloading;
+float		scr_disabled_time;
+qboolean	scr_skipupdate;
+
+qboolean	block_drawing;
+
+void SCR_ScreenShot_f (void);
+
+/*
+===============================================================================
+
+CENTER PRINTING
+
+===============================================================================
+*/
+
+char		scr_centerstring[1024];
+float		scr_centertime_start;	// for slow victory printing
+float		scr_centertime_off;
+int			scr_center_lines;
+int			scr_erase_lines;
+int			scr_erase_center;
+
+/*
+==============
+SCR_CenterPrint
+
+Called for important messages that should stay in the center of the screen
+for a few moments
+==============
+*/
+void SCR_CenterPrint (char *str)
+{
+	strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
+	scr_centertime_off = scr_centertime.value;
+	scr_centertime_start = cl.time;
+
+// count the number of lines for centering
+	scr_center_lines = 1;
+	while (*str)
+	{
+		if (*str == '\n')
+			scr_center_lines++;
+		str++;
+	}
+}
+
+void SCR_EraseCenterString (void)
+{
+	int		y;
+
+	if (scr_erase_center++ > vid.numpages)
+	{
+		scr_erase_lines = 0;
+		return;
+	}
+
+	if (scr_center_lines <= 4)
+		y = vid.height*0.35;
+	else
+		y = 48;
+
+	scr_copytop = 1;
+	Draw_TileClear (0, y,vid.width, 8*scr_erase_lines);
+}
+
+void SCR_DrawCenterString (void)
+{
+	char	*start;
+	int		l;
+	int		j;
+	int		x, y;
+	int		remaining;
+
+// the finale prints the characters one at a time
+	if (cl.intermission)
+		remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
+	else
+		remaining = 9999;
+
+	scr_erase_center = 0;
+	start = scr_centerstring;
+
+	if (scr_center_lines <= 4)
+		y = vid.height*0.35;
+	else
+		y = 48;
+
+	do	
+	{
+	// scan the width of the line
+		for (l=0 ; l<40 ; l++)
+			if (start[l] == '\n' || !start[l])
+				break;
+		x = (vid.width - l*8)/2;
+		for (j=0 ; j<l ; j++, x+=8)
+		{
+			Draw_Character (x, y, start[j]);	
+			if (!remaining--)
+				return;
+		}
+			
+		y += 8;
+
+		while (*start && *start != '\n')
+			start++;
+
+		if (!*start)
+			break;
+		start++;		// skip the \n
+	} while (1);
+}
+
+void SCR_CheckDrawCenterString (void)
+{
+	scr_copytop = 1;
+	if (scr_center_lines > scr_erase_lines)
+		scr_erase_lines = scr_center_lines;
+
+	scr_centertime_off -= host_frametime;
+	
+	if (scr_centertime_off <= 0 && !cl.intermission)
+		return;
+	if (key_dest != key_game)
+		return;
+
+	SCR_DrawCenterString ();
+}
+
+//=============================================================================
+
+/*
+====================
+CalcFov
+====================
+*/
+float CalcFov (float fov_x, float width, float height)
+{
+        float   a;
+        float   x;
+
+        if (fov_x < 1 || fov_x > 179)
+                Sys_Error ("Bad fov: %f", fov_x);
+
+        x = width/tan(fov_x/360*M_PI);
+
+        a = atan (height/x);
+
+        a = a*360/M_PI;
+
+        return a;
+}
+
+/*
+=================
+SCR_CalcRefdef
+
+Must be called whenever vid changes
+Internal use only
+=================
+*/
+static void SCR_CalcRefdef (void)
+{
+	vrect_t		vrect;
+	float		size;
+
+	scr_fullupdate = 0;		// force a background redraw
+	vid.recalc_refdef = 0;
+
+// force the status bar to redraw
+	Sbar_Changed ();
+
+//========================================
+	
+// bound viewsize
+	if (scr_viewsize.value < 30)
+		Cvar_Set ("viewsize","30");
+	if (scr_viewsize.value > 120)
+		Cvar_Set ("viewsize","120");
+
+// bound field of view
+	if (scr_fov.value < 10)
+		Cvar_Set ("fov","10");
+	if (scr_fov.value > 170)
+		Cvar_Set ("fov","170");
+
+	r_refdef.fov_x = scr_fov.value;
+	r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
+
+// intermission is always full screen	
+	if (cl.intermission)
+		size = 120;
+	else
+		size = scr_viewsize.value;
+
+	if (size >= 120)
+		sb_lines = 0;		// no status bar at all
+	else if (size >= 110)
+		sb_lines = 24;		// no inventory
+	else
+		sb_lines = 24+16+8;
+
+// these calculations mirror those in R_Init() for r_refdef, but take no
+// account of water warping
+	vrect.x = 0;
+	vrect.y = 0;
+	vrect.width = vid.width;
+	vrect.height = vid.height;
+
+	R_SetVrect (&vrect, &scr_vrect, sb_lines);
+
+// guard against going from one mode to another that's less than half the
+// vertical resolution
+	if (scr_con_current > vid.height)
+		scr_con_current = vid.height;
+
+// notify the refresh of the change
+	R_ViewChanged (&vrect, sb_lines, vid.aspect);
+}
+
+
+/*
+=================
+SCR_SizeUp_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeUp_f (void)
+{
+	Cvar_SetValue ("viewsize",scr_viewsize.value+10);
+	vid.recalc_refdef = 1;
+}
+
+
+/*
+=================
+SCR_SizeDown_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeDown_f (void)
+{
+	Cvar_SetValue ("viewsize",scr_viewsize.value-10);
+	vid.recalc_refdef = 1;
+}
+
+//============================================================================
+
+/*
+==================
+SCR_Init
+==================
+*/
+void SCR_Init (void)
+{
+	Cvar_RegisterVariable (&scr_fov);
+	Cvar_RegisterVariable (&scr_viewsize);
+	Cvar_RegisterVariable (&scr_conspeed);
+	Cvar_RegisterVariable (&scr_showram);
+	Cvar_RegisterVariable (&scr_showturtle);
+	Cvar_RegisterVariable (&scr_showpause);
+	Cvar_RegisterVariable (&scr_centertime);
+	Cvar_RegisterVariable (&scr_printspeed);
+
+//
+// register our commands
+//
+	Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
+	Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
+	Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
+
+	scr_ram = Draw_PicFromWad ("ram");
+	scr_net = Draw_PicFromWad ("net");
+	scr_turtle = Draw_PicFromWad ("turtle");
+
+	scr_initialized = true;
+}
+
+
+
+/*
+==============
+SCR_DrawRam
+==============
+*/
+void SCR_DrawRam (void)
+{
+	if (!scr_showram.value)
+		return;
+
+	if (!r_cache_thrash)
+		return;
+
+	Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram);
+}
+
+/*
+==============
+SCR_DrawTurtle
+==============
+*/
+void SCR_DrawTurtle (void)
+{
+	static int	count;
+	
+	if (!scr_showturtle.value)
+		return;
+
+	if (host_frametime < 0.1)
+	{
+		count = 0;
+		return;
+	}
+
+	count++;
+	if (count < 3)
+		return;
+
+	Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle);
+}
+
+/*
+==============
+SCR_DrawNet
+==============
+*/
+void SCR_DrawNet (void)
+{
+	if (realtime - cl.last_received_message < 0.3)
+		return;
+	if (cls.demoplayback)
+		return;
+
+	Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net);
+}
+
+/*
+==============
+DrawPause
+==============
+*/
+void SCR_DrawPause (void)
+{
+	qpic_t	*pic;
+
+	if (!scr_showpause.value)		// turn off for screenshots
+		return;
+
+	if (!cl.paused)
+		return;
+
+	pic = Draw_CachePic ("gfx/pause.lmp");
+	Draw_Pic ( (vid.width - pic->width)/2, 
+		(vid.height - 48 - pic->height)/2, pic);
+}
+
+
+
+/*
+==============
+SCR_DrawLoading
+==============
+*/
+void SCR_DrawLoading (void)
+{
+	qpic_t	*pic;
+
+	if (!scr_drawloading)
+		return;
+		
+	pic = Draw_CachePic ("gfx/loading.lmp");
+	Draw_Pic ( (vid.width - pic->width)/2, 
+		(vid.height - 48 - pic->height)/2, pic);
+}
+
+
+
+//=============================================================================
+
+
+/*
+==================
+SCR_SetUpToDrawConsole
+==================
+*/
+void SCR_SetUpToDrawConsole (void)
+{
+	Con_CheckResize ();
+	
+	if (scr_drawloading)
+		return;		// never a console with loading plaque
+		
+// decide on the height of the console
+	con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
+
+	if (con_forcedup)
+	{
+		scr_conlines = vid.height;		// full screen
+		scr_con_current = scr_conlines;
+	}
+	else if (key_dest == key_console)
+		scr_conlines = vid.height/2;	// half screen
+	else
+		scr_conlines = 0;				// none visible
+	
+	if (scr_conlines < scr_con_current)
+	{
+		scr_con_current -= scr_conspeed.value*host_frametime;
+		if (scr_conlines > scr_con_current)
+			scr_con_current = scr_conlines;
+
+	}
+	else if (scr_conlines > scr_con_current)
+	{
+		scr_con_current += scr_conspeed.value*host_frametime;
+		if (scr_conlines < scr_con_current)
+			scr_con_current = scr_conlines;
+	}
+
+	if (clearconsole++ < vid.numpages)
+	{
+		scr_copytop = 1;
+		Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current);
+		Sbar_Changed ();
+	}
+	else if (clearnotify++ < vid.numpages)
+	{
+		scr_copytop = 1;
+		Draw_TileClear (0,0,vid.width, con_notifylines);
+	}
+	else
+		con_notifylines = 0;
+}
+	
+/*
+==================
+SCR_DrawConsole
+==================
+*/
+void SCR_DrawConsole (void)
+{
+	if (scr_con_current)
+	{
+		scr_copyeverything = 1;
+		Con_DrawConsole (scr_con_current, true);
+		clearconsole = 0;
+	}
+	else
+	{
+		if (key_dest == key_game || key_dest == key_message)
+			Con_DrawNotify ();	// only draw notify in game
+	}
+}
+
+
+/* 
+============================================================================== 
+ 
+						SCREEN SHOTS 
+ 
+============================================================================== 
+*/ 
+ 
+
+typedef struct
+{
+    char	manufacturer;
+    char	version;
+    char	encoding;
+    char	bits_per_pixel;
+    unsigned short	xmin,ymin,xmax,ymax;
+    unsigned short	hres,vres;
+    unsigned char	palette[48];
+    char	reserved;
+    char	color_planes;
+    unsigned short	bytes_per_line;
+    unsigned short	palette_type;
+    char	filler[58];
+    unsigned char	data;			// unbounded
+} pcx_t;
+
+/* 
+============== 
+WritePCXfile 
+============== 
+*/ 
+void WritePCXfile (char *filename, byte *data, int width, int height,
+	int rowbytes, byte *palette) 
+{
+	int		i, j, length;
+	pcx_t	*pcx;
+	byte		*pack;
+	  
+	pcx = Hunk_TempAlloc (width*height*2+1000);
+	if (pcx == NULL)
+	{
+		Con_Printf("SCR_ScreenShot_f: not enough memory\n");
+		return;
+	} 
+ 
+	pcx->manufacturer = 0x0a;	// PCX id
+	pcx->version = 5;			// 256 color
+ 	pcx->encoding = 1;		// uncompressed
+	pcx->bits_per_pixel = 8;		// 256 color
+	pcx->xmin = 0;
+	pcx->ymin = 0;
+	pcx->xmax = LittleShort((short)(width-1));
+	pcx->ymax = LittleShort((short)(height-1));
+	pcx->hres = LittleShort((short)width);
+	pcx->vres = LittleShort((short)height);
+	Q_memset (pcx->palette,0,sizeof(pcx->palette));
+	pcx->color_planes = 1;		// chunky image
+	pcx->bytes_per_line = LittleShort((short)width);
+	pcx->palette_type = LittleShort(2);		// not a grey scale
+	Q_memset (pcx->filler,0,sizeof(pcx->filler));
+
+// pack the image
+	pack = &pcx->data;
+	
+	for (i=0 ; i<height ; i++)
+	{
+		for (j=0 ; j<width ; j++)
+		{
+			if ( (*data & 0xc0) != 0xc0)
+				*pack++ = *data++;
+			else
+			{
+				*pack++ = 0xc1;
+				*pack++ = *data++;
+			}
+		}
+
+		data += rowbytes - width;
+	}
+			
+// write the palette
+	*pack++ = 0x0c;	// palette ID byte
+	for (i=0 ; i<768 ; i++)
+		*pack++ = *palette++;
+		
+// write output file 
+	length = pack - (byte *)pcx;
+	COM_WriteFile (filename, pcx, length);
+} 
+ 
+
+
+/* 
+================== 
+SCR_ScreenShot_f
+================== 
+*/  
+void SCR_ScreenShot_f (void) 
+{ 
+	int     i; 
+	char		pcxname[80]; 
+	char		checkname[MAX_OSPATH];
+
+// 
+// find a file name to save it to 
+// 
+	strcpy(pcxname,"quake00.pcx");
+		
+	for (i=0 ; i<=99 ; i++) 
+	{ 
+		pcxname[5] = i/10 + '0'; 
+		pcxname[6] = i%10 + '0'; 
+		sprintf (checkname, "%s/%s", com_gamedir, pcxname);
+		if (Sys_FileTime(checkname) == -1)
+			break;	// file doesn't exist
+	} 
+	if (i==100) 
+	{
+		Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); 
+		return;
+ 	}
+
+// 
+// save the pcx file 
+// 
+	D_EnableBackBufferAccess ();	// enable direct drawing of console to back
+									//  buffer
+
+	WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes,
+				  host_basepal);
+
+	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
+									//  for linear writes all the time
+
+	Con_Printf ("Wrote %s\n", pcxname);
+} 
+
+
+//=============================================================================
+
+
+/*
+===============
+SCR_BeginLoadingPlaque
+
+================
+*/
+void SCR_BeginLoadingPlaque (void)
+{
+	S_StopAllSounds (true);
+
+	if (cls.state != ca_connected)
+		return;
+	if (cls.signon != SIGNONS)
+		return;
+	
+// redraw with no console and the loading plaque
+	Con_ClearNotify ();
+	scr_centertime_off = 0;
+	scr_con_current = 0;
+
+	scr_drawloading = true;
+	scr_fullupdate = 0;
+	Sbar_Changed ();
+	SCR_UpdateScreen ();
+	scr_drawloading = false;
+
+	scr_disabled_for_loading = true;
+	scr_disabled_time = realtime;
+	scr_fullupdate = 0;
+}
+
+/*
+===============
+SCR_EndLoadingPlaque
+
+================
+*/
+void SCR_EndLoadingPlaque (void)
+{
+	scr_disabled_for_loading = false;
+	scr_fullupdate = 0;
+	Con_ClearNotify ();
+}
+
+//=============================================================================
+
+char	*scr_notifystring;
+qboolean	scr_drawdialog;
+
+void SCR_DrawNotifyString (void)
+{
+	char	*start;
+	int		l;
+	int		j;
+	int		x, y;
+
+	start = scr_notifystring;
+
+	y = vid.height*0.35;
+
+	do	
+	{
+	// scan the width of the line
+		for (l=0 ; l<40 ; l++)
+			if (start[l] == '\n' || !start[l])
+				break;
+		x = (vid.width - l*8)/2;
+		for (j=0 ; j<l ; j++, x+=8)
+			Draw_Character (x, y, start[j]);	
+			
+		y += 8;
+
+		while (*start && *start != '\n')
+			start++;
+
+		if (!*start)
+			break;
+		start++;		// skip the \n
+	} while (1);
+}
+
+/*
+==================
+SCR_ModalMessage
+
+Displays a text string in the center of the screen and waits for a Y or N
+keypress.  
+==================
+*/
+int SCR_ModalMessage (char *text)
+{
+	if (cls.state == ca_dedicated)
+		return true;
+
+	scr_notifystring = text;
+ 
+// draw a fresh screen
+	scr_fullupdate = 0;
+	scr_drawdialog = true;
+	SCR_UpdateScreen ();
+	scr_drawdialog = false;
+	
+	S_ClearBuffer ();		// so dma doesn't loop current sound
+
+        // clear
+        key_lastpress = 0;
+        
+	do
+	{
+		key_count = -1;		// wait for a key down and up
+		Sys_SendKeyEvents ();
+	} while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE && key_lastpress != K_ENTER);
+
+	scr_fullupdate = 0;
+	SCR_UpdateScreen ();
+
+	return key_lastpress == K_ENTER;
+}
+
+
+//=============================================================================
+
+/*
+===============
+SCR_BringDownConsole
+
+Brings the console down and fades the palettes back to normal
+================
+*/
+void SCR_BringDownConsole (void)
+{
+	int		i;
+	
+	scr_centertime_off = 0;
+	
+	for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
+		SCR_UpdateScreen ();
+
+	cl.cshifts[0].percent = 0;		// no area contents palette on next frame
+	VID_SetPalette (host_basepal);
+}
+
+
+/*
+==================
+SCR_UpdateScreen
+
+This is called every frame, and can also be called explicitly to flush
+text to the screen.
+
+WARNING: be very careful calling this from elsewhere, because the refresh
+needs almost the entire 256k of stack space!
+==================
+*/
+void SCR_UpdateScreen (void)
+{
+	static float	oldscr_viewsize;
+	static float	oldlcd_x;
+	vrect_t		vrect;
+	
+	if (scr_skipupdate || block_drawing)
+		return;
+
+	scr_copytop = 0;
+	scr_copyeverything = 0;
+
+	if (scr_disabled_for_loading)
+	{
+		if (realtime - scr_disabled_time > 60)
+		{
+			scr_disabled_for_loading = false;
+			Con_Printf ("load failed.\n");
+		}
+		else
+			return;
+	}
+
+	if (cls.state == ca_dedicated)
+		return;				// stdout only
+
+	if (!scr_initialized || !con_initialized)
+		return;				// not initialized yet
+
+	if (scr_viewsize.value != oldscr_viewsize)
+	{
+		oldscr_viewsize = scr_viewsize.value;
+		vid.recalc_refdef = 1;
+	}
+	
+//
+// check for vid changes
+//
+	if (oldfov != scr_fov.value)
+	{
+		oldfov = scr_fov.value;
+		vid.recalc_refdef = true;
+	}
+	
+	if (oldlcd_x != lcd_x.value)
+	{
+		oldlcd_x = lcd_x.value;
+		vid.recalc_refdef = true;
+	}
+	
+	if (oldscreensize != scr_viewsize.value)
+	{
+		oldscreensize = scr_viewsize.value;
+		vid.recalc_refdef = true;
+	}
+	
+	if (vid.recalc_refdef)
+	{
+	// something changed, so reorder the screen
+		SCR_CalcRefdef ();
+	}
+
+//
+// do 3D refresh drawing, and then update the screen
+//
+	D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
+
+	if (scr_fullupdate++ < vid.numpages)
+	{	// clear the entire screen
+		scr_copyeverything = 1;
+		Draw_TileClear (0,0,vid.width,vid.height);
+		Sbar_Changed ();
+	}
+
+	pconupdate = NULL;
+
+
+	SCR_SetUpToDrawConsole ();
+	SCR_EraseCenterString ();
+
+	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
+									//  for linear writes all the time
+
+	VID_LockBuffer ();
+
+	V_RenderView ();
+
+	VID_UnlockBuffer ();
+
+	D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
+
+	if (scr_drawdialog)
+	{
+		Sbar_Draw ();
+		Draw_FadeScreen ();
+		SCR_DrawNotifyString ();
+		scr_copyeverything = true;
+	}
+	else if (scr_drawloading)
+	{
+		SCR_DrawLoading ();
+		Sbar_Draw ();
+	}
+	else if (cl.intermission == 1 && key_dest == key_game)
+	{
+		Sbar_IntermissionOverlay ();
+	}
+	else if (cl.intermission == 2 && key_dest == key_game)
+	{
+		Sbar_FinaleOverlay ();
+		SCR_CheckDrawCenterString ();
+	}
+	else if (cl.intermission == 3 && key_dest == key_game)
+	{
+		SCR_CheckDrawCenterString ();
+	}
+	else
+	{
+		SCR_DrawRam ();
+		SCR_DrawNet ();
+		SCR_DrawTurtle ();
+		SCR_DrawPause ();
+		SCR_CheckDrawCenterString ();
+		Sbar_Draw ();
+		SCR_DrawConsole ();
+		M_Draw ();
+	}
+
+	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
+									//  for linear writes all the time
+	if (pconupdate)
+	{
+		D_UpdateRects (pconupdate);
+	}
+
+	V_UpdatePalette ();
+
+//
+// update one of three areas
+//
+
+	if (scr_copyeverything)
+	{
+		vrect.x = 0;
+		vrect.y = 0;
+		vrect.width = vid.width;
+		vrect.height = vid.height;
+		vrect.pnext = 0;
+	
+		VID_Update (&vrect);
+	}
+	else if (scr_copytop)
+	{
+		vrect.x = 0;
+		vrect.y = 0;
+		vrect.width = vid.width;
+		vrect.height = vid.height - sb_lines;
+		vrect.pnext = 0;
+	
+		VID_Update (&vrect);
+	}	
+	else
+	{
+		vrect.x = scr_vrect.x;
+		vrect.y = scr_vrect.y;
+		vrect.width = scr_vrect.width;
+		vrect.height = scr_vrect.height;
+		vrect.pnext = 0;
+	
+		VID_Update (&vrect);
+	}
+}
+
+
+/*
+==================
+SCR_UpdateWholeScreen
+==================
+*/
+void SCR_UpdateWholeScreen (void)
+{
+	scr_fullupdate = 0;
+	SCR_UpdateScreen ();
+}
diff --git a/apps/plugins/sdl/progs/quake/screen.h b/apps/plugins/sdl/progs/quake/screen.h
new file mode 100644
index 0000000..845ab55
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/screen.h
@@ -0,0 +1,57 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// screen.h
+
+void SCR_Init (void);
+
+void SCR_UpdateScreen (void);
+
+
+void SCR_SizeUp (void);
+void SCR_SizeDown (void);
+void SCR_BringDownConsole (void);
+void SCR_CenterPrint (char *str);
+
+void SCR_BeginLoadingPlaque (void);
+void SCR_EndLoadingPlaque (void);
+
+int SCR_ModalMessage (char *text);
+
+extern	float		scr_con_current;
+extern	float		scr_conlines;		// lines of console to display
+
+extern	int			scr_fullupdate;	// set to 0 to force full redraw
+extern	int			sb_lines;
+
+extern	int			clearnotify;	// set to 0 whenever notify text is drawn
+extern	qboolean	scr_disabled_for_loading;
+extern	qboolean	scr_skipupdate;
+
+extern	cvar_t		scr_viewsize;
+
+extern cvar_t scr_viewsize;
+
+// only the refresh window will be updated unless these variables are flagged 
+extern	int			scr_copytop;
+extern	int			scr_copyeverything;
+
+extern qboolean		block_drawing;
+
+void SCR_UpdateWholeScreen (void);
diff --git a/apps/plugins/sdl/progs/quake/server.h b/apps/plugins/sdl/progs/quake/server.h
new file mode 100644
index 0000000..5fe33de
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/server.h
@@ -0,0 +1,258 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// server.h
+
+typedef struct
+{
+	int			maxclients;
+	int			maxclientslimit;
+	struct client_s	*clients;		// [maxclients]
+	int			serverflags;		// episode completion information
+	qboolean	changelevel_issued;	// cleared when at SV_SpawnServer
+} server_static_t;
+
+//=============================================================================
+
+typedef int server_state_t;
+enum {ss_loading, ss_active};
+
+typedef struct
+{
+	qboolean	active;				// false if only a net client
+
+	qboolean	paused;
+	qboolean	loadgame;			// handle connections specially
+
+	double		time;
+	
+	int			lastcheck;			// used by PF_checkclient
+	double		lastchecktime;
+	
+	char		name[64];			// map name
+#ifdef QUAKE2
+	char		startspot[64];
+#endif
+	char		modelname[64];		// maps/<name>.bsp, for model_precache[0]
+	struct model_s 	*worldmodel;
+	char		*model_precache[MAX_MODELS];	// NULL terminated
+	struct model_s	*models[MAX_MODELS];
+	char		*sound_precache[MAX_SOUNDS];	// NULL terminated
+	char		*lightstyles[MAX_LIGHTSTYLES];
+	int			num_edicts;
+	int			max_edicts;
+	edict_t		*edicts;			// can NOT be array indexed, because
+									// edict_t is variable sized, but can
+									// be used to reference the world ent
+	server_state_t	state;			// some actions are only valid during load
+
+	sizebuf_t	datagram;
+	byte		datagram_buf[MAX_DATAGRAM];
+
+	sizebuf_t	reliable_datagram;	// copied to all clients at end of frame
+	byte		reliable_datagram_buf[MAX_DATAGRAM];
+
+	sizebuf_t	signon;
+	byte		signon_buf[8192];
+} server_t;
+
+
+#define	NUM_PING_TIMES		16
+#define	NUM_SPAWN_PARMS		16
+
+typedef struct client_s
+{
+	qboolean		active;				// false = client is free
+	qboolean		spawned;			// false = don't send datagrams
+	qboolean		dropasap;			// has been told to go to another level
+	qboolean		privileged;			// can execute any host command
+	qboolean		sendsignon;			// only valid before spawned
+
+	double			last_message;		// reliable messages must be sent
+										// periodically
+
+	struct qsocket_s *netconnection;	// communications handle
+
+	usercmd_t		cmd;				// movement
+	vec3_t			wishdir;			// intended motion calced from cmd
+
+	sizebuf_t		message;			// can be added to at any time,
+										// copied and clear once per frame
+	byte			msgbuf[MAX_MSGLEN];
+	edict_t			*edict;				// EDICT_NUM(clientnum+1)
+	char			name[32];			// for printing to other people
+	int				colors;
+		
+	float			ping_times[NUM_PING_TIMES];
+	int				num_pings;			// ping_times[num_pings%NUM_PING_TIMES]
+
+// spawn parms are carried from level to level
+	float			spawn_parms[NUM_SPAWN_PARMS];
+
+// client known data for deltas	
+	int				old_frags;
+} client_t;
+
+
+//=============================================================================
+
+// edict->movetype values
+#define	MOVETYPE_NONE			0		// never moves
+#define	MOVETYPE_ANGLENOCLIP	1
+#define	MOVETYPE_ANGLECLIP		2
+#define	MOVETYPE_WALK			3		// gravity
+#define	MOVETYPE_STEP			4		// gravity, special edge handling
+#define	MOVETYPE_FLY			5
+#define	MOVETYPE_TOSS			6		// gravity
+#define	MOVETYPE_PUSH			7		// no clip to world, push and crush
+#define	MOVETYPE_NOCLIP			8
+#define	MOVETYPE_FLYMISSILE		9		// extra size to monsters
+#define	MOVETYPE_BOUNCE			10
+#ifdef QUAKE2
+#define MOVETYPE_BOUNCEMISSILE	11		// bounce w/o gravity
+#define MOVETYPE_FOLLOW			12		// track movement of aiment
+#endif
+
+// edict->solid values
+#define	SOLID_NOT				0		// no interaction with other objects
+#define	SOLID_TRIGGER			1		// touch on edge, but not blocking
+#define	SOLID_BBOX				2		// touch on edge, block
+#define	SOLID_SLIDEBOX			3		// touch on edge, but not an onground
+#define	SOLID_BSP				4		// bsp clip, touch on edge, block
+
+// edict->deadflag values
+#define	DEAD_NO					0
+#define	DEAD_DYING				1
+#define	DEAD_DEAD				2
+
+#define	DAMAGE_NO				0
+#define	DAMAGE_YES				1
+#define	DAMAGE_AIM				2
+
+// edict->flags
+#define	FL_FLY					1
+#define	FL_SWIM					2
+//#define	FL_GLIMPSE				4
+#define	FL_CONVEYOR				4
+#define	FL_CLIENT				8
+#define	FL_INWATER				16
+#define	FL_MONSTER				32
+#define	FL_GODMODE				64
+#define	FL_NOTARGET				128
+#define	FL_ITEM					256
+#define	FL_ONGROUND				512
+#define	FL_PARTIALGROUND		1024	// not all corners are valid
+#define	FL_WATERJUMP			2048	// player jumping out of water
+#define	FL_JUMPRELEASED			4096	// for jump debouncing
+#ifdef QUAKE2
+#define FL_FLASHLIGHT			8192
+#define FL_ARCHIVE_OVERRIDE		1048576
+#endif
+
+// entity effects
+
+#define	EF_BRIGHTFIELD			1
+#define	EF_MUZZLEFLASH 			2
+#define	EF_BRIGHTLIGHT 			4
+#define	EF_DIMLIGHT 			8
+#ifdef QUAKE2
+#define EF_DARKLIGHT			16
+#define EF_DARKFIELD			32
+#define EF_LIGHT				64
+#define EF_NODRAW				128
+#endif
+
+#define	SPAWNFLAG_NOT_EASY			256
+#define	SPAWNFLAG_NOT_MEDIUM		512
+#define	SPAWNFLAG_NOT_HARD			1024
+#define	SPAWNFLAG_NOT_DEATHMATCH	2048
+
+#ifdef QUAKE2
+// server flags
+#define	SFL_EPISODE_1		1
+#define	SFL_EPISODE_2		2
+#define	SFL_EPISODE_3		4
+#define	SFL_EPISODE_4		8
+#define	SFL_NEW_UNIT		16
+#define	SFL_NEW_EPISODE		32
+#define	SFL_CROSS_TRIGGERS	65280
+#endif
+
+//============================================================================
+
+extern	cvar_t	teamplay;
+extern	cvar_t	skill;
+extern	cvar_t	deathmatch;
+extern	cvar_t	coop;
+extern	cvar_t	fraglimit;
+extern	cvar_t	timelimit;
+
+extern	server_static_t	svs;				// persistant server info
+extern	server_t		sv;					// local server
+
+extern	client_t	*host_client;
+
+extern	jmp_buf 	host_abortserver;
+
+extern	double		host_time;
+
+extern	edict_t		*sv_player;
+
+//===========================================================
+
+void SV_Init (void);
+
+void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count);
+void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
+    float attenuation);
+
+void SV_DropClient (qboolean crash);
+
+void SV_SendClientMessages (void);
+void SV_ClearDatagram (void);
+
+int SV_ModelIndex (char *name);
+
+void SV_SetIdealPitch (void);
+
+void SV_AddUpdates (void);
+
+void SV_ClientThink (void);
+void SV_AddClientToServer (struct qsocket_s	*ret);
+
+void SV_ClientPrintf (char *fmt, ...);
+void SV_BroadcastPrintf (char *fmt, ...);
+
+void SV_Physics (void);
+
+qboolean SV_CheckBottom (edict_t *ent);
+qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink);
+
+void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg);
+
+void SV_MoveToGoal (void);
+
+void SV_CheckForNewClients (void);
+void SV_RunClients (void);
+void SV_SaveSpawnparms ();
+#ifdef QUAKE2
+void SV_SpawnServer (char *server, char *startspot);
+#else
+void SV_SpawnServer (char *server);
+#endif
diff --git a/apps/plugins/sdl/progs/quake/snd_dma.c b/apps/plugins/sdl/progs/quake/snd_dma.c
new file mode 100644
index 0000000..4c42f32
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_dma.c
@@ -0,0 +1,1021 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_dma.c -- main control for any streaming sound output device
+
+#include "quakedef.h"
+
+#ifdef _WIN32
+#include "winquake.h"
+#endif
+
+void S_Play(void);
+void S_PlayVol(void);
+void S_SoundList(void);
+void S_Update_();
+void S_StopAllSounds(qboolean clear);
+void S_StopAllSoundsC(void);
+
+// =======================================================================
+// Internal sound data & structures
+// =======================================================================
+
+channel_t   channels[MAX_CHANNELS];
+int			total_channels;
+
+int				snd_blocked = 0;
+static qboolean	snd_ambient = 1;
+qboolean		snd_initialized = false;
+
+// pointer should go away
+volatile dma_t  *shm = 0;
+volatile dma_t sn;
+
+vec3_t		listener_origin;
+vec3_t		listener_forward;
+vec3_t		listener_right;
+vec3_t		listener_up;
+vec_t		sound_nominal_clip_dist=1000.0;
+
+int			soundtime;		// sample PAIRS
+int   		paintedtime; 	// sample PAIRS
+
+
+#define	MAX_SFX		512
+sfx_t		*known_sfx;		// hunk allocated [MAX_SFX]
+int			num_sfx;
+
+sfx_t		*ambient_sfx[NUM_AMBIENTS];
+
+// lowest rockbox supported
+int 		desired_speed = SAMPR_16;
+int 		desired_bits = 16;
+
+int sound_started=0;
+
+cvar_t bgmvolume = {"bgmvolume", "1", true};
+cvar_t volume = {"volume", "0.7", true};
+
+cvar_t nosound = {"nosound", "0"};
+cvar_t precache = {"precache", "1"};
+cvar_t loadas8bit = {"loadas8bit", "0"};
+cvar_t bgmbuffer = {"bgmbuffer", "4096"};
+cvar_t ambient_level = {"ambient_level", "0.3"};
+cvar_t ambient_fade = {"ambient_fade", "100"};
+cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"};
+cvar_t snd_show = {"snd_show", "0"};
+cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", true};
+
+
+// ====================================================================
+// User-setable variables
+// ====================================================================
+
+
+//
+// Fake dma is a synchronous faking of the DMA progress used for
+// isolating performance in the renderer.  The fakedma_updates is
+// number of times S_Update() is called per second.
+//
+
+qboolean fakedma = false;
+int fakedma_updates = 15;
+
+
+void S_AmbientOff (void)
+{
+	snd_ambient = false;
+}
+
+
+void S_AmbientOn (void)
+{
+	snd_ambient = true;
+}
+
+
+void S_SoundInfo_f(void)
+{
+	if (!sound_started || !shm)
+	{
+		Con_Printf ("sound system not started\n");
+		return;
+	}
+	
+    Con_Printf("%5d stereo\n", shm->channels - 1);
+    Con_Printf("%5d samples\n", shm->samples);
+    Con_Printf("%5d samplepos\n", shm->samplepos);
+    Con_Printf("%5d samplebits\n", shm->samplebits);
+    Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
+    Con_Printf("%5d speed\n", shm->speed);
+    Con_Printf("0x%x dma buffer\n", shm->buffer);
+	Con_Printf("%5d total_channels\n", total_channels);
+}
+
+
+/*
+================
+S_Startup
+================
+*/
+
+void S_Startup (void)
+{
+	int		rc;
+
+	if (!snd_initialized)
+		return;
+
+	if (!fakedma)
+	{
+		rc = SNDDMA_Init();
+
+		if (!rc)
+		{
+#ifndef	_WIN32
+			Con_Printf("S_Startup: SNDDMA_Init failed.\n");
+#endif
+			sound_started = 0;
+			return;
+		}
+	}
+
+	sound_started = 1;
+}
+
+
+/*
+================
+S_Init
+================
+*/
+void S_Init (void)
+{
+
+	Con_Printf("\nSound Initialization\n");
+
+	if (COM_CheckParm("-nosound"))
+		return;
+
+	if (COM_CheckParm("-simsound"))
+		fakedma = true;
+
+	Cmd_AddCommand("play", S_Play);
+	Cmd_AddCommand("playvol", S_PlayVol);
+	Cmd_AddCommand("stopsound", S_StopAllSoundsC);
+	Cmd_AddCommand("soundlist", S_SoundList);
+	Cmd_AddCommand("soundinfo", S_SoundInfo_f);
+
+	Cvar_RegisterVariable(&nosound);
+	Cvar_RegisterVariable(&volume);
+	Cvar_RegisterVariable(&precache);
+	Cvar_RegisterVariable(&loadas8bit);
+	Cvar_RegisterVariable(&bgmvolume);
+	Cvar_RegisterVariable(&bgmbuffer);
+	Cvar_RegisterVariable(&ambient_level);
+	Cvar_RegisterVariable(&ambient_fade);
+	Cvar_RegisterVariable(&snd_noextraupdate);
+	Cvar_RegisterVariable(&snd_show);
+	Cvar_RegisterVariable(&_snd_mixahead);
+
+	if (host_parms.memsize < 0x800000)
+	{
+		Cvar_Set ("loadas8bit", "1");
+		Con_Printf ("loading all sounds as 8bit\n");
+	}
+
+
+
+	snd_initialized = true;
+
+	S_Startup ();
+
+	known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t");
+	num_sfx = 0;
+
+// create a piece of DMA memory
+
+	if (fakedma)
+	{
+		shm = (void *) Hunk_AllocName(sizeof(*shm), "shm");
+		shm->splitbuffer = 0;
+		shm->samplebits = 16;
+		shm->speed = 22050;
+		shm->channels = 2;
+		shm->samples = 32768;
+		shm->samplepos = 0;
+		shm->soundalive = true;
+		shm->gamealive = true;
+		shm->submission_chunk = 1;
+		shm->buffer = Hunk_AllocName(1<<16, "shmbuf");
+	}
+
+	if ( shm ) {
+		Con_Printf ("Sound sampling rate: %i\n", shm->speed);
+	}
+
+	// provides a tick sound until washed clean
+
+//	if (shm->buffer)
+//		shm->buffer[4] = shm->buffer[5] = 0x7f;	// force a pop for debugging
+
+	ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav");
+	ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav");
+
+	S_StopAllSounds (true);
+}
+
+
+// =======================================================================
+// Shutdown sound engine
+// =======================================================================
+
+void S_Shutdown(void)
+{
+
+	if (!sound_started)
+		return;
+
+	if (shm)
+		shm->gamealive = 0;
+
+	shm = 0;
+	sound_started = 0;
+
+	if (!fakedma)
+	{
+		SNDDMA_Shutdown();
+	}
+}
+
+
+// =======================================================================
+// Load a sound
+// =======================================================================
+
+/*
+==================
+S_FindName
+
+==================
+*/
+sfx_t *S_FindName (char *name)
+{
+	int		i;
+	sfx_t	*sfx;
+
+	if (!name)
+		Sys_Error ("S_FindName: NULL\n");
+
+	if (Q_strlen(name) >= MAX_QPATH)
+		Sys_Error ("Sound name too long: %s", name);
+
+// see if already loaded
+	for (i=0 ; i < num_sfx ; i++)
+		if (!Q_strcmp(known_sfx[i].name, name))
+		{
+			return &known_sfx[i];
+		}
+
+	if (num_sfx == MAX_SFX)
+		Sys_Error ("S_FindName: out of sfx_t");
+	
+	sfx = &known_sfx[i];
+	strcpy (sfx->name, name);
+
+	num_sfx++;
+	
+	return sfx;
+}
+
+
+/*
+==================
+S_TouchSound
+
+==================
+*/
+void S_TouchSound (char *name)
+{
+	sfx_t	*sfx;
+	
+	if (!sound_started)
+		return;
+
+	sfx = S_FindName (name);
+	Cache_Check (&sfx->cache);
+}
+
+/*
+==================
+S_PrecacheSound
+
+==================
+*/
+sfx_t *S_PrecacheSound (char *name)
+{
+	sfx_t	*sfx;
+
+	if (!sound_started || nosound.value)
+		return NULL;
+
+	sfx = S_FindName (name);
+	
+// cache it in
+	if (precache.value)
+		S_LoadSound (sfx);
+	
+	return sfx;
+}
+
+
+//=============================================================================
+
+/*
+=================
+SND_PickChannel
+=================
+*/
+channel_t *SND_PickChannel(int entnum, int entchannel)
+{
+    int ch_idx;
+    int first_to_die;
+    int life_left;
+
+// Check for replacement sound, or find the best one to replace
+    first_to_die = -1;
+    life_left = 0x7fffffff;
+    for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
+    {
+		if (entchannel != 0		// channel 0 never overrides
+		&& channels[ch_idx].entnum == entnum
+		&& (channels[ch_idx].entchannel == entchannel || entchannel == -1) )
+		{	// allways override sound from same entity
+			first_to_die = ch_idx;
+			break;
+		}
+
+		// don't let monster sounds override player sounds
+		if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx)
+			continue;
+
+		if (channels[ch_idx].end - paintedtime < life_left)
+		{
+			life_left = channels[ch_idx].end - paintedtime;
+			first_to_die = ch_idx;
+		}
+   }
+
+	if (first_to_die == -1)
+		return NULL;
+
+	if (channels[first_to_die].sfx)
+		channels[first_to_die].sfx = NULL;
+
+    return &channels[first_to_die];    
+}       
+
+/*
+=================
+SND_Spatialize
+=================
+*/
+void SND_Spatialize(channel_t *ch)
+{
+    vec_t dot;
+    vec_t ldist, rdist, dist;
+    vec_t lscale, rscale, scale;
+    vec3_t source_vec;
+	sfx_t *snd;
+
+// anything coming from the view entity will allways be full volume
+	if (ch->entnum == cl.viewentity)
+	{
+		ch->leftvol = ch->master_vol;
+		ch->rightvol = ch->master_vol;
+		return;
+	}
+
+// calculate stereo seperation and distance attenuation
+
+	snd = ch->sfx;
+	VectorSubtract(ch->origin, listener_origin, source_vec);
+	
+	dist = VectorNormalize(source_vec) * ch->dist_mult;
+	
+	dot = DotProduct(listener_right, source_vec);
+
+	if (shm->channels == 1)
+	{
+		rscale = 1.0;
+		lscale = 1.0;
+	}
+	else
+	{
+		rscale = 1.0 + dot;
+		lscale = 1.0 - dot;
+	}
+
+// add in distance effect
+	scale = (1.0 - dist) * rscale;
+	ch->rightvol = (int) (ch->master_vol * scale);
+	if (ch->rightvol < 0)
+		ch->rightvol = 0;
+
+	scale = (1.0 - dist) * lscale;
+	ch->leftvol = (int) (ch->master_vol * scale);
+	if (ch->leftvol < 0)
+		ch->leftvol = 0;
+}           
+
+
+// =======================================================================
+// Start a sound effect
+// =======================================================================
+
+void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
+{
+	channel_t *target_chan, *check;
+	sfxcache_t	*sc;
+	int		vol;
+	int		ch_idx;
+	int		skip;
+
+	if (!sound_started)
+		return;
+
+	if (!sfx)
+		return;
+
+	if (nosound.value)
+		return;
+
+	vol = fvol*255;
+
+// pick a channel to play on
+	target_chan = SND_PickChannel(entnum, entchannel);
+	if (!target_chan)
+		return;
+		
+// spatialize
+	memset (target_chan, 0, sizeof(*target_chan));
+	VectorCopy(origin, target_chan->origin);
+	target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
+	target_chan->master_vol = vol;
+	target_chan->entnum = entnum;
+	target_chan->entchannel = entchannel;
+	SND_Spatialize(target_chan);
+
+	if (!target_chan->leftvol && !target_chan->rightvol)
+		return;		// not audible at all
+
+// new channel
+	sc = S_LoadSound (sfx);
+	if (!sc)
+	{
+		target_chan->sfx = NULL;
+		return;		// couldn't load the sound's data
+	}
+
+	target_chan->sfx = sfx;
+	target_chan->pos = 0.0;
+    target_chan->end = paintedtime + sc->length;	
+
+// if an identical sound has also been started this frame, offset the pos
+// a bit to keep it from just making the first one louder
+	check = &channels[NUM_AMBIENTS];
+    for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++)
+    {
+		if (check == target_chan)
+			continue;
+		if (check->sfx == sfx && !check->pos)
+		{
+			skip = rand () % (int)(0.1*shm->speed);
+			if (skip >= target_chan->end)
+				skip = target_chan->end - 1;
+			target_chan->pos += skip;
+			target_chan->end -= skip;
+			break;
+		}
+		
+	}
+}
+
+void S_StopSound(int entnum, int entchannel)
+{
+	int i;
+
+	for (i=0 ; i<MAX_DYNAMIC_CHANNELS ; i++)
+	{
+		if (channels[i].entnum == entnum
+			&& channels[i].entchannel == entchannel)
+		{
+			channels[i].end = 0;
+			channels[i].sfx = NULL;
+			return;
+		}
+	}
+}
+
+void S_StopAllSounds(qboolean clear)
+{
+	int		i;
+
+	if (!sound_started)
+		return;
+
+	total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;	// no statics
+
+	for (i=0 ; i<MAX_CHANNELS ; i++)
+		if (channels[i].sfx)
+			channels[i].sfx = NULL;
+
+	Q_memset(channels, 0, MAX_CHANNELS * sizeof(channel_t));
+
+	if (clear)
+		S_ClearBuffer ();
+}
+
+void S_StopAllSoundsC (void)
+{
+	S_StopAllSounds (true);
+}
+
+void S_ClearBuffer (void)
+{
+	int		clear;
+		
+#ifdef _WIN32
+	if (!sound_started || !shm || (!shm->buffer && !pDSBuf))
+#else
+	if (!sound_started || !shm || !shm->buffer)
+#endif
+		return;
+
+	if (shm->samplebits == 8)
+		clear = 0x80;
+	else
+		clear = 0;
+
+#ifdef _WIN32
+	if (pDSBuf)
+	{
+		DWORD	dwSize;
+		DWORD	*pData;
+		int		reps;
+		HRESULT	hresult;
+
+		reps = 0;
+
+		while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK)
+		{
+			if (hresult != DSERR_BUFFERLOST)
+			{
+				Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n");
+				S_Shutdown ();
+				return;
+			}
+
+			if (++reps > 10000)
+			{
+				Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n");
+				S_Shutdown ();
+				return;
+			}
+		}
+
+		Q_memset(pData, clear, shm->samples * shm->samplebits/8);
+
+		pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0);
+	
+	}
+	else
+#endif
+	{
+		Q_memset(shm->buffer, clear, shm->samples * shm->samplebits/8);
+	}
+}
+
+
+/*
+=================
+S_StaticSound
+=================
+*/
+void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
+{
+	channel_t	*ss;
+	sfxcache_t		*sc;
+
+	if (!sfx)
+		return;
+
+	if (total_channels == MAX_CHANNELS)
+	{
+		Con_Printf ("total_channels == MAX_CHANNELS\n");
+		return;
+	}
+
+	ss = &channels[total_channels];
+	total_channels++;
+
+	sc = S_LoadSound (sfx);
+	if (!sc)
+		return;
+
+	if (sc->loopstart == -1)
+	{
+		Con_Printf ("Sound %s not looped\n", sfx->name);
+		return;
+	}
+	
+	ss->sfx = sfx;
+	VectorCopy (origin, ss->origin);
+	ss->master_vol = vol;
+	ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist;
+    ss->end = paintedtime + sc->length;	
+	
+	SND_Spatialize (ss);
+}
+
+
+//=============================================================================
+
+/*
+===================
+S_UpdateAmbientSounds
+===================
+*/
+void S_UpdateAmbientSounds (void)
+{
+	mleaf_t		*l;
+	float		vol;
+	int			ambient_channel;
+	channel_t	*chan;
+
+	if (!snd_ambient)
+		return;
+
+// calc ambient sound levels
+	if (!cl.worldmodel)
+		return;
+
+	l = Mod_PointInLeaf (listener_origin, cl.worldmodel);
+	if (!l || !ambient_level.value)
+	{
+		for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
+			channels[ambient_channel].sfx = NULL;
+		return;
+	}
+
+	for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
+	{
+		chan = &channels[ambient_channel];	
+		chan->sfx = ambient_sfx[ambient_channel];
+	
+		vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
+		if (vol < 8)
+			vol = 0;
+
+	// don't adjust volume too fast
+		if (chan->master_vol < vol)
+		{
+			chan->master_vol += host_frametime * ambient_fade.value;
+			if (chan->master_vol > vol)
+				chan->master_vol = vol;
+		}
+		else if (chan->master_vol > vol)
+		{
+			chan->master_vol -= host_frametime * ambient_fade.value;
+			if (chan->master_vol < vol)
+				chan->master_vol = vol;
+		}
+		
+		chan->leftvol = chan->rightvol = chan->master_vol;
+	}
+}
+
+
+/*
+============
+S_Update
+
+Called once each time through the main loop
+============
+*/
+void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
+{
+	int			i, j;
+	int			total;
+	channel_t	*ch;
+	channel_t	*combine;
+
+	if (!sound_started || (snd_blocked > 0))
+		return;
+
+	VectorCopy(origin, listener_origin);
+	VectorCopy(forward, listener_forward);
+	VectorCopy(right, listener_right);
+	VectorCopy(up, listener_up);
+	
+// update general area ambient sound sources
+	S_UpdateAmbientSounds ();
+
+	combine = NULL;
+
+// update spatialization for static and dynamic sounds	
+	ch = channels+NUM_AMBIENTS;
+	for (i=NUM_AMBIENTS ; i<total_channels; i++, ch++)
+	{
+		if (!ch->sfx)
+			continue;
+		SND_Spatialize(ch);         // respatialize channel
+		if (!ch->leftvol && !ch->rightvol)
+			continue;
+
+	// try to combine static sounds with a previous channel of the same
+	// sound effect so we don't mix five torches every frame
+	
+		if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
+		{
+		// see if it can just use the last one
+			if (combine && combine->sfx == ch->sfx)
+			{
+				combine->leftvol += ch->leftvol;
+				combine->rightvol += ch->rightvol;
+				ch->leftvol = ch->rightvol = 0;
+				continue;
+			}
+		// search for one
+			combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
+			for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
+				if (combine->sfx == ch->sfx)
+					break;
+					
+			if (j == total_channels)
+			{
+				combine = NULL;
+			}
+			else
+			{
+				if (combine != ch)
+				{
+					combine->leftvol += ch->leftvol;
+					combine->rightvol += ch->rightvol;
+					ch->leftvol = ch->rightvol = 0;
+				}
+				continue;
+			}
+		}
+		
+		
+	}
+
+//
+// debugging output
+//
+	if (snd_show.value)
+	{
+		total = 0;
+		ch = channels;
+		for (i=0 ; i<total_channels; i++, ch++)
+			if (ch->sfx && (ch->leftvol || ch->rightvol) )
+			{
+				//Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
+				total++;
+			}
+		
+		Con_Printf ("----(%i)----\n", total);
+	}
+
+// mix some sound
+	S_Update_();
+}
+
+void GetSoundtime(void)
+{
+	int		samplepos;
+	static	int		buffers;
+	static	int		oldsamplepos;
+	int		fullsamples;
+	
+	fullsamples = shm->samples / shm->channels;
+
+// it is possible to miscount buffers if it has wrapped twice between
+// calls to S_Update.  Oh well.
+#ifdef __sun__
+	soundtime = SNDDMA_GetSamples();
+#else
+	samplepos = SNDDMA_GetDMAPos();
+
+
+	if (samplepos < oldsamplepos)
+	{
+		buffers++;					// buffer wrapped
+		
+		if (paintedtime > 0x40000000)
+		{	// time to chop things off to avoid 32 bit limits
+			buffers = 0;
+			paintedtime = fullsamples;
+			S_StopAllSounds (true);
+		}
+	}
+	oldsamplepos = samplepos;
+
+	soundtime = buffers*fullsamples + samplepos/shm->channels;
+#endif
+}
+
+void S_ExtraUpdate (void)
+{
+
+#ifdef _WIN32
+	IN_Accumulate ();
+#endif
+
+	if (snd_noextraupdate.value)
+		return;		// don't pollute timings
+	S_Update_();
+}
+
+void S_Update_(void)
+{
+#ifndef SDL
+
+	unsigned        endtime;
+	int				samps;
+	
+	if (!sound_started || (snd_blocked > 0))
+		return;
+
+// Updates DMA time
+	GetSoundtime();
+
+// check to make sure that we haven't overshot
+	if (paintedtime < soundtime)
+	{
+		//Con_Printf ("S_Update_ : overflow\n");
+		paintedtime = soundtime;
+	}
+
+// mix ahead of current position
+	endtime = soundtime + _snd_mixahead.value * shm->speed;
+	samps = shm->samples >> (shm->channels-1);
+	if (endtime - soundtime > samps)
+		endtime = soundtime + samps;
+
+#ifdef _WIN32
+// if the buffer was lost or stopped, restore it and/or restart it
+	{
+		DWORD	dwStatus;
+
+		if (pDSBuf)
+		{
+			if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK)
+				Con_Printf ("Couldn't get sound buffer status\n");
+			
+			if (dwStatus & DSBSTATUS_BUFFERLOST)
+				pDSBuf->lpVtbl->Restore (pDSBuf);
+			
+			if (!(dwStatus & DSBSTATUS_PLAYING))
+				pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
+		}
+	}
+#endif
+
+	S_PaintChannels (endtime);
+
+	SNDDMA_Submit ();
+#endif /* ! SDL */
+}
+
+/*
+===============================================================================
+
+console functions
+
+===============================================================================
+*/
+
+void S_Play(void)
+{
+	static int hash=345;
+	int 	i;
+	char name[256];
+	sfx_t	*sfx;
+	
+	i = 1;
+	while (i<Cmd_Argc())
+	{
+		if (!Q_strrchr(Cmd_Argv(i), '.'))
+		{
+			Q_strcpy(name, Cmd_Argv(i));
+			Q_strcat(name, ".wav");
+		}
+		else
+			Q_strcpy(name, Cmd_Argv(i));
+		sfx = S_PrecacheSound(name);
+		S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0);
+		i++;
+	}
+}
+
+void S_PlayVol(void)
+{
+	static int hash=543;
+	int i;
+	float vol;
+	char name[256];
+	sfx_t	*sfx;
+	
+	i = 1;
+	while (i<Cmd_Argc())
+	{
+		if (!Q_strrchr(Cmd_Argv(i), '.'))
+		{
+			Q_strcpy(name, Cmd_Argv(i));
+			Q_strcat(name, ".wav");
+		}
+		else
+			Q_strcpy(name, Cmd_Argv(i));
+		sfx = S_PrecacheSound(name);
+		vol = Q_atof(Cmd_Argv(i+1));
+		S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0);
+		i+=2;
+	}
+}
+
+void S_SoundList(void)
+{
+	int		i;
+	sfx_t	*sfx;
+	sfxcache_t	*sc;
+	int		size, total;
+
+	total = 0;
+	for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
+	{
+		sc = Cache_Check (&sfx->cache);
+		if (!sc)
+			continue;
+		size = sc->length*sc->width*(sc->stereo+1);
+		total += size;
+		if (sc->loopstart >= 0)
+			Con_Printf ("L");
+		else
+			Con_Printf (" ");
+		Con_Printf("(%2db) %6i : %s\n",sc->width*8,  size, sfx->name);
+	}
+	Con_Printf ("Total resident: %i\n", total);
+}
+
+
+void S_LocalSound (char *sound)
+{
+	sfx_t	*sfx;
+
+	if (nosound.value)
+		return;
+	if (!sound_started)
+		return;
+		
+	sfx = S_PrecacheSound (sound);
+	if (!sfx)
+	{
+		Con_Printf ("S_LocalSound: can't cache %s\n", sound);
+		return;
+	}
+	S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1);
+}
+
+
+void S_ClearPrecache (void)
+{
+}
+
+
+void S_BeginPrecaching (void)
+{
+}
+
+
+void S_EndPrecaching (void)
+{
+}
+
diff --git a/apps/plugins/sdl/progs/quake/snd_linux.c b/apps/plugins/sdl/progs/quake/snd_linux.c
new file mode 100644
index 0000000..4579e40
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_linux.c
@@ -0,0 +1,269 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <linux/soundcard.h>
+#include <stdio.h>
+#include "quakedef.h"
+
+int audio_fd;
+int snd_inited;
+
+static int tryrates[] = { 11025, 22051, 44100, 8000 };
+
+qboolean SNDDMA_Init(void)
+{
+
+	int rc;
+    int fmt;
+	int tmp;
+    int i;
+    char *s;
+	struct audio_buf_info info;
+	int caps;
+
+	snd_inited = 0;
+
+// open /dev/dsp, confirm capability to mmap, and get size of dma buffer
+
+    audio_fd = open("/dev/dsp", O_RDWR);
+    if (audio_fd < 0)
+	{
+		perror("/dev/dsp");
+        Con_Printf("Could not open /dev/dsp\n");
+		return 0;
+	}
+
+    rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
+    if (rc < 0)
+	{
+		perror("/dev/dsp");
+		Con_Printf("Could not reset /dev/dsp\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)
+	{
+		perror("/dev/dsp");
+        Con_Printf("Sound driver too old\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP))
+	{
+		Con_Printf("Sorry but your soundcard can't do this\n");
+		close(audio_fd);
+		return 0;
+	}
+
+    if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)
+    {   
+        perror("GETOSPACE");
+		Con_Printf("Um, can't do GETOSPACE?\n");
+		close(audio_fd);
+		return 0;
+    }
+    
+	shm = &sn;
+    shm->splitbuffer = 0;
+
+// set sample bits & speed
+
+    s = getenv("QUAKE_SOUND_SAMPLEBITS");
+    if (s) shm->samplebits = atoi(s);
+	else if ((i = COM_CheckParm("-sndbits")) != 0)
+		shm->samplebits = atoi(com_argv[i+1]);
+	if (shm->samplebits != 16 && shm->samplebits != 8)
+    {
+        ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
+        if (fmt & AFMT_S16_LE) shm->samplebits = 16;
+        else if (fmt & AFMT_U8) shm->samplebits = 8;
+    }
+
+    s = getenv("QUAKE_SOUND_SPEED");
+    if (s) shm->speed = atoi(s);
+	else if ((i = COM_CheckParm("-sndspeed")) != 0)
+		shm->speed = atoi(com_argv[i+1]);
+    else
+    {
+        for (i=0 ; i<sizeof(tryrates)/4 ; i++)
+            if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) break;
+        shm->speed = tryrates[i];
+    }
+
+    s = getenv("QUAKE_SOUND_CHANNELS");
+    if (s) shm->channels = atoi(s);
+	else if ((i = COM_CheckParm("-sndmono")) != 0)
+		shm->channels = 1;
+	else if ((i = COM_CheckParm("-sndstereo")) != 0)
+		shm->channels = 2;
+    else shm->channels = 2;
+
+	shm->samples = info.fragstotal * info.fragsize / (shm->samplebits/8);
+	shm->submission_chunk = 1;
+
+// memory map the dma buffer
+
+	shm->buffer = (unsigned char *) mmap(NULL, info.fragstotal
+		* info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
+	if (!shm->buffer || shm->buffer == (unsigned char *)-1)
+	{
+		perror("/dev/dsp");
+		Con_Printf("Could not mmap /dev/dsp\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	tmp = 0;
+	if (shm->channels == 2)
+		tmp = 1;
+    rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
+    if (rc < 0)
+    {
+		perror("/dev/dsp");
+        Con_Printf("Could not set /dev/dsp to stereo=%d", shm->channels);
+		close(audio_fd);
+        return 0;
+    }
+	if (tmp)
+		shm->channels = 2;
+	else
+		shm->channels = 1;
+
+    rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->speed);
+    if (rc < 0)
+    {
+		perror("/dev/dsp");
+        Con_Printf("Could not set /dev/dsp speed to %d", shm->speed);
+		close(audio_fd);
+        return 0;
+    }
+
+    if (shm->samplebits == 16)
+    {
+        rc = AFMT_S16_LE;
+        rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+        if (rc < 0)
+		{
+			perror("/dev/dsp");
+			Con_Printf("Could not support 16-bit data.  Try 8-bit.\n");
+			close(audio_fd);
+			return 0;
+		}
+    }
+    else if (shm->samplebits == 8)
+    {
+        rc = AFMT_U8;
+        rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
+        if (rc < 0)
+		{
+			perror("/dev/dsp");
+			Con_Printf("Could not support 8-bit data.\n");
+			close(audio_fd);
+			return 0;
+		}
+    }
+	else
+	{
+		perror("/dev/dsp");
+		Con_Printf("%d-bit sound not supported.", shm->samplebits);
+		close(audio_fd);
+		return 0;
+	}
+
+// toggle the trigger & start her up
+
+    tmp = 0;
+    rc  = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+	if (rc < 0)
+	{
+		perror("/dev/dsp");
+		Con_Printf("Could not toggle.\n");
+		close(audio_fd);
+		return 0;
+	}
+    tmp = PCM_ENABLE_OUTPUT;
+    rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
+	if (rc < 0)
+	{
+		perror("/dev/dsp");
+		Con_Printf("Could not toggle.\n");
+		close(audio_fd);
+		return 0;
+	}
+
+	shm->samplepos = 0;
+
+	snd_inited = 1;
+	return 1;
+
+}
+
+int SNDDMA_GetDMAPos(void)
+{
+
+	struct count_info count;
+
+	if (!snd_inited) return 0;
+
+	if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)
+	{
+		perror("/dev/dsp");
+		Con_Printf("Uh, sound dead.\n");
+		close(audio_fd);
+		snd_inited = 0;
+		return 0;
+	}
+//	shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1);
+//	fprintf(stderr, "%d    \r", count.ptr);
+	shm->samplepos = count.ptr / (shm->samplebits / 8);
+
+	return shm->samplepos;
+
+}
+
+void SNDDMA_Shutdown(void)
+{
+	if (snd_inited)
+	{
+		close(audio_fd);
+		snd_inited = 0;
+	}
+}
+
+/*
+==============
+SNDDMA_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+===============
+*/
+void SNDDMA_Submit(void)
+{
+}
+
diff --git a/apps/plugins/sdl/progs/quake/snd_mem.c b/apps/plugins/sdl/progs/quake/snd_mem.c
new file mode 100644
index 0000000..71e32aa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_mem.c
@@ -0,0 +1,341 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_mem.c: sound caching
+
+#include "quakedef.h"
+
+int			cache_full_cycle;
+
+byte *S_Alloc (int size);
+
+/*
+================
+ResampleSfx
+================
+*/
+void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
+{
+	int		outcount;
+	int		srcsample;
+	float	stepscale;
+	int		i;
+	int		sample, samplefrac, fracstep;
+	sfxcache_t	*sc;
+	
+	sc = Cache_Check (&sfx->cache);
+	if (!sc)
+		return;
+
+	stepscale = (float)inrate / shm->speed;	// this is usually 0.5, 1, or 2
+
+	outcount = sc->length / stepscale;
+	sc->length = outcount;
+	if (sc->loopstart != -1)
+		sc->loopstart = sc->loopstart / stepscale;
+
+	sc->speed = shm->speed;
+	if (loadas8bit.value)
+		sc->width = 1;
+	else
+		sc->width = inwidth;
+	sc->stereo = 0;
+
+// resample / decimate to the current source rate
+
+	if (stepscale == 1 && inwidth == 1 && sc->width == 1)
+	{
+// fast special case
+		for (i=0 ; i<outcount ; i++)
+			((signed char *)sc->data)[i]
+			= (int)( (unsigned char)(data[i]) - 128);
+	}
+	else
+	{
+// general case
+		samplefrac = 0;
+		fracstep = stepscale*256;
+		for (i=0 ; i<outcount ; i++)
+		{
+			srcsample = samplefrac >> 8;
+			samplefrac += fracstep;
+			if (inwidth == 2)
+				sample = LittleShort ( ((short *)data)[srcsample] );
+			else
+				sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
+			if (sc->width == 2)
+				((short *)sc->data)[i] = sample;
+			else
+				((signed char *)sc->data)[i] = sample >> 8;
+		}
+	}
+}
+
+//=============================================================================
+
+/*
+==============
+S_LoadSound
+==============
+*/
+sfxcache_t *S_LoadSound (sfx_t *s)
+{
+    char	namebuffer[256];
+	byte	*data;
+	wavinfo_t	info;
+	int		len;
+	float	stepscale;
+	sfxcache_t	*sc;
+	byte	stackbuf[1*1024];		// avoid dirtying the cache heap
+
+// see if still in memory
+	sc = Cache_Check (&s->cache);
+	if (sc)
+		return sc;
+
+//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf);
+// load it in
+    Q_strcpy(namebuffer, "sound/");
+    Q_strcat(namebuffer, s->name);
+
+//	Con_Printf ("loading %s\n",namebuffer);
+
+	data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf));
+
+	if (!data)
+	{
+		Con_Printf ("Couldn't load %s\n", namebuffer);
+		return NULL;
+	}
+
+	info = GetWavinfo (s->name, data, com_filesize);
+	if (info.channels != 1)
+	{
+		Con_Printf ("%s is a stereo sample\n",s->name);
+		return NULL;
+	}
+
+	stepscale = (float)info.rate / shm->speed;	
+	len = info.samples / stepscale;
+
+	len = len * info.width * info.channels;
+
+	sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
+	if (!sc)
+		return NULL;
+	
+	sc->length = info.samples;
+	sc->loopstart = info.loopstart;
+	sc->speed = info.rate;
+	sc->width = info.width;
+	sc->stereo = info.channels;
+
+	ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
+
+	return sc;
+}
+
+
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+
+byte	*data_p;
+byte 	*iff_end;
+byte 	*last_chunk;
+byte 	*iff_data;
+int 	iff_chunk_len;
+
+
+short GetLittleShort(void)
+{
+	short val = 0;
+	val = *data_p;
+	val = val + (*(data_p+1)<<8);
+	data_p += 2;
+	return val;
+}
+
+int GetLittleLong(void)
+{
+	int val = 0;
+	val = *data_p;
+	val = val + (*(data_p+1)<<8);
+	val = val + (*(data_p+2)<<16);
+	val = val + (*(data_p+3)<<24);
+	data_p += 4;
+	return val;
+}
+
+void FindNextChunk(char *name)
+{
+	while (1)
+	{
+		data_p=last_chunk;
+
+		if (data_p >= iff_end)
+		{	// didn't find the chunk
+			data_p = NULL;
+			return;
+		}
+		
+		data_p += 4;
+		iff_chunk_len = GetLittleLong();
+		if (iff_chunk_len < 0)
+		{
+			data_p = NULL;
+			return;
+		}
+//		if (iff_chunk_len > 1024*1024)
+//			Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+		data_p -= 8;
+		last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
+		if (!Q_strncmp(data_p, name, 4))
+			return;
+	}
+}
+
+void FindChunk(char *name)
+{
+	last_chunk = iff_data;
+	FindNextChunk (name);
+}
+
+
+void DumpChunks(void)
+{
+	char	str[5];
+	
+	str[4] = 0;
+	data_p=iff_data;
+	do
+	{
+		memcpy (str, data_p, 4);
+		data_p += 4;
+		iff_chunk_len = GetLittleLong();
+		Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
+		data_p += (iff_chunk_len + 1) & ~1;
+	} while (data_p < iff_end);
+}
+
+/*
+============
+GetWavinfo
+============
+*/
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
+{
+	wavinfo_t	info;
+	int     i;
+	int     format;
+	int		samples;
+
+	memset (&info, 0, sizeof(info));
+
+	if (!wav)
+		return info;
+		
+	iff_data = wav;
+	iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+	FindChunk("RIFF");
+	if (!(data_p && !Q_strncmp(data_p+8, "WAVE", 4)))
+	{
+		Con_Printf("Missing RIFF/WAVE chunks\n");
+		return info;
+	}
+
+// get "fmt " chunk
+	iff_data = data_p + 12;
+// DumpChunks ();
+
+	FindChunk("fmt ");
+	if (!data_p)
+	{
+		Con_Printf("Missing fmt chunk\n");
+		return info;
+	}
+	data_p += 8;
+	format = GetLittleShort();
+	if (format != 1)
+	{
+		Con_Printf("Microsoft PCM format only\n");
+		return info;
+	}
+
+	info.channels = GetLittleShort();
+	info.rate = GetLittleLong();
+	data_p += 4+2;
+	info.width = GetLittleShort() / 8;
+
+// get cue chunk
+	FindChunk("cue ");
+	if (data_p)
+	{
+		data_p += 32;
+		info.loopstart = GetLittleLong();
+//		Con_Printf("loopstart=%d\n", sfx->loopstart);
+
+	// if the next chunk is a LIST chunk, look for a cue length marker
+		FindNextChunk ("LIST");
+		if (data_p)
+		{
+			if (!strncmp (data_p + 28, "mark", 4))
+			{	// this is not a proper parse, but it works with cooledit...
+				data_p += 24;
+				i = GetLittleLong ();	// samples in loop
+				info.samples = info.loopstart + i;
+//				Con_Printf("looped length: %i\n", i);
+			}
+		}
+	}
+	else
+		info.loopstart = -1;
+
+// find data chunk
+	FindChunk("data");
+	if (!data_p)
+	{
+		Con_Printf("Missing data chunk\n");
+		return info;
+	}
+
+	data_p += 4;
+	samples = GetLittleLong () / info.width;
+
+	if (info.samples)
+	{
+		if (samples < info.samples)
+			Sys_Error ("Sound %s has a bad loop length", name);
+	}
+	else
+		info.samples = samples;
+
+	info.dataofs = data_p - wav;
+	
+	return info;
+}
+
diff --git a/apps/plugins/sdl/progs/quake/snd_mix.c b/apps/plugins/sdl/progs/quake/snd_mix.c
new file mode 100644
index 0000000..3f25455
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_mix.c
@@ -0,0 +1,132 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_mix.c -- portable code to mix sounds for snd_dma.c
+
+#include "quakedef.h"
+
+#define DWORD	unsigned long
+
+// why not write straight to SHM buffer?!?
+//portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
+int16_t *paintbuffer; // stereo interleaved samples
+
+/*
+===============================================================================
+
+CHANNEL MIXING
+
+===============================================================================
+*/
+
+// in snd_mix_*
+
+void SND_PaintChannelFrom8 (int true_lvol, int true_rvol, signed char *sfx, int count);
+void SND_PaintChannelFrom16 (int true_lvol, int true_rvol, signed short *sfx, int count);
+
+void S_PaintChannels(int endtime)
+{
+    int 	i;
+    int 	end;
+    channel_t *ch;
+    sfxcache_t	*sc;
+    int		ltime, count;
+
+    paintbuffer = shm->buffer;
+        
+    //while (paintedtime < endtime)
+    //{
+    // if paintbuffer is smaller than DMA buffer
+        
+    end = endtime;
+    //if (endtime - paintedtime > PAINTBUFFER_SIZE)
+    //end = paintedtime + PAINTBUFFER_SIZE;
+        
+    // clear the paint buffer
+    memset(paintbuffer, 0, (end - paintedtime) * sizeof(int16_t));
+
+    // 0-255
+    int volume_fp = volume.value * 255;
+    
+    // paint in the channels.
+    ch = channels;
+    for (i=0; i<total_channels ; i++, ch++)
+    {
+        if (!ch->sfx)
+            continue;
+        if (!ch->leftvol && !ch->rightvol)
+            continue;
+        sc = S_LoadSound (ch->sfx); // sound fx cache
+        if (!sc)
+            continue;
+
+        ltime = paintedtime;
+
+        while (ltime < end)
+        {	// paint up to end of sound channel, or end of buffer, whichever is greatest
+            if (ch->end < end)
+                count = ch->end - ltime;
+            else
+                count = end - ltime;
+
+            //if(count == 1)
+            //    rb->splashf(HZ, "Potential problem");
+            
+            if (count > 0)
+            {
+                int true_lvol, true_rvol;
+                true_lvol = (volume_fp * ch->leftvol) / 256;
+                true_rvol = (volume_fp * ch->rightvol) / 256;
+
+                if (sc->width == 1)
+                {
+                    signed char *sfx = (char*)sc->data + ch->pos;
+                    SND_PaintChannelFrom8(true_lvol, true_rvol, sfx, count);
+                }
+                else
+                {
+                    signed short *sfx = (short*)sc->data + ch->pos;
+                    SND_PaintChannelFrom16(true_lvol, true_rvol, sfx, count);
+                }
+
+                ch->pos += count;
+                ltime += count;
+            }
+
+            // if at end of loop, restart
+            if (ltime >= ch->end)
+            {
+                if (sc->loopstart >= 0)
+                {
+                    ch->pos = sc->loopstart;
+                    ch->end = ltime + sc->length - ch->pos;
+                }
+                else				
+                {	// channel just stopped
+                    ch->sfx = NULL;
+                    break;
+                }
+            }
+        }
+    }
+
+    // transfer out according to DMA format
+    //S_TransferPaintBuffer(end);
+    paintedtime = end;
+}
diff --git a/apps/plugins/sdl/progs/quake/snd_mix_arm.S b/apps/plugins/sdl/progs/quake/snd_mix_arm.S
new file mode 100644
index 0000000..15733d8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_mix_arm.S
@@ -0,0 +1,129 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2019 Franklin Wei
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+        /** Sound mixing code for ARM. **/
+        /* Takes 8-bit mono audio and outputs stereo 16-bit samples.
+         * stereo volumes are passed as arguments.
+         *
+         * Bear with me. This is my first ARM assembly, ever.
+        */
+
+        .text
+        .align  2
+        .global SND_PaintChannelFrom8
+        .type   SND_PaintChannelFrom8, %function
+
+#if defined(__ARM_ARCH_5TEJ__)
+SND_PaintChannelFrom8:
+        // r0: int true_lvol
+        // r1: int true_rvol
+        // r2: char *sfx
+        // r3: int count
+
+        stmfd sp!, {r4, r5, r6, r7, r8, sl}
+
+        ldr ip, =paintbuffer
+        ldr ip, [ip]
+
+        mov r0, r0, asl #16 // pre-scale both volumes by 2^16
+        mov r1, r1, asl #16
+
+        sub r3, r3, #1 // we'll count backwards
+        // sl = 0xffff0000
+        ldrh sl, =0xffff
+
+.loop:
+        ldrsb r4, [r2, r3] // load *sfx[i] -> r4
+
+        // keep endianness in mind here
+        // buffer looks like [left_0, left_1, right_0, right_1] in memory
+        // but it is loaded as [right1, right0, left1, left0] to registers
+        ldr r8, [ip, r3, lsl #2] // load paintbuffer[0:1] = RIGHTCHANNEL:LEFTCHANNEL
+
+        // handle high half (right channel) first
+        mul r5, r4, r1 // SCALEDRIGHT = SFXI * (true_rvol << 16) -- bottom half is zero
+
+        // r7 holds right channel in high half (dirty bottom half)
+        qadd r7, r5, r8 // RIGHTCHANORIG = SCALEDRIGHT + RIGHTCHANORIG (high half)
+
+        bic r7, r7, sl // zero bottom bits of r7
+
+        // trash r5, r6 and handle left channel
+        mul r5, r4, r0 // SCALEDLEFT = SFXI * (true_rvol << 16)
+
+        mov r8, r8, lsl #16 // extract original left channel from paintbuffer
+
+        // r8 holds left channel in high half with zero bottom half
+        qadd r8, r5, r8
+
+        // combine the two 16-bit samples in r7 as 32-bit [left:right]
+        // (use lsr to not sign-extend the lower half)
+        orr r7, r7, r8, lsr #16
+
+        str r7, [ip, r3, lsl #2] // write 32-bit to paintbuffer
+        subs r3, r3, #1
+        bgt .loop // must use instead of bne because of the corner case count=1
+
+
+
+        ldmfd sp!, {r4, r5, r6, r7, r8, sl}
+
+        bx lr
+
+#elif defined(__ARM_ARCH_6__) // ARMv6 with QADD16 (disabled)
+SND_PaintChannelFrom8:
+        // r0: int true_lvol
+        // r1: int true_rvol
+        // r2: char *sfx
+        // r3: int count
+
+        stmfd sp!, {r4, r5, r6, r7}
+
+        ldr ip, =paintbuffer
+        ldr ip, [ip]
+        sub r3, r3, #1 // we'll count backwards
+.loop:
+        ldrsb r4, [r2, r3] // load *sfx[i] -> r4
+
+        // keep endianness in mind here
+        // buffer looks like [left_0, left_1, right_0, right_1] in memory
+        // but it is loaded as [right1, right0, left1, left0] to registers
+        ldr r7, [ip, r3, lsl #2] // load paintbuffer[0:1] = RIGHTCHANNEL:LEFTCHANNEL
+
+        // handle high half (right channel) first
+        mul r5, r4, r1 // SCALEDRIGHT = SFXI * true_rvol
+        mul r6, r4, r0 // SCALEDLEFT = SFXI * true_rvol
+
+        orr r6, r6, r5, lsl #16
+
+        qadd16 r6, r6, r7
+
+        str r6, [ip, r3, lsl #2] // write 32-bit to paintbuffer
+
+        subs r3, r3, #1
+        bne .loop
+
+        ldmfd sp!, {r4, r5, r6, r7}
+
+        bx lr
+#else
+#error ARMv5/6 only
+#endif
diff --git a/apps/plugins/sdl/progs/quake/snd_mix_generic.c b/apps/plugins/sdl/progs/quake/snd_mix_generic.c
new file mode 100644
index 0000000..2c4b67b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_mix_generic.c
@@ -0,0 +1,69 @@
+/* This file is included from snd_mix.c */
+
+#include "SDL.h"
+
+// allow standalone compilation
+#ifndef QUAKE_SOUND
+extern short *paintbuffer;
+#define INLINE
+#else
+#define INLINE static inline
+#endif
+
+// C version
+static inline short CLAMPADD(short a, short b)
+{
+    int val = (int)a + (int)b;
+    if (val > 32767)
+    {
+        //rb->splashf(HZ, "saturate");
+        return 32767;
+    }
+    else if (val < -32768)
+    {
+        //rb->splashf(HZ, "saturate");
+        return -32768;
+    }
+    else
+        return val;
+}
+
+// if not ARMv5
+#ifndef __ARM_ARCH__5TEJ__
+void SND_PaintChannelFrom8 (int true_lvol, int true_rvol, signed char *sfx, int count)
+{
+    //return;
+    //LOGF("mix8\n");
+        int     data;
+        int             i;
+
+        // we have 8-bit sound in sfx[], which we want to scale to
+        // 16bit and take the volume into account
+        for (i=0 ; i<count ; i++)
+        {
+            // We could use the QADD16 instruction on ARMv6+
+            // or just 32-bit QADD with pre-shifted arguments
+            data = sfx[i];
+            paintbuffer[2*i+0] = CLAMPADD(paintbuffer[2*i+0], data * true_lvol); // need saturation
+            paintbuffer[2*i+1] = CLAMPADD(paintbuffer[2*i+1], data * true_rvol);
+        }
+}
+#endif
+
+void SND_PaintChannelFrom16 (int true_lvol, int true_rvol, signed short *sfx, int count)
+{
+    //return;
+    //LOGF("mix16\n");
+        int data;
+        int left, right;
+        int     i;
+
+        for (i=0 ; i<count ; i+=2)
+        {
+                data = sfx[i];
+                left = ((int)data * true_lvol) >> 8;
+                right = ((int)data * true_rvol) >> 8;
+                paintbuffer[2*i+0] = CLAMPADD(paintbuffer[2*i+0], left); // need saturation
+                paintbuffer[2*i+1] = CLAMPADD(paintbuffer[2*i+1], right);
+        }
+}
diff --git a/apps/plugins/sdl/progs/quake/snd_mix_generic_annotated.s b/apps/plugins/sdl/progs/quake/snd_mix_generic_annotated.s
new file mode 100644
index 0000000..474b0e9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_mix_generic_annotated.s
@@ -0,0 +1,191 @@
+	.cpu arm926ej-s
+	.fpu softvfp
+	.eabi_attribute 20, 1
+	.eabi_attribute 21, 1
+	.eabi_attribute 23, 3
+	.eabi_attribute 24, 1
+	.eabi_attribute 25, 1
+	.eabi_attribute 26, 1
+	.eabi_attribute 30, 2
+	.eabi_attribute 18, 4
+	.file	"snd_mix_generic.c"
+@ GNU C (GCC) version 4.4.4 (arm-elf-eabi)
+@	compiled by GNU C version 4.8.2 20140206 (prerelease), GMP version 4.3.2, MPFR version 2.4.2.
+@ GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+@ options passed:  -imultilib arm926ej-s -D__USES_INITFINI__
+@ ./snd_mix_generic.c -mcpu=arm926ej-s -O3 -fverbose-asm
+@ options enabled:  -falign-loops -fargument-alias -fauto-inc-dec
+@ -fbranch-count-reg -fcaller-saves -fcommon -fcprop-registers
+@ -fcrossjumping -fcse-follow-jumps -fdefer-pop
+@ -fdelete-null-pointer-checks -fearly-inlining
+@ -feliminate-unused-debug-types -fexpensive-optimizations
+@ -fforward-propagate -ffunction-cse -fgcse -fgcse-after-reload -fgcse-lm
+@ -fguess-branch-probability -fident -fif-conversion -fif-conversion2
+@ -findirect-inlining -finline -finline-functions
+@ -finline-functions-called-once -finline-small-functions -fipa-cp
+@ -fipa-cp-clone -fipa-pure-const -fipa-reference -fira-share-save-slots
+@ -fira-share-spill-slots -fivopts -fkeep-static-consts
+@ -fleading-underscore -fmath-errno -fmerge-constants -fmerge-debug-strings
+@ -fmove-loop-invariants -fomit-frame-pointer -foptimize-register-move
+@ -foptimize-sibling-calls -fpeephole -fpeephole2 -fpredictive-commoning
+@ -freg-struct-return -fregmove -freorder-blocks -freorder-functions
+@ -frerun-cse-after-loop -fsched-interblock -fsched-spec
+@ -fsched-stalled-insns-dep -fschedule-insns -fschedule-insns2
+@ -fsection-anchors -fsigned-zeros -fsplit-ivs-in-unroller
+@ -fsplit-wide-types -fstrict-aliasing -fstrict-overflow -fthread-jumps
+@ -ftoplevel-reorder -ftrapping-math -ftree-builtin-call-dce -ftree-ccp
+@ -ftree-ch -ftree-copy-prop -ftree-copyrename -ftree-cselim -ftree-dce
+@ -ftree-dominator-opts -ftree-dse -ftree-fre -ftree-loop-im
+@ -ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
+@ -ftree-pre -ftree-reassoc -ftree-scev-cprop -ftree-sink -ftree-sra
+@ -ftree-switch-conversion -ftree-ter -ftree-vect-loop-version
+@ -ftree-vectorize -ftree-vrp -funit-at-a-time -funswitch-loops
+@ -fverbose-asm -fzero-initialized-in-bss -mlittle-endian -msched-prolog
+
+@ Compiler executable checksum: 6bc9eb36ed7f27ae4e8601da7e401eb4
+
+	.text
+	.align	2
+	.global	SND_PaintChannelFrom8
+	.type	SND_PaintChannelFrom8, %function
+SND_PaintChannelFrom8:
+        // r0: true_lvol
+        // r1: true_rvol
+        // r2: *sfx
+        // r3: count
+
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+	cmp	r3, #0	@ count
+	stmfd	sp!, {r4, r5, r6, r7, r8, sl}	@,
+	ble	.L10	@, // exit early if count <= 0
+	ldr	ip, .L13	@ tmp207, // ip = paintbuffer
+	ldr	r5, .L13+4	@ tmp224, // r5 = 32767
+	ldr	ip, [ip, #0]	@ ivtmp.61, paintbuffer // ip = &paintbuffer
+
+        // this does {rvol,lvol} &= 0xffff in a really convoluted way
+	mov	r1, r1, asl #16	@ tmp206, true_rvol, // r1 = rvol << 16
+	mov	r0, r0, asl #16	@ tmp205, true_lvol, // r0 = lvol << 16
+	mov	r6, r1, lsr #16	@ pretmp.46, tmp206, // rvol_16 = r1 >> 16
+	mov	r7, r0, lsr #16	@ pretmp.44, tmp205, // lvol_16 = r0 >> 16
+	mov	r1, #0	@ i, // i = 0
+        // loop?
+.L9:
+	ldrsb	r0, [r2, r1]	@ tmp208,* i // r0 = (byte) sfx[i]
+	ldrsh	r8, [ip, #0]	@ tmp213,* ivtmp.61 // r8 = (int16) paintbuffer[0]
+
+        // r0 &= 0xffff
+	mov	r0, r0, asl #16	@ tmp209, tmp208, // r0 <<= 16
+	mov	r0, r0, lsr #16	@ D.1289, tmp209, // r0 >>= 16
+
+
+	mul	r4, r0, r7	@ tmp210, D.1289, pretmp.44 // r4 = sfx[i] * (lvol_16)
+
+	mov	r4, r4, asl #16	@ tmp212, tmp210,        // r4 <<= 16
+
+	add	r4, r8, r4, asr #16	@, val, tmp213, tmp212, // r8 += r4 & 0xffff
+
+        // handle saturation
+	cmp	r4, r5	@ val, tmp224 // compare r4, 32767
+	ldrgt	r8, .L13+4	@ D.1341, // r8 = 32767 if saturated
+	bgt	.L4	@,                // jump to .L4 if saturated
+	cmn	r4, #32768	@ val, // compare r4, -32768
+	movge	r4, r4, asl #16	@ tmp216, val, // if not saturated, &= 0xffff
+	movlt	r8, #32768	@ D.1341,         // r8 = -32768 if saturated
+	movge	r8, r4, lsr #16	@ D.1341, tmp216, // r8 = val
+
+        // r8 is final value of paintbuffer[2*i+0]
+
+        // now handle right channel
+.L4:
+        // sl is scaled right channel
+
+        // this does paintbuffer[1] += rvol * sfx[i]
+	mul	sl, r0, r6	@ tmp217, D.1289, pretmp.46 // sl = sfx[i] * rvol_16
+	ldrsh	r4, [ip, #2]	@ tmp220, // r4 = paintbuffer[1]
+	mov	r0, sl, asl #16	@ tmp219, tmp217, // r0 = sl << 16
+	add	r0, r4, r0, asr #16	@, val, tmp220, tmp219, // r0 = r4 + (r0 >> 16)
+
+        // now saturate
+	cmp	r0, r5	@ val, tmp224 // compare final right channel to 32767
+
+	strh	r8, [ip, #0]	@ movhi	@ D.1341,* ivtmp.61 // write left channel back to paintbuffer
+	ldrgt	r0, .L13+4	@ D.1348, // r0 = 32767 if saturated
+	bgt	.L7	@, // skip to .L7
+	cmn	r0, #32768	@ val, // check < -32768
+	movge	r0, r0, asl #16	@ tmp223, val, // <<= 16 if not saturated
+	movlt	r0, #32768	@ D.1348, // if < -32768, saturate
+	movge	r0, r0, lsr #16	@ D.1348, tmp223, // >>= 16 if not saturated
+.L7:
+	add	r1, r1, #1	@ i, i, // i++
+	cmp	r1, r3	@ i, count // i =? count
+	strh	r0, [ip, #2]	@ movhi	@ D.1348 , // store right channel to paintbuffer
+	add	ip, ip, #4	@ ivtmp.61, ivtmp.61, // paintbuffer += 4 bytes
+	bne	.L9	@, // restart if i != count
+.L10:
+	ldmfd	sp!, {r4, r5, r6, r7, r8, sl} // pop stack
+	bx	lr // return
+.L14:
+	.align	2
+.L13:
+	.word	paintbuffer
+	.word	32767
+	.size	SND_PaintChannelFrom8, .-SND_PaintChannelFrom8
+	.align	2
+	.global	SND_PaintChannelFrom16
+	.type	SND_PaintChannelFrom16, %function
+SND_PaintChannelFrom16:
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+	cmp	r3, #0	@ count
+	stmfd	sp!, {r4, r5, r6, r7, r8, sl}	@,
+	ble	.L24	@,
+	ldr	ip, .L26	@ tmp146,
+	ldr	r7, .L26+4	@ tmp164,
+	ldr	ip, [ip, #0]	@ ivtmp.119, paintbuffer
+	mov	r4, #0	@ i,
+.L23:
+	mov	r5, r4, asl #1	@ tmp147, i,
+	ldrsh	r5, [r2, r5]	@ data,
+	ldrsh	r8, [ip, #0]	@ tmp152,* ivtmp.119
+	mul	r6, r5, r0	@ tmp148, data, true_lvol
+	mov	r6, r6, asl #8	@ tmp151, tmp148,
+	add	r6, r8, r6, asr #16	@, val, tmp152, tmp151,
+	cmp	r6, r7	@ val, tmp164
+	ldrgt	r8, .L26+4	@ D.1355,
+	bgt	.L18	@,
+	cmn	r6, #32768	@ val,
+	movge	r6, r6, asl #16	@ tmp155, val,
+	movlt	r8, #32768	@ D.1355,
+	movge	r8, r6, lsr #16	@ D.1355, tmp155,
+.L18:
+	mul	sl, r5, r1	@ tmp156, data, true_rvol
+	ldrsh	r6, [ip, #2]	@ tmp160,
+	mov	r5, sl, asl #8	@ tmp159, tmp156,
+	add	r5, r6, r5, asr #16	@, val, tmp160, tmp159,
+	cmp	r5, r7	@ val, tmp164
+	strh	r8, [ip, #0]	@ movhi	@ D.1355,* ivtmp.119
+	ldrgt	r5, .L26+4	@ D.1362,
+	bgt	.L21	@,
+	cmn	r5, #32768	@ val,
+	movge	r5, r5, asl #16	@ tmp163, val,
+	movlt	r5, #32768	@ D.1362,
+	movge	r5, r5, lsr #16	@ D.1362, tmp163,
+.L21:
+	add	r4, r4, #2	@ i, i,
+	cmp	r3, r4	@ count, i
+	strh	r5, [ip, #2]	@ movhi	@ D.1362,
+	add	ip, ip, #8	@ ivtmp.119, ivtmp.119,
+	bgt	.L23	@,
+.L24:
+	ldmfd	sp!, {r4, r5, r6, r7, r8, sl}
+	bx	lr
+.L27:
+	.align	2
+.L26:
+	.word	paintbuffer
+	.word	32767
+	.size	SND_PaintChannelFrom16, .-SND_PaintChannelFrom16
+	.ident	"GCC: (GNU) 4.4.4"
diff --git a/apps/plugins/sdl/progs/quake/snd_null.c b/apps/plugins/sdl/progs/quake/snd_null.c
new file mode 100644
index 0000000..9db2ebc
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_null.c
@@ -0,0 +1,97 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// snd_null.c -- include this instead of all the other snd_* files to have
+// no sound code whatsoever
+
+#include "quakedef.h"
+
+cvar_t bgmvolume = {"bgmvolume", "1", true};
+cvar_t volume = {"volume", "0.7", true};
+
+ 
+void S_Init (void)
+{
+}
+
+void S_AmbientOff (void)
+{
+}
+
+void S_AmbientOn (void)
+{
+}
+
+void S_Shutdown (void)
+{
+}
+
+void S_TouchSound (char *sample)
+{
+}
+
+void S_ClearBuffer (void)
+{
+}
+
+void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)
+{
+}
+
+void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol,  float attenuation)
+{
+}
+
+void S_StopSound (int entnum, int entchannel)
+{
+}
+
+sfx_t *S_PrecacheSound (char *sample)
+{
+	return NULL;
+}
+
+void S_ClearPrecache (void)
+{
+}
+
+void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up)
+{	
+}
+
+void S_StopAllSounds (qboolean clear)
+{
+}
+
+void S_BeginPrecaching (void)
+{
+}
+
+void S_EndPrecaching (void)
+{
+}
+
+void S_ExtraUpdate (void)
+{
+}
+
+void S_LocalSound (char *s)
+{
+}
+
diff --git a/apps/plugins/sdl/progs/quake/snd_sdl.c b/apps/plugins/sdl/progs/quake/snd_sdl.c
new file mode 100644
index 0000000..644d4e1
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/snd_sdl.c
@@ -0,0 +1,113 @@
+
+#include <stdio.h>
+#include "SDL_audio.h"
+#include "SDL_byteorder.h"
+#include "quakedef.h"
+
+static dma_t the_shm;
+static int snd_inited;
+
+extern int desired_speed;
+extern int desired_bits;
+
+// SDL hereby demands `len' samples in stream, *NOW*!
+static void paint_audio(void *unused, Uint8 *stream, int len)
+{
+	if ( shm ) {
+		shm->buffer = stream;
+		shm->samplepos += len/(shm->samplebits/8)/2;
+		// Check for samplepos overflow?
+		S_PaintChannels (shm->samplepos);
+	}
+}
+
+qboolean SNDDMA_Init(void)
+{
+	SDL_AudioSpec desired, obtained;
+
+	snd_inited = 0;
+
+	/* Set up the desired format */
+	desired.freq = desired_speed;
+	switch (desired_bits) {
+		case 8:
+			desired.format = AUDIO_U8;
+			break;
+		case 16:
+			if ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
+				desired.format = AUDIO_S16MSB;
+			else
+				desired.format = AUDIO_S16LSB;
+			break;
+		default:
+        		Con_Printf("Unknown number of audio bits: %d\n",
+								desired_bits);
+			return 0;
+	}
+	desired.channels = 2;
+	desired.samples = 1024;
+	desired.callback = paint_audio;
+
+	/* Open the audio device */
+	if ( SDL_OpenAudio(&desired, &obtained) < 0 ) {
+        	Con_Printf("Couldn't open SDL audio: %s\n", SDL_GetError());
+		return 0;
+	}
+
+	/* Make sure we can support the audio format */
+	switch (obtained.format) {
+		case AUDIO_U8:
+			/* Supported */
+			break;
+		case AUDIO_S16LSB:
+		case AUDIO_S16MSB:
+			if ( ((obtained.format == AUDIO_S16LSB) &&
+			     (SDL_BYTEORDER == SDL_LIL_ENDIAN)) ||
+			     ((obtained.format == AUDIO_S16MSB) &&
+			     (SDL_BYTEORDER == SDL_BIG_ENDIAN)) ) {
+				/* Supported */
+				break;
+			}
+			/* Unsupported, fall through */;
+		default:
+			/* Not supported -- force SDL to do our bidding */
+			SDL_CloseAudio();
+			if ( SDL_OpenAudio(&desired, NULL) < 0 ) {
+        			Con_Printf("Couldn't open SDL audio: %s\n",
+							SDL_GetError());
+				return 0;
+			}
+			memcpy(&obtained, &desired, sizeof(desired));
+			break;
+	}
+	SDL_PauseAudio(0);
+
+	/* Fill the audio DMA information block */
+	shm = &the_shm;
+	shm->splitbuffer = 0;
+	shm->samplebits = (obtained.format & 0xFF);
+	shm->speed = obtained.freq;
+	shm->channels = obtained.channels;
+	shm->samples = obtained.samples*shm->channels;
+	shm->samplepos = 0;
+	shm->submission_chunk = 1;
+	shm->buffer = NULL;
+
+	snd_inited = 1;
+	return 1;
+}
+
+int SNDDMA_GetDMAPos(void)
+{
+	return shm->samplepos;
+}
+
+void SNDDMA_Shutdown(void)
+{
+	if (snd_inited)
+	{
+		SDL_CloseAudio();
+		snd_inited = 0;
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/spritegn.h b/apps/plugins/sdl/progs/quake/spritegn.h
new file mode 100644
index 0000000..8e35c23
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/spritegn.h
@@ -0,0 +1,114 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// spritegn.h: header file for sprite generation program
+//
+
+// **********************************************************
+// * This file must be identical in the spritegen directory *
+// * and in the Quake directory, because it's used to       *
+// * pass data from one to the other via .spr files.        *
+// **********************************************************
+
+//-------------------------------------------------------
+// This program generates .spr sprite package files.
+// The format of the files is as follows:
+//
+// dsprite_t file header structure
+// <repeat dsprite_t.numframes times>
+//   <if spritegroup, repeat dspritegroup_t.numframes times>
+//     dspriteframe_t frame header structure
+//     sprite bitmap
+//   <else (single sprite frame)>
+//     dspriteframe_t frame header structure
+//     sprite bitmap
+// <endrepeat>
+//-------------------------------------------------------
+
+#ifdef INCLUDELIBS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "dictlib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+#include "mathlib.h"
+
+#endif
+
+#define SPRITE_VERSION	1
+
+// must match definition in modelgen.h
+#ifndef SYNCTYPE_T
+#define SYNCTYPE_T
+typedef int synctime_t;
+
+enum {ST_SYNC=0, ST_RAND };
+#endif
+
+// TODO: shorten these?
+typedef struct {
+	int			ident;
+	int			version;
+	int			type;
+	float		boundingradius;
+	int			width;
+	int			height;
+	int			numframes;
+	float		beamlength;
+	synctype_t	synctype;
+} dsprite_t;
+
+#define SPR_VP_PARALLEL_UPRIGHT		0
+#define SPR_FACING_UPRIGHT			1
+#define SPR_VP_PARALLEL				2
+#define SPR_ORIENTED				3
+#define SPR_VP_PARALLEL_ORIENTED	4
+
+typedef struct {
+	int			origin[2];
+	int			width;
+	int			height;
+} dspriteframe_t;
+
+typedef struct {
+	int			numframes;
+} dspritegroup_t;
+
+typedef struct {
+	float	interval;
+} dspriteinterval_t;
+
+typedef int spriteframetype_t;
+
+enum { SPR_SINGLE=0, SPR_GROUP };
+
+typedef struct {
+	spriteframetype_t	type;
+} dspriteframetype_t;
+
+#define IDSPRITEHEADER	(('P'<<24)+('S'<<16)+('D'<<8)+'I')
+														// little-endian "IDSP"
+
diff --git a/apps/plugins/sdl/progs/quake/sv_main.c b/apps/plugins/sdl/progs/quake/sv_main.c
new file mode 100644
index 0000000..5ea62f1
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sv_main.c
@@ -0,0 +1,1209 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_main.c -- server main program
+
+#include "quakedef.h"
+
+server_t		sv;
+server_static_t	svs;
+
+char	localmodels[MAX_MODELS][5];			// inline model names for precache
+
+//============================================================================
+
+/*
+===============
+SV_Init
+===============
+*/
+void SV_Init (void)
+{
+	int		i;
+	extern	cvar_t	sv_maxvelocity;
+	extern	cvar_t	sv_gravity;
+	extern	cvar_t	sv_nostep;
+	extern	cvar_t	sv_friction;
+	extern	cvar_t	sv_edgefriction;
+	extern	cvar_t	sv_stopspeed;
+	extern	cvar_t	sv_maxspeed;
+	extern	cvar_t	sv_accelerate;
+	extern	cvar_t	sv_idealpitchscale;
+	extern	cvar_t	sv_aim;
+
+	Cvar_RegisterVariable (&sv_maxvelocity);
+	Cvar_RegisterVariable (&sv_gravity);
+	Cvar_RegisterVariable (&sv_friction);
+	Cvar_RegisterVariable (&sv_edgefriction);
+	Cvar_RegisterVariable (&sv_stopspeed);
+	Cvar_RegisterVariable (&sv_maxspeed);
+	Cvar_RegisterVariable (&sv_accelerate);
+	Cvar_RegisterVariable (&sv_idealpitchscale);
+	Cvar_RegisterVariable (&sv_aim);
+	Cvar_RegisterVariable (&sv_nostep);
+
+	for (i=0 ; i<MAX_MODELS ; i++)
+		sprintf (localmodels[i], "*%i", i);
+}
+
+/*
+=============================================================================
+
+EVENT MESSAGES
+
+=============================================================================
+*/
+
+/*  
+==================
+SV_StartParticle
+
+Make sure the event gets sent to all clients
+==================
+*/
+void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count)
+{
+	int		i, v;
+
+	if (sv.datagram.cursize > MAX_DATAGRAM-16)
+		return;	
+	MSG_WriteByte (&sv.datagram, svc_particle);
+	MSG_WriteCoord (&sv.datagram, org[0]);
+	MSG_WriteCoord (&sv.datagram, org[1]);
+	MSG_WriteCoord (&sv.datagram, org[2]);
+	for (i=0 ; i<3 ; i++)
+	{
+		v = dir[i]*16;
+		if (v > 127)
+			v = 127;
+		else if (v < -128)
+			v = -128;
+		MSG_WriteChar (&sv.datagram, v);
+	}
+	MSG_WriteByte (&sv.datagram, count);
+	MSG_WriteByte (&sv.datagram, color);
+}           
+
+/*  
+==================
+SV_StartSound
+
+Each entity can have eight independant sound sources, like voice,
+weapon, feet, etc.
+
+Channel 0 is an auto-allocate channel, the others override anything
+allready running on that entity/channel pair.
+
+An attenuation of 0 will play full volume everywhere in the level.
+Larger attenuations will drop off.  (max 4 attenuation)
+
+==================
+*/  
+void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
+    float attenuation)
+{       
+    int         sound_num;
+    int field_mask;
+    int			i;
+	int			ent;
+	
+	if (volume < 0 || volume > 255)
+		Sys_Error ("SV_StartSound: volume = %i", volume);
+
+	if (attenuation < 0 || attenuation > 4)
+		Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
+
+	if (channel < 0 || channel > 7)
+		Sys_Error ("SV_StartSound: channel = %i", channel);
+
+	if (sv.datagram.cursize > MAX_DATAGRAM-16)
+		return;	
+
+// find precache number for sound
+    for (sound_num=1 ; sound_num<MAX_SOUNDS
+        && sv.sound_precache[sound_num] ; sound_num++)
+        if (!strcmp(sample, sv.sound_precache[sound_num]))
+            break;
+    
+    if ( sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num] )
+    {
+        Con_Printf ("SV_StartSound: %s not precacheed\n", sample);
+        return;
+    }
+    
+	ent = NUM_FOR_EDICT(entity);
+
+	channel = (ent<<3) | channel;
+
+	field_mask = 0;
+	if (volume != DEFAULT_SOUND_PACKET_VOLUME)
+		field_mask |= SND_VOLUME;
+	if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
+		field_mask |= SND_ATTENUATION;
+
+// directed messages go only to the entity the are targeted on
+	MSG_WriteByte (&sv.datagram, svc_sound);
+	MSG_WriteByte (&sv.datagram, field_mask);
+	if (field_mask & SND_VOLUME)
+		MSG_WriteByte (&sv.datagram, volume);
+	if (field_mask & SND_ATTENUATION)
+		MSG_WriteByte (&sv.datagram, attenuation*64);
+	MSG_WriteShort (&sv.datagram, channel);
+	MSG_WriteByte (&sv.datagram, sound_num);
+	for (i=0 ; i<3 ; i++)
+		MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]));
+}           
+
+/*
+==============================================================================
+
+CLIENT SPAWNING
+
+==============================================================================
+*/
+
+/*
+================
+SV_SendServerinfo
+
+Sends the first message from the server to a connected client.
+This will be sent on the initial connection and upon each server load.
+================
+*/
+void SV_SendServerinfo (client_t *client)
+{
+	char			**s;
+	char			message[2048];
+
+	MSG_WriteByte (&client->message, svc_print);
+	sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc);
+	MSG_WriteString (&client->message,message);
+
+	MSG_WriteByte (&client->message, svc_serverinfo);
+	MSG_WriteLong (&client->message, PROTOCOL_VERSION);
+	MSG_WriteByte (&client->message, svs.maxclients);
+
+	if (!coop.value && deathmatch.value)
+		MSG_WriteByte (&client->message, GAME_DEATHMATCH);
+	else
+		MSG_WriteByte (&client->message, GAME_COOP);
+
+	sprintf (message, pr_strings+sv.edicts->v.message);
+
+	MSG_WriteString (&client->message,message);
+
+	for (s = sv.model_precache+1 ; *s ; s++)
+		MSG_WriteString (&client->message, *s);
+	MSG_WriteByte (&client->message, 0);
+
+	for (s = sv.sound_precache+1 ; *s ; s++)
+		MSG_WriteString (&client->message, *s);
+	MSG_WriteByte (&client->message, 0);
+
+// send music
+	MSG_WriteByte (&client->message, svc_cdtrack);
+	MSG_WriteByte (&client->message, sv.edicts->v.sounds);
+	MSG_WriteByte (&client->message, sv.edicts->v.sounds);
+
+// set view	
+	MSG_WriteByte (&client->message, svc_setview);
+	MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict));
+
+	MSG_WriteByte (&client->message, svc_signonnum);
+	MSG_WriteByte (&client->message, 1);
+
+	client->sendsignon = true;
+	client->spawned = false;		// need prespawn, spawn, etc
+}
+
+/*
+================
+SV_ConnectClient
+
+Initializes a client_t for a new net connection.  This will only be called
+once for a player each game, not once for each level change.
+================
+*/
+void SV_ConnectClient (int clientnum)
+{
+	edict_t			*ent;
+	client_t		*client;
+	int				edictnum;
+	struct qsocket_s *netconnection;
+	int				i;
+	float			spawn_parms[NUM_SPAWN_PARMS];
+
+	client = svs.clients + clientnum;
+
+	Con_DPrintf ("Client %s connected\n", client->netconnection->address);
+
+	edictnum = clientnum+1;
+
+	ent = EDICT_NUM(edictnum);
+	
+// set up the client_t
+	netconnection = client->netconnection;
+	
+	if (sv.loadgame)
+		memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
+	memset (client, 0, sizeof(*client));
+	client->netconnection = netconnection;
+
+	strcpy (client->name, "unconnected");
+	client->active = true;
+	client->spawned = false;
+	client->edict = ent;
+	client->message.data = client->msgbuf;
+	client->message.maxsize = sizeof(client->msgbuf);
+	client->message.allowoverflow = true;		// we can catch it
+
+#ifdef IDGODS
+	client->privileged = IsID(&client->netconnection->addr);
+#else	
+	client->privileged = false;				
+#endif
+
+	if (sv.loadgame)
+		memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms));
+	else
+	{
+	// call the progs to get default spawn parms for the new client
+		PR_ExecuteProgram (pr_global_struct->SetNewParms);
+		for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
+			client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
+	}
+
+	SV_SendServerinfo (client);
+}
+
+
+/*
+===================
+SV_CheckForNewClients
+
+===================
+*/
+void SV_CheckForNewClients (void)
+{
+	struct qsocket_s	*ret;
+	int				i;
+		
+//
+// check for new connections
+//
+	while (1)
+	{
+		ret = NET_CheckNewConnections ();
+		if (!ret)
+			break;
+
+	// 
+	// init a new client structure
+	//	
+		for (i=0 ; i<svs.maxclients ; i++)
+			if (!svs.clients[i].active)
+				break;
+		if (i == svs.maxclients)
+			Sys_Error ("Host_CheckForNewClients: no free clients");
+		
+		svs.clients[i].netconnection = ret;
+		SV_ConnectClient (i);	
+	
+		net_activeconnections++;
+	}
+}
+
+
+
+/*
+===============================================================================
+
+FRAME UPDATES
+
+===============================================================================
+*/
+
+/*
+==================
+SV_ClearDatagram
+
+==================
+*/
+void SV_ClearDatagram (void)
+{
+	SZ_Clear (&sv.datagram);
+}
+
+/*
+=============================================================================
+
+The PVS must include a small area around the client to allow head bobbing
+or other small motion on the client side.  Otherwise, a bob might cause an
+entity that should be visible to not show up, especially when the bob
+crosses a waterline.
+
+=============================================================================
+*/
+
+int		fatbytes;
+byte	fatpvs[MAX_MAP_LEAFS/8];
+
+void SV_AddToFatPVS (vec3_t org, mnode_t *node)
+{
+	int		i;
+	byte	*pvs;
+	mplane_t	*plane;
+	float	d;
+
+	while (1)
+	{
+	// if this is a leaf, accumulate the pvs bits
+		if (node->contents < 0)
+		{
+			if (node->contents != CONTENTS_SOLID)
+			{
+				pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel);
+				for (i=0 ; i<fatbytes ; i++)
+					fatpvs[i] |= pvs[i];
+			}
+			return;
+		}
+	
+		plane = node->plane;
+		d = DotProduct (org, plane->normal) - plane->dist;
+		if (d > 8)
+			node = node->children[0];
+		else if (d < -8)
+			node = node->children[1];
+		else
+		{	// go down both
+			SV_AddToFatPVS (org, node->children[0]);
+			node = node->children[1];
+		}
+	}
+}
+
+/*
+=============
+SV_FatPVS
+
+Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
+given point.
+=============
+*/
+byte *SV_FatPVS (vec3_t org)
+{
+	fatbytes = (sv.worldmodel->numleafs+31)>>3;
+	Q_memset (fatpvs, 0, fatbytes);
+	SV_AddToFatPVS (org, sv.worldmodel->nodes);
+	return fatpvs;
+}
+
+//=============================================================================
+
+
+/*
+=============
+SV_WriteEntitiesToClient
+
+=============
+*/
+void SV_WriteEntitiesToClient (edict_t	*clent, sizebuf_t *msg)
+{
+	int		e, i;
+	int		bits;
+	byte	*pvs;
+	vec3_t	org;
+	float	miss;
+	edict_t	*ent;
+
+// find the client's PVS
+	VectorAdd (clent->v.origin, clent->v.view_ofs, org);
+	pvs = SV_FatPVS (org);
+
+// send over all entities (excpet the client) that touch the pvs
+	ent = NEXT_EDICT(sv.edicts);
+	for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+	{
+#ifdef QUAKE2
+		// don't send if flagged for NODRAW and there are no lighting effects
+		if (ent->v.effects == EF_NODRAW)
+			continue;
+#endif
+
+// ignore if not touching a PV leaf
+		if (ent != clent)	// clent is ALLWAYS sent
+		{
+// ignore ents without visible models
+			if (!ent->v.modelindex || !pr_strings[ent->v.model])
+				continue;
+
+			for (i=0 ; i < ent->num_leafs ; i++)
+				if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
+					break;
+				
+			if (i == ent->num_leafs)
+				continue;		// not visible
+		}
+
+		if (msg->maxsize - msg->cursize < 16)
+		{
+			Con_Printf ("packet overflow\n");
+			return;
+		}
+
+// send an update
+		bits = 0;
+		
+		for (i=0 ; i<3 ; i++)
+		{
+			miss = ent->v.origin[i] - ent->baseline.origin[i];
+			if ( miss < -0.1 || miss > 0.1 )
+				bits |= U_ORIGIN1<<i;
+		}
+
+		if ( ent->v.angles[0] != ent->baseline.angles[0] )
+			bits |= U_ANGLE1;
+			
+		if ( ent->v.angles[1] != ent->baseline.angles[1] )
+			bits |= U_ANGLE2;
+			
+		if ( ent->v.angles[2] != ent->baseline.angles[2] )
+			bits |= U_ANGLE3;
+			
+		if (ent->v.movetype == MOVETYPE_STEP)
+			bits |= U_NOLERP;	// don't mess up the step animation
+	
+		if (ent->baseline.colormap != ent->v.colormap)
+			bits |= U_COLORMAP;
+			
+		if (ent->baseline.skin != ent->v.skin)
+			bits |= U_SKIN;
+			
+		if (ent->baseline.frame != ent->v.frame)
+			bits |= U_FRAME;
+		
+		if (ent->baseline.effects != ent->v.effects)
+			bits |= U_EFFECTS;
+		
+		if (ent->baseline.modelindex != ent->v.modelindex)
+			bits |= U_MODEL;
+
+		if (e >= 256)
+			bits |= U_LONGENTITY;
+			
+		if (bits >= 256)
+			bits |= U_MOREBITS;
+
+	//
+	// write the message
+	//
+		MSG_WriteByte (msg,bits | U_SIGNAL);
+		
+		if (bits & U_MOREBITS)
+			MSG_WriteByte (msg, bits>>8);
+		if (bits & U_LONGENTITY)
+			MSG_WriteShort (msg,e);
+		else
+			MSG_WriteByte (msg,e);
+
+		if (bits & U_MODEL)
+			MSG_WriteByte (msg,	ent->v.modelindex);
+		if (bits & U_FRAME)
+			MSG_WriteByte (msg, ent->v.frame);
+		if (bits & U_COLORMAP)
+			MSG_WriteByte (msg, ent->v.colormap);
+		if (bits & U_SKIN)
+			MSG_WriteByte (msg, ent->v.skin);
+		if (bits & U_EFFECTS)
+			MSG_WriteByte (msg, ent->v.effects);
+		if (bits & U_ORIGIN1)
+			MSG_WriteCoord (msg, ent->v.origin[0]);		
+		if (bits & U_ANGLE1)
+			MSG_WriteAngle(msg, ent->v.angles[0]);
+		if (bits & U_ORIGIN2)
+			MSG_WriteCoord (msg, ent->v.origin[1]);
+		if (bits & U_ANGLE2)
+			MSG_WriteAngle(msg, ent->v.angles[1]);
+		if (bits & U_ORIGIN3)
+			MSG_WriteCoord (msg, ent->v.origin[2]);
+		if (bits & U_ANGLE3)
+			MSG_WriteAngle(msg, ent->v.angles[2]);
+	}
+}
+
+/*
+=============
+SV_CleanupEnts
+
+=============
+*/
+void SV_CleanupEnts (void)
+{
+	int		e;
+	edict_t	*ent;
+	
+	ent = NEXT_EDICT(sv.edicts);
+	for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+	{
+		ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
+	}
+
+}
+
+/*
+==================
+SV_WriteClientdataToMessage
+
+==================
+*/
+void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg)
+{
+	int		bits;
+	int		i;
+	edict_t	*other;
+	int		items;
+#ifndef QUAKE2
+	eval_t	*val;
+#endif
+
+//
+// send a damage message
+//
+	if (ent->v.dmg_take || ent->v.dmg_save)
+	{
+		other = PROG_TO_EDICT(ent->v.dmg_inflictor);
+		MSG_WriteByte (msg, svc_damage);
+		MSG_WriteByte (msg, ent->v.dmg_save);
+		MSG_WriteByte (msg, ent->v.dmg_take);
+		for (i=0 ; i<3 ; i++)
+			MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
+	
+		ent->v.dmg_take = 0;
+		ent->v.dmg_save = 0;
+	}
+
+//
+// send the current viewpos offset from the view entity
+//
+	SV_SetIdealPitch ();		// how much to look up / down ideally
+
+// a fixangle might get lost in a dropped packet.  Oh well.
+	if ( ent->v.fixangle )
+	{
+		MSG_WriteByte (msg, svc_setangle);
+		for (i=0 ; i < 3 ; i++)
+			MSG_WriteAngle (msg, ent->v.angles[i] );
+		ent->v.fixangle = 0;
+	}
+
+	bits = 0;
+	
+	if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT)
+		bits |= SU_VIEWHEIGHT;
+		
+	if (ent->v.idealpitch)
+		bits |= SU_IDEALPITCH;
+
+// stuff the sigil bits into the high bits of items for sbar, or else
+// mix in items2
+#ifdef QUAKE2
+	items = (int)ent->v.items | ((int)ent->v.items2 << 23);
+#else
+	val = GetEdictFieldValue(ent, "items2");
+
+	if (val)
+		items = (int)ent->v.items | ((int)val->_float << 23);
+	else
+		items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28);
+#endif
+
+	bits |= SU_ITEMS;
+	
+	if ( (int)ent->v.flags & FL_ONGROUND)
+		bits |= SU_ONGROUND;
+	
+	if ( ent->v.waterlevel >= 2)
+		bits |= SU_INWATER;
+	
+	for (i=0 ; i<3 ; i++)
+	{
+		if (ent->v.punchangle[i])
+			bits |= (SU_PUNCH1<<i);
+		if (ent->v.velocity[i])
+			bits |= (SU_VELOCITY1<<i);
+	}
+	
+	if (ent->v.weaponframe)
+		bits |= SU_WEAPONFRAME;
+
+	if (ent->v.armorvalue)
+		bits |= SU_ARMOR;
+
+//	if (ent->v.weapon)
+		bits |= SU_WEAPON;
+
+// send the data
+
+	MSG_WriteByte (msg, svc_clientdata);
+	MSG_WriteShort (msg, bits);
+
+	if (bits & SU_VIEWHEIGHT)
+		MSG_WriteChar (msg, ent->v.view_ofs[2]);
+
+	if (bits & SU_IDEALPITCH)
+		MSG_WriteChar (msg, ent->v.idealpitch);
+
+	for (i=0 ; i<3 ; i++)
+	{
+		if (bits & (SU_PUNCH1<<i))
+			MSG_WriteChar (msg, ent->v.punchangle[i]);
+		if (bits & (SU_VELOCITY1<<i))
+			MSG_WriteChar (msg, ent->v.velocity[i]/16);
+	}
+
+// [always sent]	if (bits & SU_ITEMS)
+	MSG_WriteLong (msg, items);
+
+	if (bits & SU_WEAPONFRAME)
+		MSG_WriteByte (msg, ent->v.weaponframe);
+	if (bits & SU_ARMOR)
+		MSG_WriteByte (msg, ent->v.armorvalue);
+	if (bits & SU_WEAPON)
+		MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel));
+	
+	MSG_WriteShort (msg, ent->v.health);
+	MSG_WriteByte (msg, ent->v.currentammo);
+	MSG_WriteByte (msg, ent->v.ammo_shells);
+	MSG_WriteByte (msg, ent->v.ammo_nails);
+	MSG_WriteByte (msg, ent->v.ammo_rockets);
+	MSG_WriteByte (msg, ent->v.ammo_cells);
+
+	if (standard_quake)
+	{
+		MSG_WriteByte (msg, ent->v.weapon);
+	}
+	else
+	{
+		for(i=0;i<32;i++)
+		{
+			if ( ((int)ent->v.weapon) & (1<<i) )
+			{
+				MSG_WriteByte (msg, i);
+				break;
+			}
+		}
+	}
+}
+
+/*
+=======================
+SV_SendClientDatagram
+=======================
+*/
+qboolean SV_SendClientDatagram (client_t *client)
+{
+	byte		buf[MAX_DATAGRAM];
+	sizebuf_t	msg;
+	
+	msg.data = buf;
+	msg.maxsize = sizeof(buf);
+	msg.cursize = 0;
+
+	MSG_WriteByte (&msg, svc_time);
+	MSG_WriteFloat (&msg, sv.time);
+
+// add the client specific data to the datagram
+	SV_WriteClientdataToMessage (client->edict, &msg);
+
+	SV_WriteEntitiesToClient (client->edict, &msg);
+
+// copy the server datagram if there is space
+	if (msg.cursize + sv.datagram.cursize < msg.maxsize)
+		SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize);
+
+// send the datagram
+	if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
+	{
+		SV_DropClient (true);// if the message couldn't send, kick off
+		return false;
+	}
+	
+	return true;
+}
+
+/*
+=======================
+SV_UpdateToReliableMessages
+=======================
+*/
+void SV_UpdateToReliableMessages (void)
+{
+	int			i, j;
+	client_t *client;
+
+// check for changes to be sent over the reliable streams
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+	{
+		if (host_client->old_frags != host_client->edict->v.frags)
+		{
+			for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+			{
+				if (!client->active)
+					continue;
+				MSG_WriteByte (&client->message, svc_updatefrags);
+				MSG_WriteByte (&client->message, i);
+				MSG_WriteShort (&client->message, host_client->edict->v.frags);
+			}
+
+			host_client->old_frags = host_client->edict->v.frags;
+		}
+	}
+	
+	for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
+	{
+		if (!client->active)
+			continue;
+		SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
+	}
+
+	SZ_Clear (&sv.reliable_datagram);
+}
+
+
+/*
+=======================
+SV_SendNop
+
+Send a nop message without trashing or sending the accumulated client
+message buffer
+=======================
+*/
+void SV_SendNop (client_t *client)
+{
+	sizebuf_t	msg;
+	byte		buf[4];
+	
+	msg.data = buf;
+	msg.maxsize = sizeof(buf);
+	msg.cursize = 0;
+
+	MSG_WriteChar (&msg, svc_nop);
+
+	if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
+		SV_DropClient (true);	// if the message couldn't send, kick off
+	client->last_message = realtime;
+}
+
+/*
+=======================
+SV_SendClientMessages
+=======================
+*/
+void SV_SendClientMessages (void)
+{
+	int			i;
+	
+// update frags, names, etc
+	SV_UpdateToReliableMessages ();
+
+// build individual updates
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+	{
+		if (!host_client->active)
+			continue;
+
+		if (host_client->spawned)
+		{
+			if (!SV_SendClientDatagram (host_client))
+				continue;
+		}
+		else
+		{
+		// the player isn't totally in the game yet
+		// send small keepalive messages if too much time has passed
+		// send a full message when the next signon stage has been requested
+		// some other message data (name changes, etc) may accumulate 
+		// between signon stages
+			if (!host_client->sendsignon)
+			{
+				if (realtime - host_client->last_message > 5)
+					SV_SendNop (host_client);
+				continue;	// don't send out non-signon messages
+			}
+		}
+
+		// check for an overflowed message.  Should only happen
+		// on a very fucked up connection that backs up a lot, then
+		// changes level
+		if (host_client->message.overflowed)
+		{
+			SV_DropClient (true);
+			host_client->message.overflowed = false;
+			continue;
+		}
+			
+		if (host_client->message.cursize || host_client->dropasap)
+		{
+			if (!NET_CanSendMessage (host_client->netconnection))
+			{
+//				I_Printf ("can't write\n");
+				continue;
+			}
+
+			if (host_client->dropasap)
+				SV_DropClient (false);	// went to another level
+			else
+			{
+				if (NET_SendMessage (host_client->netconnection
+				, &host_client->message) == -1)
+					SV_DropClient (true);	// if the message couldn't send, kick off
+				SZ_Clear (&host_client->message);
+				host_client->last_message = realtime;
+				host_client->sendsignon = false;
+			}
+		}
+	}
+	
+	
+// clear muzzle flashes
+	SV_CleanupEnts ();
+}
+
+
+/*
+==============================================================================
+
+SERVER SPAWNING
+
+==============================================================================
+*/
+
+/*
+================
+SV_ModelIndex
+
+================
+*/
+int SV_ModelIndex (char *name)
+{
+	int		i;
+	
+	if (!name || !name[0])
+		return 0;
+
+	for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++)
+		if (!strcmp(sv.model_precache[i], name))
+			return i;
+	if (i==MAX_MODELS || !sv.model_precache[i])
+		Sys_Error ("SV_ModelIndex: model %s not precached", name);
+	return i;
+}
+
+/*
+================
+SV_CreateBaseline
+
+================
+*/
+void SV_CreateBaseline (void)
+{
+	int			i;
+	edict_t			*svent;
+	int				entnum;	
+		
+	for (entnum = 0; entnum < sv.num_edicts ; entnum++)
+	{
+	// get the current server version
+		svent = EDICT_NUM(entnum);
+		if (svent->free)
+			continue;
+		if (entnum > svs.maxclients && !svent->v.modelindex)
+			continue;
+
+	//
+	// create entity baseline
+	//
+		VectorCopy (svent->v.origin, svent->baseline.origin);
+		VectorCopy (svent->v.angles, svent->baseline.angles);
+		svent->baseline.frame = svent->v.frame;
+		svent->baseline.skin = svent->v.skin;
+		if (entnum > 0 && entnum <= svs.maxclients)
+		{
+			svent->baseline.colormap = entnum;
+			svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
+		}
+		else
+		{
+			svent->baseline.colormap = 0;
+			svent->baseline.modelindex =
+				SV_ModelIndex(pr_strings + svent->v.model);
+		}
+		
+	//
+	// add to the message
+	//
+		MSG_WriteByte (&sv.signon,svc_spawnbaseline);		
+		MSG_WriteShort (&sv.signon,entnum);
+
+		MSG_WriteByte (&sv.signon, svent->baseline.modelindex);
+		MSG_WriteByte (&sv.signon, svent->baseline.frame);
+		MSG_WriteByte (&sv.signon, svent->baseline.colormap);
+		MSG_WriteByte (&sv.signon, svent->baseline.skin);
+		for (i=0 ; i<3 ; i++)
+		{
+			MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
+			MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
+		}
+	}
+}
+
+
+/*
+================
+SV_SendReconnect
+
+Tell all the clients that the server is changing levels
+================
+*/
+void SV_SendReconnect (void)
+{
+	char	data[128];
+	sizebuf_t	msg;
+
+	msg.data = data;
+	msg.cursize = 0;
+	msg.maxsize = sizeof(data);
+
+	MSG_WriteChar (&msg, svc_stufftext);
+	MSG_WriteString (&msg, "reconnect\n");
+	NET_SendToAll (&msg, 5);
+	
+	if (cls.state != ca_dedicated)
+#ifdef QUAKE2
+		Cbuf_InsertText ("reconnect\n");
+#else
+		Cmd_ExecuteString ("reconnect\n", src_command);
+#endif
+}
+
+
+/*
+================
+SV_SaveSpawnparms
+
+Grabs the current state of each client for saving across the
+transition to another level
+================
+*/
+void SV_SaveSpawnparms (void)
+{
+	int		i, j;
+
+	svs.serverflags = pr_global_struct->serverflags;
+
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+	{
+		if (!host_client->active)
+			continue;
+
+	// call the progs to get default spawn parms for the new client
+		pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
+		PR_ExecuteProgram (pr_global_struct->SetChangeParms);
+		for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
+			host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
+	}
+}
+
+
+/*
+================
+SV_SpawnServer
+
+This is called at the start of each level
+================
+*/
+extern float		scr_centertime_off;
+
+#ifdef QUAKE2
+void SV_SpawnServer (char *server, char *startspot)
+#else
+void SV_SpawnServer (char *server)
+#endif
+{
+	edict_t		*ent;
+	int			i;
+
+	// let's not have any servers with no name
+	if (hostname.string[0] == 0)
+		Cvar_Set ("hostname", "UNNAMED");
+	scr_centertime_off = 0;
+
+	Con_DPrintf ("SpawnServer: %s\n",server);
+	svs.changelevel_issued = false;		// now safe to issue another
+
+//
+// tell all connected clients that we are going to a new level
+//
+	if (sv.active)
+	{
+		SV_SendReconnect ();
+	}
+
+//
+// make cvars consistant
+//
+	if (coop.value)
+		Cvar_SetValue ("deathmatch", 0);
+	current_skill = (int)(skill.value + 0.5);
+	if (current_skill < 0)
+		current_skill = 0;
+	if (current_skill > 3)
+		current_skill = 3;
+
+	Cvar_SetValue ("skill", (float)current_skill);
+	
+//
+// set up the new server
+//
+	Host_ClearMemory ();
+
+	memset (&sv, 0, sizeof(sv));
+
+	strcpy (sv.name, server);
+#ifdef QUAKE2
+	if (startspot)
+		strcpy(sv.startspot, startspot);
+#endif
+
+// load progs to get entity field count
+        //printf("progs 1");
+	PR_LoadProgs ();
+        //printf("progs 2");
+
+// allocate server memory
+	sv.max_edicts = MAX_EDICTS;
+	
+	sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts");
+
+	sv.datagram.maxsize = sizeof(sv.datagram_buf);
+	sv.datagram.cursize = 0;
+	sv.datagram.data = sv.datagram_buf;
+	
+	sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
+	sv.reliable_datagram.cursize = 0;
+	sv.reliable_datagram.data = sv.reliable_datagram_buf;
+	
+	sv.signon.maxsize = sizeof(sv.signon_buf);
+	sv.signon.cursize = 0;
+	sv.signon.data = sv.signon_buf;
+	
+        //printf("progs 3");
+// leave slots at start for clients only
+	sv.num_edicts = svs.maxclients+1;
+	for (i=0 ; i<svs.maxclients ; i++)
+	{
+		ent = EDICT_NUM(i+1);
+		svs.clients[i].edict = ent;
+	}
+	
+	sv.state = ss_loading;
+	sv.paused = false;
+
+	sv.time = 1.0;
+	
+	strcpy (sv.name, server);
+	sprintf (sv.modelname,"maps/%s.bsp", server);
+	sv.worldmodel = Mod_ForName (sv.modelname, false);
+	if (!sv.worldmodel)
+	{
+		Con_Printf ("Couldn't spawn server %s\n", sv.modelname);
+		sv.active = false;
+		return;
+	}
+	sv.models[1] = sv.worldmodel;
+	
+//
+// clear world interaction links
+//
+        //printf("progs 4");
+	SV_ClearWorld ();
+        //printf("progs 5");
+	
+	sv.sound_precache[0] = pr_strings;
+
+	sv.model_precache[0] = pr_strings;
+	sv.model_precache[1] = sv.modelname;
+	for (i=1 ; i<sv.worldmodel->numsubmodels ; i++)
+	{
+		sv.model_precache[1+i] = localmodels[i];
+		sv.models[i+1] = Mod_ForName (localmodels[i], false);
+	}
+
+//
+// load the rest of the entities
+//	
+        //printf("progs 6");
+	ent = EDICT_NUM(0);
+	memset (&ent->v, 0, progs->entityfields * 4);
+        //printf("progs 7");
+	ent->free = false;
+	ent->v.model = sv.worldmodel->name - pr_strings;
+	ent->v.modelindex = 1;		// world model
+	ent->v.solid = SOLID_BSP;
+	ent->v.movetype = MOVETYPE_PUSH;
+
+	if (coop.value)
+		pr_global_struct->coop = coop.value;
+	else
+		pr_global_struct->deathmatch = deathmatch.value;
+
+	pr_global_struct->mapname = sv.name - pr_strings;
+#ifdef QUAKE2
+	pr_global_struct->startspot = sv.startspot - pr_strings;
+#endif
+
+// serverflags are for cross level information (sigils)
+	pr_global_struct->serverflags = svs.serverflags;
+	
+        //printf("progs 8");
+	ED_LoadFromFile (sv.worldmodel->entities);
+
+        //printf("progs 9");
+	sv.active = true;
+
+// all setup is completed, any further precache statements are errors
+	sv.state = ss_active;
+	
+// run two frames to allow everything to settle
+	host_frametime = 0.1;
+	SV_Physics ();
+	SV_Physics ();
+
+// create a baseline for more efficient communications
+	SV_CreateBaseline ();
+
+// send serverinfo to all connected clients
+	for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+		if (host_client->active)
+			SV_SendServerinfo (host_client);
+	
+	Con_DPrintf ("Server spawned.\n");
+}
+
diff --git a/apps/plugins/sdl/progs/quake/sv_move.c b/apps/plugins/sdl/progs/quake/sv_move.c
new file mode 100644
index 0000000..a0078b9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sv_move.c
@@ -0,0 +1,427 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_move.c -- monster movement
+
+#include "quakedef.h"
+
+#define	STEPSIZE	18
+
+/*
+=============
+SV_CheckBottom
+
+Returns false if any part of the bottom of the entity is off an edge that
+is not a staircase.
+
+=============
+*/
+int c_yes, c_no;
+
+qboolean SV_CheckBottom (edict_t *ent)
+{
+	vec3_t	mins, maxs, start, stop;
+	trace_t	trace;
+	int		x, y;
+	float	mid, bottom;
+	
+	VectorAdd (ent->v.origin, ent->v.mins, mins);
+	VectorAdd (ent->v.origin, ent->v.maxs, maxs);
+
+// if all of the points under the corners are solid world, don't bother
+// with the tougher checks
+// the corners must be within 16 of the midpoint
+	start[2] = mins[2] - 1;
+	for	(x=0 ; x<=1 ; x++)
+		for	(y=0 ; y<=1 ; y++)
+		{
+			start[0] = x ? maxs[0] : mins[0];
+			start[1] = y ? maxs[1] : mins[1];
+			if (SV_PointContents (start) != CONTENTS_SOLID)
+				goto realcheck;
+		}
+
+	c_yes++;
+	return true;		// we got out easy
+
+realcheck:
+	c_no++;
+//
+// check it for real...
+//
+	start[2] = mins[2];
+	
+// the midpoint must be within 16 of the bottom
+	start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
+	start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
+	stop[2] = start[2] - 2*STEPSIZE;
+	trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent);
+
+	if (trace.fraction == 1.0)
+		return false;
+	mid = bottom = trace.endpos[2];
+	
+// the corners must be within 16 of the midpoint	
+	for	(x=0 ; x<=1 ; x++)
+		for	(y=0 ; y<=1 ; y++)
+		{
+			start[0] = stop[0] = x ? maxs[0] : mins[0];
+			start[1] = stop[1] = y ? maxs[1] : mins[1];
+			
+			trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent);
+			
+			if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
+				bottom = trace.endpos[2];
+			if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
+				return false;
+		}
+
+	c_yes++;
+	return true;
+}
+
+
+/*
+=============
+SV_movestep
+
+Called by monster program code.
+The move will be adjusted for slopes and stairs, but if the move isn't
+possible, no move is done, false is returned, and
+pr_global_struct->trace_normal is set to the normal of the blocking wall
+=============
+*/
+qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
+{
+	float		dz;
+	vec3_t		oldorg, neworg, end;
+	trace_t		trace;
+	int			i;
+	edict_t		*enemy;
+
+// try the move	
+	VectorCopy (ent->v.origin, oldorg);
+	VectorAdd (ent->v.origin, move, neworg);
+
+// flying monsters don't step up
+	if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) )
+	{
+	// try one move with vertical motion, then one without
+		for (i=0 ; i<2 ; i++)
+		{
+			VectorAdd (ent->v.origin, move, neworg);
+			enemy = PROG_TO_EDICT(ent->v.enemy);
+			if (i == 0 && enemy != sv.edicts)
+			{
+				dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
+				if (dz > 40)
+					neworg[2] -= 8;
+				if (dz < 30)
+					neworg[2] += 8;
+			}
+			trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent);
+	
+			if (trace.fraction == 1)
+			{
+				if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY )
+					return false;	// swim monster left water
+	
+				VectorCopy (trace.endpos, ent->v.origin);
+				if (relink)
+					SV_LinkEdict (ent, true);
+				return true;
+			}
+			
+			if (enemy == sv.edicts)
+				break;
+		}
+		
+		return false;
+	}
+
+// push down from a step height above the wished position
+	neworg[2] += STEPSIZE;
+	VectorCopy (neworg, end);
+	end[2] -= STEPSIZE*2;
+
+	trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
+
+	if (trace.allsolid)
+		return false;
+
+	if (trace.startsolid)
+	{
+		neworg[2] -= STEPSIZE;
+		trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent);
+		if (trace.allsolid || trace.startsolid)
+			return false;
+	}
+	if (trace.fraction == 1)
+	{
+	// if monster had the ground pulled out, go ahead and fall
+		if ( (int)ent->v.flags & FL_PARTIALGROUND )
+		{
+			VectorAdd (ent->v.origin, move, ent->v.origin);
+			if (relink)
+				SV_LinkEdict (ent, true);
+			ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+//	Con_Printf ("fall down\n"); 
+			return true;
+		}
+	
+		return false;		// walked off an edge
+	}
+
+// check point traces down for dangling corners
+	VectorCopy (trace.endpos, ent->v.origin);
+	
+	if (!SV_CheckBottom (ent))
+	{
+		if ( (int)ent->v.flags & FL_PARTIALGROUND )
+		{	// entity had floor mostly pulled out from underneath it
+			// and is trying to correct
+			if (relink)
+				SV_LinkEdict (ent, true);
+			return true;
+		}
+		VectorCopy (oldorg, ent->v.origin);
+		return false;
+	}
+
+	if ( (int)ent->v.flags & FL_PARTIALGROUND )
+	{
+//		Con_Printf ("back on ground\n"); 
+		ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND;
+	}
+	ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+
+// the move is ok
+	if (relink)
+		SV_LinkEdict (ent, true);
+	return true;
+}
+
+
+//============================================================================
+
+/*
+======================
+SV_StepDirection
+
+Turns to the movement direction, and walks the current distance if
+facing it.
+
+======================
+*/
+void PF_changeyaw (void);
+qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
+{
+	vec3_t		move, oldorigin;
+	float		delta;
+	
+	ent->v.ideal_yaw = yaw;
+	PF_changeyaw();
+	
+	yaw = yaw*M_PI*2 / 360;
+	move[0] = cos(yaw)*dist;
+	move[1] = sin(yaw)*dist;
+	move[2] = 0;
+
+	VectorCopy (ent->v.origin, oldorigin);
+	if (SV_movestep (ent, move, false))
+	{
+		delta = ent->v.angles[YAW] - ent->v.ideal_yaw;
+		if (delta > 45 && delta < 315)
+		{		// not turned far enough, so don't take the step
+			VectorCopy (oldorigin, ent->v.origin);
+		}
+		SV_LinkEdict (ent, true);
+		return true;
+	}
+	SV_LinkEdict (ent, true);
+		
+	return false;
+}
+
+/*
+======================
+SV_FixCheckBottom
+
+======================
+*/
+void SV_FixCheckBottom (edict_t *ent)
+{
+//	Con_Printf ("SV_FixCheckBottom\n");
+	
+	ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND;
+}
+
+
+
+/*
+================
+SV_NewChaseDir
+
+================
+*/
+#define	DI_NODIR	-1
+void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
+{
+	float		deltax,deltay;
+	float			d[3];
+	float		tdir, olddir, turnaround;
+
+	olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 );
+	turnaround = anglemod(olddir - 180);
+
+	deltax = enemy->v.origin[0] - actor->v.origin[0];
+	deltay = enemy->v.origin[1] - actor->v.origin[1];
+	if (deltax>10)
+		d[1]= 0;
+	else if (deltax<-10)
+		d[1]= 180;
+	else
+		d[1]= DI_NODIR;
+	if (deltay<-10)
+		d[2]= 270;
+	else if (deltay>10)
+		d[2]= 90;
+	else
+		d[2]= DI_NODIR;
+
+// try direct route
+	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
+	{
+		if (d[1] == 0)
+			tdir = d[2] == 90 ? 45 : 315;
+		else
+			tdir = d[2] == 90 ? 135 : 215;
+			
+		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
+			return;
+	}
+
+// try other directions
+	if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
+	{
+		tdir=d[1];
+		d[1]=d[2];
+		d[2]=tdir;
+	}
+
+	if (d[1]!=DI_NODIR && d[1]!=turnaround 
+	&& SV_StepDirection(actor, d[1], dist))
+			return;
+
+	if (d[2]!=DI_NODIR && d[2]!=turnaround
+	&& SV_StepDirection(actor, d[2], dist))
+			return;
+
+/* there is no direct path to the player, so pick another direction */
+
+	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
+			return;
+
+	if (rand()&1) 	/*randomly determine direction of search*/
+	{
+		for (tdir=0 ; tdir<=315 ; tdir += 45)
+			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
+					return;
+	}
+	else
+	{
+		for (tdir=315 ; tdir >=0 ; tdir -= 45)
+			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
+					return;
+	}
+
+	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
+			return;
+
+	actor->v.ideal_yaw = olddir;		// can't move
+
+// if a bridge was pulled out from underneath a monster, it may not have
+// a valid standing position at all
+
+	if (!SV_CheckBottom (actor))
+		SV_FixCheckBottom (actor);
+
+}
+
+/*
+======================
+SV_CloseEnough
+
+======================
+*/
+qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
+{
+	int		i;
+	
+	for (i=0 ; i<3 ; i++)
+	{
+		if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
+			return false;
+		if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
+			return false;
+	}
+	return true;
+}
+
+/*
+======================
+SV_MoveToGoal
+
+======================
+*/
+void SV_MoveToGoal (void)
+{
+	edict_t		*ent, *goal;
+	float		dist;
+#ifdef QUAKE2
+	edict_t		*enemy;
+#endif
+
+	ent = PROG_TO_EDICT(pr_global_struct->self);
+	goal = PROG_TO_EDICT(ent->v.goalentity);
+	dist = G_FLOAT(OFS_PARM0);
+
+	if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
+	{
+		G_FLOAT(OFS_RETURN) = 0;
+		return;
+	}
+
+// if the next step hits the enemy, return immediately
+#ifdef QUAKE2
+	enemy = PROG_TO_EDICT(ent->v.enemy);
+	if (enemy != sv.edicts &&  SV_CloseEnough (ent, enemy, dist) )
+#else
+	if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts &&  SV_CloseEnough (ent, goal, dist) )
+#endif
+		return;
+
+// bump around...
+	if ( (rand()&3)==1 ||
+	!SV_StepDirection (ent, ent->v.ideal_yaw, dist))
+	{
+		SV_NewChaseDir (ent, goal, dist);
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/sv_phys.c b/apps/plugins/sdl/progs/quake/sv_phys.c
new file mode 100644
index 0000000..e13c0e4
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sv_phys.c
@@ -0,0 +1,1617 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_phys.c
+
+#include "quakedef.h"
+
+/*
+
+
+pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
+
+onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects 
+
+doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
+bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
+corpses are SOLID_NOT and MOVETYPE_TOSS
+crates are SOLID_BBOX and MOVETYPE_TOSS
+walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
+flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
+
+solid_edge items only clip against bsp models.
+
+*/
+
+cvar_t	sv_friction = {"sv_friction","4",false,true};
+cvar_t	sv_stopspeed = {"sv_stopspeed","100"};
+cvar_t	sv_gravity = {"sv_gravity","800",false,true};
+cvar_t	sv_maxvelocity = {"sv_maxvelocity","2000"};
+cvar_t	sv_nostep = {"sv_nostep","0"};
+
+#ifdef QUAKE2
+static	vec3_t	vec_origin = {0.0, 0.0, 0.0};
+#endif
+
+#define	MOVE_EPSILON	0.01
+
+void SV_Physics_Toss (edict_t *ent);
+
+/*
+================
+SV_CheckAllEnts
+================
+*/
+void SV_CheckAllEnts (void)
+{
+	int			e;
+	edict_t		*check;
+
+// see if any solid entities are inside the final position
+	check = NEXT_EDICT(sv.edicts);
+	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+	{
+		if (check->free)
+			continue;
+		if (check->v.movetype == MOVETYPE_PUSH
+		|| check->v.movetype == MOVETYPE_NONE
+#ifdef QUAKE2
+		|| check->v.movetype == MOVETYPE_FOLLOW
+#endif
+		|| check->v.movetype == MOVETYPE_NOCLIP)
+			continue;
+
+		if (SV_TestEntityPosition (check))
+			Con_Printf ("entity in invalid position\n");
+	}
+}
+
+/*
+================
+SV_CheckVelocity
+================
+*/
+void SV_CheckVelocity (edict_t *ent)
+{
+	int		i;
+
+//
+// bound velocity
+//
+	for (i=0 ; i<3 ; i++)
+	{
+		if (IS_NAN(ent->v.velocity[i]))
+		{
+			Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
+			ent->v.velocity[i] = 0;
+		}
+		if (IS_NAN(ent->v.origin[i]))
+		{
+			Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
+			ent->v.origin[i] = 0;
+		}
+		if (ent->v.velocity[i] > sv_maxvelocity.value)
+			ent->v.velocity[i] = sv_maxvelocity.value;
+		else if (ent->v.velocity[i] < -sv_maxvelocity.value)
+			ent->v.velocity[i] = -sv_maxvelocity.value;
+	}
+}
+
+/*
+=============
+SV_RunThink
+
+Runs thinking code if time.  There is some play in the exact time the think
+function will be called, because it is called before any movement is done
+in a frame.  Not used for pushmove objects, because they must be exact.
+Returns false if the entity removed itself.
+=============
+*/
+qboolean SV_RunThink (edict_t *ent)
+{
+	float	thinktime;
+
+	thinktime = ent->v.nextthink;
+	if (thinktime <= 0 || thinktime > sv.time + host_frametime)
+		return true;
+		
+	if (thinktime < sv.time)
+		thinktime = sv.time;	// don't let things stay in the past.
+								// it is possible to start that way
+								// by a trigger with a local time.
+	ent->v.nextthink = 0;
+	pr_global_struct->time = thinktime;
+	pr_global_struct->self = EDICT_TO_PROG(ent);
+	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+	PR_ExecuteProgram (ent->v.think);
+	return !ent->free;
+}
+
+/*
+==================
+SV_Impact
+
+Two entities have touched, so run their touch functions
+==================
+*/
+void SV_Impact (edict_t *e1, edict_t *e2)
+{
+	int		old_self, old_other;
+	
+	old_self = pr_global_struct->self;
+	old_other = pr_global_struct->other;
+	
+	pr_global_struct->time = sv.time;
+	if (e1->v.touch && e1->v.solid != SOLID_NOT)
+	{
+		pr_global_struct->self = EDICT_TO_PROG(e1);
+		pr_global_struct->other = EDICT_TO_PROG(e2);
+		PR_ExecuteProgram (e1->v.touch);
+	}
+	
+	if (e2->v.touch && e2->v.solid != SOLID_NOT)
+	{
+		pr_global_struct->self = EDICT_TO_PROG(e2);
+		pr_global_struct->other = EDICT_TO_PROG(e1);
+		PR_ExecuteProgram (e2->v.touch);
+	}
+
+	pr_global_struct->self = old_self;
+	pr_global_struct->other = old_other;
+}
+
+
+/*
+==================
+ClipVelocity
+
+Slide off of the impacting object
+returns the blocked flags (1 = floor, 2 = step / wall)
+==================
+*/
+#define	STOP_EPSILON	0.1
+
+int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
+{
+	float	backoff;
+	float	change;
+	int		i, blocked;
+	
+	blocked = 0;
+	if (normal[2] > 0)
+		blocked |= 1;		// floor
+	if (!normal[2])
+		blocked |= 2;		// step
+	
+	backoff = DotProduct (in, normal) * overbounce;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		change = normal[i]*backoff;
+		out[i] = in[i] - change;
+		if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
+			out[i] = 0;
+	}
+	
+	return blocked;
+}
+
+
+/*
+============
+SV_FlyMove
+
+The basic solid body movement clip that slides along multiple planes
+Returns the clipflags if the velocity was modified (hit something solid)
+1 = floor
+2 = wall / step
+4 = dead stop
+If steptrace is not NULL, the trace of any vertical wall hit will be stored
+============
+*/
+#define	MAX_CLIP_PLANES	5
+int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
+{
+	int			bumpcount, numbumps;
+	vec3_t		dir;
+	float		d;
+	int			numplanes;
+	vec3_t		planes[MAX_CLIP_PLANES];
+	vec3_t		primal_velocity, original_velocity, new_velocity;
+	int			i, j;
+	trace_t		trace;
+	vec3_t		end;
+	float		time_left;
+	int			blocked;
+	
+	numbumps = 4;
+	
+	blocked = 0;
+	VectorCopy (ent->v.velocity, original_velocity);
+	VectorCopy (ent->v.velocity, primal_velocity);
+	numplanes = 0;
+	
+	time_left = time;
+
+	for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
+	{
+		if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
+			break;
+
+		for (i=0 ; i<3 ; i++)
+			end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
+
+		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
+
+		if (trace.allsolid)
+		{	// entity is trapped in another solid
+			VectorCopy (vec3_origin, ent->v.velocity);
+			return 3;
+		}
+
+		if (trace.fraction > 0)
+		{	// actually covered some distance
+			VectorCopy (trace.endpos, ent->v.origin);
+			VectorCopy (ent->v.velocity, original_velocity);
+			numplanes = 0;
+		}
+
+		if (trace.fraction == 1)
+			 break;		// moved the entire distance
+
+		if (!trace.ent)
+			Sys_Error ("SV_FlyMove: !trace.ent");
+
+		if (trace.plane.normal[2] > 0.7)
+		{
+			blocked |= 1;		// floor
+			if (trace.ent->v.solid == SOLID_BSP)
+			{
+				ent->v.flags =	(int)ent->v.flags | FL_ONGROUND;
+				ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+			}
+		}
+		if (!trace.plane.normal[2])
+		{
+			blocked |= 2;		// step
+			if (steptrace)
+				*steptrace = trace;	// save for player extrafriction
+		}
+
+//
+// run the impact function
+//
+		SV_Impact (ent, trace.ent);
+		if (ent->free)
+			break;		// removed by the impact function
+
+		
+		time_left -= time_left * trace.fraction;
+		
+	// cliped to another plane
+		if (numplanes >= MAX_CLIP_PLANES)
+		{	// this shouldn't really happen
+			VectorCopy (vec3_origin, ent->v.velocity);
+			return 3;
+		}
+
+		VectorCopy (trace.plane.normal, planes[numplanes]);
+		numplanes++;
+
+//
+// modify original_velocity so it parallels all of the clip planes
+//
+		for (i=0 ; i<numplanes ; i++)
+		{
+			ClipVelocity (original_velocity, planes[i], new_velocity, 1);
+			for (j=0 ; j<numplanes ; j++)
+				if (j != i)
+				{
+					if (DotProduct (new_velocity, planes[j]) < 0)
+						break;	// not ok
+				}
+			if (j == numplanes)
+				break;
+		}
+		
+		if (i != numplanes)
+		{	// go along this plane
+			VectorCopy (new_velocity, ent->v.velocity);
+		}
+		else
+		{	// go along the crease
+			if (numplanes != 2)
+			{
+//				Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
+				VectorCopy (vec3_origin, ent->v.velocity);
+				return 7;
+			}
+			CrossProduct (planes[0], planes[1], dir);
+			d = DotProduct (dir, ent->v.velocity);
+			VectorScale (dir, d, ent->v.velocity);
+		}
+
+//
+// if original velocity is against the original velocity, stop dead
+// to avoid tiny occilations in sloping corners
+//
+		if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
+		{
+			VectorCopy (vec3_origin, ent->v.velocity);
+			return blocked;
+		}
+	}
+
+	return blocked;
+}
+
+
+/*
+============
+SV_AddGravity
+
+============
+*/
+void SV_AddGravity (edict_t *ent)
+{
+	float	ent_gravity;
+
+#ifdef QUAKE2
+	if (ent->v.gravity)
+		ent_gravity = ent->v.gravity;
+	else
+		ent_gravity = 1.0;
+#else
+	eval_t	*val;
+
+	val = GetEdictFieldValue(ent, "gravity");
+	if (val && val->_float)
+		ent_gravity = val->_float;
+	else
+		ent_gravity = 1.0;
+#endif
+	ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime;
+}
+
+
+/*
+===============================================================================
+
+PUSHMOVE
+
+===============================================================================
+*/
+
+/*
+============
+SV_PushEntity
+
+Does not change the entities velocity at all
+============
+*/
+trace_t SV_PushEntity (edict_t *ent, vec3_t push)
+{
+	trace_t	trace;
+	vec3_t	end;
+		
+	VectorAdd (ent->v.origin, push, end);
+
+	if (ent->v.movetype == MOVETYPE_FLYMISSILE)
+		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
+	else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
+	// only clip against bmodels
+		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
+	else
+		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);	
+	
+	VectorCopy (trace.endpos, ent->v.origin);
+	SV_LinkEdict (ent, true);
+
+	if (trace.ent)
+		SV_Impact (ent, trace.ent);		
+
+	return trace;
+}					
+
+
+/*
+============
+SV_PushMove
+
+============
+*/
+void SV_PushMove (edict_t *pusher, float movetime)
+{
+	int			i, e;
+	edict_t		*check, *block;
+	vec3_t		mins, maxs, move;
+	vec3_t		entorig, pushorig;
+	int			num_moved;
+	edict_t		*moved_edict[MAX_EDICTS];
+	vec3_t		moved_from[MAX_EDICTS];
+
+	if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
+	{
+		pusher->v.ltime += movetime;
+		return;
+	}
+
+	for (i=0 ; i<3 ; i++)
+	{
+		move[i] = pusher->v.velocity[i] * movetime;
+		mins[i] = pusher->v.absmin[i] + move[i];
+		maxs[i] = pusher->v.absmax[i] + move[i];
+	}
+
+	VectorCopy (pusher->v.origin, pushorig);
+	
+// move the pusher to it's final position
+
+	VectorAdd (pusher->v.origin, move, pusher->v.origin);
+	pusher->v.ltime += movetime;
+	SV_LinkEdict (pusher, false);
+
+
+// see if any solid entities are inside the final position
+	num_moved = 0;
+	check = NEXT_EDICT(sv.edicts);
+	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+	{
+		if (check->free)
+			continue;
+		if (check->v.movetype == MOVETYPE_PUSH
+		|| check->v.movetype == MOVETYPE_NONE
+#ifdef QUAKE2
+		|| check->v.movetype == MOVETYPE_FOLLOW
+#endif
+		|| check->v.movetype == MOVETYPE_NOCLIP)
+			continue;
+
+	// if the entity is standing on the pusher, it will definately be moved
+		if ( ! ( ((int)check->v.flags & FL_ONGROUND)
+		&& PROG_TO_EDICT(check->v.groundentity) == pusher) )
+		{
+			if ( check->v.absmin[0] >= maxs[0]
+			|| check->v.absmin[1] >= maxs[1]
+			|| check->v.absmin[2] >= maxs[2]
+			|| check->v.absmax[0] <= mins[0]
+			|| check->v.absmax[1] <= mins[1]
+			|| check->v.absmax[2] <= mins[2] )
+				continue;
+
+		// see if the ent's bbox is inside the pusher's final position
+			if (!SV_TestEntityPosition (check))
+				continue;
+		}
+
+	// remove the onground flag for non-players
+		if (check->v.movetype != MOVETYPE_WALK)
+			check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
+		
+		VectorCopy (check->v.origin, entorig);
+		VectorCopy (check->v.origin, moved_from[num_moved]);
+		moved_edict[num_moved] = check;
+		num_moved++;
+
+		// try moving the contacted entity 
+		pusher->v.solid = SOLID_NOT;
+		SV_PushEntity (check, move);
+		pusher->v.solid = SOLID_BSP;
+
+	// if it is still inside the pusher, block
+		block = SV_TestEntityPosition (check);
+		if (block)
+		{	// fail the move
+			if (check->v.mins[0] == check->v.maxs[0])
+				continue;
+			if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
+			{	// corpse
+				check->v.mins[0] = check->v.mins[1] = 0;
+				VectorCopy (check->v.mins, check->v.maxs);
+				continue;
+			}
+			
+			VectorCopy (entorig, check->v.origin);
+			SV_LinkEdict (check, true);
+
+			VectorCopy (pushorig, pusher->v.origin);
+			SV_LinkEdict (pusher, false);
+			pusher->v.ltime -= movetime;
+
+			// if the pusher has a "blocked" function, call it
+			// otherwise, just stay in place until the obstacle is gone
+			if (pusher->v.blocked)
+			{
+				pr_global_struct->self = EDICT_TO_PROG(pusher);
+				pr_global_struct->other = EDICT_TO_PROG(check);
+				PR_ExecuteProgram (pusher->v.blocked);
+			}
+			
+		// move back any entities we already moved
+			for (i=0 ; i<num_moved ; i++)
+			{
+				VectorCopy (moved_from[i], moved_edict[i]->v.origin);
+				SV_LinkEdict (moved_edict[i], false);
+			}
+			return;
+		}	
+	}
+
+	
+}
+
+#ifdef QUAKE2
+/*
+============
+SV_PushRotate
+
+============
+*/
+void SV_PushRotate (edict_t *pusher, float movetime)
+{
+	int			i, e;
+	edict_t		*check, *block;
+	vec3_t		move, a, amove;
+	vec3_t		entorig, pushorig;
+	int			num_moved;
+	edict_t		*moved_edict[MAX_EDICTS];
+	vec3_t		moved_from[MAX_EDICTS];
+	vec3_t		org, org2;
+	vec3_t		forward, right, up;
+
+	if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
+	{
+		pusher->v.ltime += movetime;
+		return;
+	}
+
+	for (i=0 ; i<3 ; i++)
+		amove[i] = pusher->v.avelocity[i] * movetime;
+
+	VectorSubtract (vec3_origin, amove, a);
+	AngleVectors (a, forward, right, up);
+
+	VectorCopy (pusher->v.angles, pushorig);
+	
+// move the pusher to it's final position
+
+	VectorAdd (pusher->v.angles, amove, pusher->v.angles);
+	pusher->v.ltime += movetime;
+	SV_LinkEdict (pusher, false);
+
+
+// see if any solid entities are inside the final position
+	num_moved = 0;
+	check = NEXT_EDICT(sv.edicts);
+	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
+	{
+		if (check->free)
+			continue;
+		if (check->v.movetype == MOVETYPE_PUSH
+		|| check->v.movetype == MOVETYPE_NONE
+		|| check->v.movetype == MOVETYPE_FOLLOW
+		|| check->v.movetype == MOVETYPE_NOCLIP)
+			continue;
+
+	// if the entity is standing on the pusher, it will definately be moved
+		if ( ! ( ((int)check->v.flags & FL_ONGROUND)
+		&& PROG_TO_EDICT(check->v.groundentity) == pusher) )
+		{
+			if ( check->v.absmin[0] >= pusher->v.absmax[0]
+			|| check->v.absmin[1] >= pusher->v.absmax[1]
+			|| check->v.absmin[2] >= pusher->v.absmax[2]
+			|| check->v.absmax[0] <= pusher->v.absmin[0]
+			|| check->v.absmax[1] <= pusher->v.absmin[1]
+			|| check->v.absmax[2] <= pusher->v.absmin[2] )
+				continue;
+
+		// see if the ent's bbox is inside the pusher's final position
+			if (!SV_TestEntityPosition (check))
+				continue;
+		}
+
+	// remove the onground flag for non-players
+		if (check->v.movetype != MOVETYPE_WALK)
+			check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
+		
+		VectorCopy (check->v.origin, entorig);
+		VectorCopy (check->v.origin, moved_from[num_moved]);
+		moved_edict[num_moved] = check;
+		num_moved++;
+
+		// calculate destination position
+		VectorSubtract (check->v.origin, pusher->v.origin, org);
+		org2[0] = DotProduct (org, forward);
+		org2[1] = -DotProduct (org, right);
+		org2[2] = DotProduct (org, up);
+		VectorSubtract (org2, org, move);
+
+		// try moving the contacted entity 
+		pusher->v.solid = SOLID_NOT;
+		SV_PushEntity (check, move);
+		pusher->v.solid = SOLID_BSP;
+
+	// if it is still inside the pusher, block
+		block = SV_TestEntityPosition (check);
+		if (block)
+		{	// fail the move
+			if (check->v.mins[0] == check->v.maxs[0])
+				continue;
+			if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
+			{	// corpse
+				check->v.mins[0] = check->v.mins[1] = 0;
+				VectorCopy (check->v.mins, check->v.maxs);
+				continue;
+			}
+			
+			VectorCopy (entorig, check->v.origin);
+			SV_LinkEdict (check, true);
+
+			VectorCopy (pushorig, pusher->v.angles);
+			SV_LinkEdict (pusher, false);
+			pusher->v.ltime -= movetime;
+
+			// if the pusher has a "blocked" function, call it
+			// otherwise, just stay in place until the obstacle is gone
+			if (pusher->v.blocked)
+			{
+				pr_global_struct->self = EDICT_TO_PROG(pusher);
+				pr_global_struct->other = EDICT_TO_PROG(check);
+				PR_ExecuteProgram (pusher->v.blocked);
+			}
+			
+		// move back any entities we already moved
+			for (i=0 ; i<num_moved ; i++)
+			{
+				VectorCopy (moved_from[i], moved_edict[i]->v.origin);
+				VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles);
+				SV_LinkEdict (moved_edict[i], false);
+			}
+			return;
+		}
+		else
+		{
+			VectorAdd (check->v.angles, amove, check->v.angles);
+		}
+	}
+
+	
+}
+#endif
+
+/*
+================
+SV_Physics_Pusher
+
+================
+*/
+void SV_Physics_Pusher (edict_t *ent)
+{
+	float	thinktime;
+	float	oldltime;
+	float	movetime;
+
+	oldltime = ent->v.ltime;
+	
+	thinktime = ent->v.nextthink;
+	if (thinktime < ent->v.ltime + host_frametime)
+	{
+		movetime = thinktime - ent->v.ltime;
+		if (movetime < 0)
+			movetime = 0;
+	}
+	else
+		movetime = host_frametime;
+
+	if (movetime)
+	{
+#ifdef QUAKE2
+		if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
+			SV_PushRotate (ent, movetime);
+		else
+#endif
+			SV_PushMove (ent, movetime);	// advances ent->v.ltime if not blocked
+	}
+		
+	if (thinktime > oldltime && thinktime <= ent->v.ltime)
+	{
+		ent->v.nextthink = 0;
+		pr_global_struct->time = sv.time;
+		pr_global_struct->self = EDICT_TO_PROG(ent);
+		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+		PR_ExecuteProgram (ent->v.think);
+		if (ent->free)
+			return;
+	}
+
+}
+
+
+/*
+===============================================================================
+
+CLIENT MOVEMENT
+
+===============================================================================
+*/
+
+/*
+=============
+SV_CheckStuck
+
+This is a big hack to try and fix the rare case of getting stuck in the world
+clipping hull.
+=============
+*/
+void SV_CheckStuck (edict_t *ent)
+{
+	int		i, j;
+	int		z;
+	vec3_t	org;
+
+	if (!SV_TestEntityPosition(ent))
+	{
+		VectorCopy (ent->v.origin, ent->v.oldorigin);
+		return;
+	}
+
+	VectorCopy (ent->v.origin, org);
+	VectorCopy (ent->v.oldorigin, ent->v.origin);
+	if (!SV_TestEntityPosition(ent))
+	{
+		Con_DPrintf ("Unstuck.\n");
+		SV_LinkEdict (ent, true);
+		return;
+	}
+	
+	for (z=0 ; z< 18 ; z++)
+		for (i=-1 ; i <= 1 ; i++)
+			for (j=-1 ; j <= 1 ; j++)
+			{
+				ent->v.origin[0] = org[0] + i;
+				ent->v.origin[1] = org[1] + j;
+				ent->v.origin[2] = org[2] + z;
+				if (!SV_TestEntityPosition(ent))
+				{
+					Con_DPrintf ("Unstuck.\n");
+					SV_LinkEdict (ent, true);
+					return;
+				}
+			}
+			
+	VectorCopy (org, ent->v.origin);
+	Con_DPrintf ("player is stuck.\n");
+}
+
+
+/*
+=============
+SV_CheckWater
+=============
+*/
+qboolean SV_CheckWater (edict_t *ent)
+{
+	vec3_t	point;
+	int		cont;
+#ifdef QUAKE2
+	int		truecont;
+#endif
+
+	point[0] = ent->v.origin[0];
+	point[1] = ent->v.origin[1];
+	point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;	
+	
+	ent->v.waterlevel = 0;
+	ent->v.watertype = CONTENTS_EMPTY;
+	cont = SV_PointContents (point);
+	if (cont <= CONTENTS_WATER)
+	{
+#ifdef QUAKE2
+		truecont = SV_TruePointContents (point);
+#endif
+		ent->v.watertype = cont;
+		ent->v.waterlevel = 1;
+		point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5;
+		cont = SV_PointContents (point);
+		if (cont <= CONTENTS_WATER)
+		{
+			ent->v.waterlevel = 2;
+			point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
+			cont = SV_PointContents (point);
+			if (cont <= CONTENTS_WATER)
+				ent->v.waterlevel = 3;
+		}
+#ifdef QUAKE2
+		if (truecont <= CONTENTS_CURRENT_0 && truecont >= CONTENTS_CURRENT_DOWN)
+		{
+			static vec3_t current_table[] =
+			{
+				{1, 0, 0},
+				{0, 1, 0},
+				{-1, 0, 0},
+				{0, -1, 0},
+				{0, 0, 1},
+				{0, 0, -1}
+			};
+
+			VectorMA (ent->v.basevelocity, 150.0*ent->v.waterlevel/3.0, current_table[CONTENTS_CURRENT_0 - truecont], ent->v.basevelocity);
+		}
+#endif
+	}
+	
+	return ent->v.waterlevel > 1;
+}
+
+/*
+============
+SV_WallFriction
+
+============
+*/
+void SV_WallFriction (edict_t *ent, trace_t *trace)
+{
+	vec3_t		forward, right, up;
+	float		d, i;
+	vec3_t		into, side;
+	
+	AngleVectors (ent->v.v_angle, forward, right, up);
+	d = DotProduct (trace->plane.normal, forward);
+	
+	d += 0.5;
+	if (d >= 0)
+		return;
+		
+// cut the tangential velocity
+	i = DotProduct (trace->plane.normal, ent->v.velocity);
+	VectorScale (trace->plane.normal, i, into);
+	VectorSubtract (ent->v.velocity, into, side);
+	
+	ent->v.velocity[0] = side[0] * (1 + d);
+	ent->v.velocity[1] = side[1] * (1 + d);
+}
+
+/*
+=====================
+SV_TryUnstick
+
+Player has come to a dead stop, possibly due to the problem with limited
+float precision at some angle joins in the BSP hull.
+
+Try fixing by pushing one pixel in each direction.
+
+This is a hack, but in the interest of good gameplay...
+======================
+*/
+int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
+{
+	int		i;
+	vec3_t	oldorg;
+	vec3_t	dir;
+	int		clip;
+	trace_t	steptrace;
+	
+	VectorCopy (ent->v.origin, oldorg);
+	VectorCopy (vec3_origin, dir);
+
+	for (i=0 ; i<8 ; i++)
+	{
+// try pushing a little in an axial direction
+		switch (i)
+		{
+			case 0:	dir[0] = 2; dir[1] = 0; break;
+			case 1:	dir[0] = 0; dir[1] = 2; break;
+			case 2:	dir[0] = -2; dir[1] = 0; break;
+			case 3:	dir[0] = 0; dir[1] = -2; break;
+			case 4:	dir[0] = 2; dir[1] = 2; break;
+			case 5:	dir[0] = -2; dir[1] = 2; break;
+			case 6:	dir[0] = 2; dir[1] = -2; break;
+			case 7:	dir[0] = -2; dir[1] = -2; break;
+		}
+		
+		SV_PushEntity (ent, dir);
+
+// retry the original move
+		ent->v.velocity[0] = oldvel[0];
+		ent->v. velocity[1] = oldvel[1];
+		ent->v. velocity[2] = 0;
+		clip = SV_FlyMove (ent, 0.1, &steptrace);
+
+		if ( fabs(oldorg[1] - ent->v.origin[1]) > 4
+		|| fabs(oldorg[0] - ent->v.origin[0]) > 4 )
+		{
+//Con_DPrintf ("unstuck!\n");
+			return clip;
+		}
+			
+// go back to the original pos and try again
+		VectorCopy (oldorg, ent->v.origin);
+	}
+	
+	VectorCopy (vec3_origin, ent->v.velocity);
+	return 7;		// still not moving
+}
+
+/*
+=====================
+SV_WalkMove
+
+Only used by players
+======================
+*/
+#define	STEPSIZE	18
+void SV_WalkMove (edict_t *ent)
+{
+	vec3_t		upmove, downmove;
+	vec3_t		oldorg, oldvel;
+	vec3_t		nosteporg, nostepvel;
+	int			clip;
+	int			oldonground;
+	trace_t		steptrace, downtrace;
+	
+//
+// do a regular slide move unless it looks like you ran into a step
+//
+	oldonground = (int)ent->v.flags & FL_ONGROUND;
+	ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+	
+	VectorCopy (ent->v.origin, oldorg);
+	VectorCopy (ent->v.velocity, oldvel);
+	
+	clip = SV_FlyMove (ent, host_frametime, &steptrace);
+
+	if ( !(clip & 2) )
+		return;		// move didn't block on a step
+
+	if (!oldonground && ent->v.waterlevel == 0)
+		return;		// don't stair up while jumping
+	
+	if (ent->v.movetype != MOVETYPE_WALK)
+		return;		// gibbed by a trigger
+	
+	if (sv_nostep.value)
+		return;
+	
+	if ( (int)sv_player->v.flags & FL_WATERJUMP )
+		return;
+
+	VectorCopy (ent->v.origin, nosteporg);
+	VectorCopy (ent->v.velocity, nostepvel);
+
+//
+// try moving up and forward to go up a step
+//
+	VectorCopy (oldorg, ent->v.origin);	// back to start pos
+
+	VectorCopy (vec3_origin, upmove);
+	VectorCopy (vec3_origin, downmove);
+	upmove[2] = STEPSIZE;
+	downmove[2] = -STEPSIZE + oldvel[2]*host_frametime;
+
+// move up
+	SV_PushEntity (ent, upmove);	// FIXME: don't link?
+
+// move forward
+	ent->v.velocity[0] = oldvel[0];
+	ent->v. velocity[1] = oldvel[1];
+	ent->v. velocity[2] = 0;
+	clip = SV_FlyMove (ent, host_frametime, &steptrace);
+
+// check for stuckness, possibly due to the limited precision of floats
+// in the clipping hulls
+	if (clip)
+	{
+		if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125
+		&& fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )
+		{	// stepping up didn't make any progress
+			clip = SV_TryUnstick (ent, oldvel);
+		}
+	}
+	
+// extra friction based on view angle
+	if ( clip & 2 )
+		SV_WallFriction (ent, &steptrace);
+
+// move down
+	downtrace = SV_PushEntity (ent, downmove);	// FIXME: don't link?
+
+	if (downtrace.plane.normal[2] > 0.7)
+	{
+		if (ent->v.solid == SOLID_BSP)
+		{
+			ent->v.flags =	(int)ent->v.flags | FL_ONGROUND;
+			ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
+		}
+	}
+	else
+	{
+// if the push down didn't end up on good ground, use the move without
+// the step up.  This happens near wall / slope combinations, and can
+// cause the player to hop up higher on a slope too steep to climb	
+		VectorCopy (nosteporg, ent->v.origin);
+		VectorCopy (nostepvel, ent->v.velocity);
+	}
+}
+
+
+/*
+================
+SV_Physics_Client
+
+Player character actions
+================
+*/
+void SV_Physics_Client (edict_t	*ent, int num)
+{
+	if ( ! svs.clients[num-1].active )
+		return;		// unconnected slot
+
+//
+// call standard client pre-think
+//	
+	pr_global_struct->time = sv.time;
+	pr_global_struct->self = EDICT_TO_PROG(ent);
+	PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
+	
+//
+// do a move
+//
+	SV_CheckVelocity (ent);
+
+//
+// decide which move function to call
+//
+	switch ((int)ent->v.movetype)
+	{
+	case MOVETYPE_NONE:
+		if (!SV_RunThink (ent))
+			return;
+		break;
+
+	case MOVETYPE_WALK:
+		if (!SV_RunThink (ent))
+			return;
+		if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
+			SV_AddGravity (ent);
+		SV_CheckStuck (ent);
+#ifdef QUAKE2
+		VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+#endif
+		SV_WalkMove (ent);
+
+#ifdef QUAKE2
+		VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+#endif
+		break;
+		
+	case MOVETYPE_TOSS:
+	case MOVETYPE_BOUNCE:
+		SV_Physics_Toss (ent);
+		break;
+
+	case MOVETYPE_FLY:
+		if (!SV_RunThink (ent))
+			return;
+		SV_FlyMove (ent, host_frametime, NULL);
+		break;
+		
+	case MOVETYPE_NOCLIP:
+		if (!SV_RunThink (ent))
+			return;
+		VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
+		break;
+		
+	default:
+		Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
+	}
+
+//
+// call standard player post-think
+//		
+	SV_LinkEdict (ent, true);
+
+	pr_global_struct->time = sv.time;
+	pr_global_struct->self = EDICT_TO_PROG(ent);
+	PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
+}
+
+//============================================================================
+
+/*
+=============
+SV_Physics_None
+
+Non moving objects can only think
+=============
+*/
+void SV_Physics_None (edict_t *ent)
+{
+// regular thinking
+	SV_RunThink (ent);
+}
+
+#ifdef QUAKE2
+/*
+=============
+SV_Physics_Follow
+
+Entities that are "stuck" to another entity
+=============
+*/
+void SV_Physics_Follow (edict_t *ent)
+{
+// regular thinking
+	SV_RunThink (ent);
+	VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin);
+	SV_LinkEdict (ent, true);
+}
+#endif
+
+/*
+=============
+SV_Physics_Noclip
+
+A moving object that doesn't obey physics
+=============
+*/
+void SV_Physics_Noclip (edict_t *ent)
+{
+// regular thinking
+	if (!SV_RunThink (ent))
+		return;
+	
+	VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
+	VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
+
+	SV_LinkEdict (ent, false);
+}
+
+/*
+==============================================================================
+
+TOSS / BOUNCE
+
+==============================================================================
+*/
+
+/*
+=============
+SV_CheckWaterTransition
+
+=============
+*/
+void SV_CheckWaterTransition (edict_t *ent)
+{
+	int		cont;
+#ifdef QUAKE2
+	vec3_t	point;
+	
+	point[0] = ent->v.origin[0];
+	point[1] = ent->v.origin[1];
+	point[2] = ent->v.origin[2] + ent->v.mins[2] + 1;	
+	cont = SV_PointContents (point);
+#else
+	cont = SV_PointContents (ent->v.origin);
+#endif
+	if (!ent->v.watertype)
+	{	// just spawned here
+		ent->v.watertype = cont;
+		ent->v.waterlevel = 1;
+		return;
+	}
+	
+	if (cont <= CONTENTS_WATER)
+	{
+		if (ent->v.watertype == CONTENTS_EMPTY)
+		{	// just crossed into water
+			SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
+		}		
+		ent->v.watertype = cont;
+		ent->v.waterlevel = 1;
+	}
+	else
+	{
+		if (ent->v.watertype != CONTENTS_EMPTY)
+		{	// just crossed into water
+			SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
+		}		
+		ent->v.watertype = CONTENTS_EMPTY;
+		ent->v.waterlevel = cont;
+	}
+}
+
+/*
+=============
+SV_Physics_Toss
+
+Toss, bounce, and fly movement.  When onground, do nothing.
+=============
+*/
+void SV_Physics_Toss (edict_t *ent)
+{
+	trace_t	trace;
+	vec3_t	move;
+	float	backoff;
+#ifdef QUAKE2
+	edict_t	*groundentity;
+
+	groundentity = PROG_TO_EDICT(ent->v.groundentity);
+	if ((int)groundentity->v.flags & FL_CONVEYOR)
+		VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
+	else
+		VectorCopy(vec_origin, ent->v.basevelocity);
+	SV_CheckWater (ent);
+#endif
+	// regular thinking
+	if (!SV_RunThink (ent))
+		return;
+
+#ifdef QUAKE2
+	if (ent->v.velocity[2] > 0)
+		ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+
+	if ( ((int)ent->v.flags & FL_ONGROUND) )
+//@@
+		if (VectorCompare(ent->v.basevelocity, vec_origin))
+			return;
+
+	SV_CheckVelocity (ent);
+
+// add gravity
+	if (! ((int)ent->v.flags & FL_ONGROUND)
+		&& ent->v.movetype != MOVETYPE_FLY
+		&& ent->v.movetype != MOVETYPE_BOUNCEMISSILE
+		&& ent->v.movetype != MOVETYPE_FLYMISSILE)
+			SV_AddGravity (ent);
+
+#else
+// if onground, return without moving
+	if ( ((int)ent->v.flags & FL_ONGROUND) )
+		return;
+
+	SV_CheckVelocity (ent);
+
+// add gravity
+	if (ent->v.movetype != MOVETYPE_FLY
+	&& ent->v.movetype != MOVETYPE_FLYMISSILE)
+		SV_AddGravity (ent);
+#endif
+
+// move angles
+	VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
+
+// move origin
+#ifdef QUAKE2
+	VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+#endif
+	VectorScale (ent->v.velocity, host_frametime, move);
+	trace = SV_PushEntity (ent, move);
+#ifdef QUAKE2
+	VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+#endif
+	if (trace.fraction == 1)
+		return;
+	if (ent->free)
+		return;
+	
+	if (ent->v.movetype == MOVETYPE_BOUNCE)
+		backoff = 1.5;
+#ifdef QUAKE2
+	else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)
+		backoff = 2.0;
+#endif
+	else
+		backoff = 1;
+
+	ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
+
+// stop if on ground
+	if (trace.plane.normal[2] > 0.7)
+	{		
+#ifdef QUAKE2
+		if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE))
+#else
+		if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE)
+#endif
+		{
+			ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+			ent->v.groundentity = EDICT_TO_PROG(trace.ent);
+			VectorCopy (vec3_origin, ent->v.velocity);
+			VectorCopy (vec3_origin, ent->v.avelocity);
+		}
+	}
+	
+// check for in water
+	SV_CheckWaterTransition (ent);
+}
+
+/*
+===============================================================================
+
+STEPPING MOVEMENT
+
+===============================================================================
+*/
+
+/*
+=============
+SV_Physics_Step
+
+Monsters freefall when they don't have a ground entity, otherwise
+all movement is done with discrete steps.
+
+This is also used for objects that have become still on the ground, but
+will fall if the floor is pulled out from under them.
+=============
+*/
+#ifdef QUAKE2
+void SV_Physics_Step (edict_t *ent)
+{
+	qboolean	wasonground;
+	qboolean	inwater;
+	qboolean	hitsound = false;
+	float		*vel;
+	float		speed, newspeed, control;
+	float		friction;
+	edict_t		*groundentity;
+
+	groundentity = PROG_TO_EDICT(ent->v.groundentity);
+	if ((int)groundentity->v.flags & FL_CONVEYOR)
+		VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
+	else
+		VectorCopy(vec_origin, ent->v.basevelocity);
+//@@
+	pr_global_struct->time = sv.time;
+	pr_global_struct->self = EDICT_TO_PROG(ent);
+	PF_WaterMove();
+
+	SV_CheckVelocity (ent);
+
+	wasonground = (int)ent->v.flags & FL_ONGROUND;
+//	ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+
+	// add gravity except:
+	//   flying monsters
+	//   swimming monsters who are in the water
+	inwater = SV_CheckWater(ent);
+	if (! wasonground)
+		if (!((int)ent->v.flags & FL_FLY))
+			if (!(((int)ent->v.flags & FL_SWIM) && (ent->v.waterlevel > 0)))
+			{
+				if (ent->v.velocity[2] < sv_gravity.value*-0.1)
+					hitsound = true;
+				if (!inwater)
+					SV_AddGravity (ent);
+			}
+
+	if (!VectorCompare(ent->v.velocity, vec_origin) || !VectorCompare(ent->v.basevelocity, vec_origin))
+	{
+		ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
+		// apply friction
+		// let dead monsters who aren't completely onground slide
+		if (wasonground)
+			if (!(ent->v.health <= 0.0 && !SV_CheckBottom(ent)))
+			{
+				vel = ent->v.velocity;
+				speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
+				if (speed)
+				{
+					friction = sv_friction.value;
+
+					control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
+					newspeed = speed - host_frametime*control*friction;
+
+					if (newspeed < 0)
+						newspeed = 0;
+					newspeed /= speed;
+
+					vel[0] = vel[0] * newspeed;
+					vel[1] = vel[1] * newspeed;
+				}
+			}
+
+		VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+		SV_FlyMove (ent, host_frametime, NULL);
+		VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity);
+
+		// determine if it's on solid ground at all
+		{
+			vec3_t	mins, maxs, point;
+			int		x, y;
+		
+			VectorAdd (ent->v.origin, ent->v.mins, mins);
+			VectorAdd (ent->v.origin, ent->v.maxs, maxs);
+
+			point[2] = mins[2] - 1;
+			for	(x=0 ; x<=1 ; x++)
+				for	(y=0 ; y<=1 ; y++)
+				{
+					point[0] = x ? maxs[0] : mins[0];
+					point[1] = y ? maxs[1] : mins[1];
+					if (SV_PointContents (point) == CONTENTS_SOLID)
+					{
+						ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
+						break;
+					}
+				}
+
+		}
+
+		SV_LinkEdict (ent, true);
+
+		if ((int)ent->v.flags & FL_ONGROUND)
+			if (!wasonground)
+				if (hitsound)
+					SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
+	}
+
+// regular thinking
+	SV_RunThink (ent);
+	SV_CheckWaterTransition (ent);
+}
+#else
+void SV_Physics_Step (edict_t *ent)
+{
+	qboolean	hitsound;
+
+// freefall if not onground
+	if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
+	{
+		if (ent->v.velocity[2] < sv_gravity.value*-0.1)
+			hitsound = true;
+		else
+			hitsound = false;
+
+		SV_AddGravity (ent);
+		SV_CheckVelocity (ent);
+		SV_FlyMove (ent, host_frametime, NULL);
+		SV_LinkEdict (ent, true);
+
+		if ( (int)ent->v.flags & FL_ONGROUND )	// just hit ground
+		{
+			if (hitsound)
+				SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
+		}
+	}
+
+// regular thinking
+	SV_RunThink (ent);
+	
+	SV_CheckWaterTransition (ent);
+}
+#endif
+
+//============================================================================
+
+/*
+================
+SV_Physics
+
+================
+*/
+void SV_Physics (void)
+{
+	int		i;
+	edict_t	*ent;
+
+// let the progs know that a new frame has started
+	pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
+	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
+	pr_global_struct->time = sv.time;
+	PR_ExecuteProgram (pr_global_struct->StartFrame);
+
+//SV_CheckAllEnts ();
+
+//
+// treat each object in turn
+//
+	ent = sv.edicts;
+	for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
+	{
+		if (ent->free)
+			continue;
+
+		if (pr_global_struct->force_retouch)
+		{
+			SV_LinkEdict (ent, true);	// force retouch even for stationary
+		}
+
+		if (i > 0 && i <= svs.maxclients)
+			SV_Physics_Client (ent, i);
+		else if (ent->v.movetype == MOVETYPE_PUSH)
+			SV_Physics_Pusher (ent);
+		else if (ent->v.movetype == MOVETYPE_NONE)
+			SV_Physics_None (ent);
+#ifdef QUAKE2
+		else if (ent->v.movetype == MOVETYPE_FOLLOW)
+			SV_Physics_Follow (ent);
+#endif
+		else if (ent->v.movetype == MOVETYPE_NOCLIP)
+			SV_Physics_Noclip (ent);
+		else if (ent->v.movetype == MOVETYPE_STEP)
+			SV_Physics_Step (ent);
+		else if (ent->v.movetype == MOVETYPE_TOSS 
+		|| ent->v.movetype == MOVETYPE_BOUNCE
+#ifdef QUAKE2
+		|| ent->v.movetype == MOVETYPE_BOUNCEMISSILE
+#endif
+		|| ent->v.movetype == MOVETYPE_FLY
+		|| ent->v.movetype == MOVETYPE_FLYMISSILE)
+			SV_Physics_Toss (ent);
+		else
+			Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);			
+	}
+	
+	if (pr_global_struct->force_retouch)
+		pr_global_struct->force_retouch--;	
+
+	sv.time += host_frametime;
+}
+
+
+#ifdef QUAKE2
+trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore)
+{
+	edict_t	tempent, *tent;
+	trace_t	trace;
+	vec3_t	move;
+	vec3_t	end;
+	double	save_frametime;
+//	extern particle_t	*active_particles, *free_particles;
+//	particle_t	*p;
+
+
+	save_frametime = host_frametime;
+	host_frametime = 0.05;
+
+	memcpy(&tempent, ent, sizeof(edict_t));
+	tent = &tempent;
+
+	while (1)
+	{
+		SV_CheckVelocity (tent);
+		SV_AddGravity (tent);
+		VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles);
+		VectorScale (tent->v.velocity, host_frametime, move);
+		VectorAdd (tent->v.origin, move, end);
+		trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent);	
+		VectorCopy (trace.endpos, tent->v.origin);
+
+//		p = free_particles;
+//		if (p)
+//		{
+//			free_particles = p->next;
+//			p->next = active_particles;
+//			active_particles = p;
+//		
+//			p->die = 256;
+//			p->color = 15;
+//			p->type = pt_static;
+//			VectorCopy (vec3_origin, p->vel);
+//			VectorCopy (tent->v.origin, p->org);
+//		}
+
+		if (trace.ent)
+			if (trace.ent != ignore)
+				break;
+	}
+//	p->color = 224;
+	host_frametime = save_frametime;
+	return trace;
+}
+#endif
diff --git a/apps/plugins/sdl/progs/quake/sv_user.c b/apps/plugins/sdl/progs/quake/sv_user.c
new file mode 100644
index 0000000..b3082a5
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sv_user.c
@@ -0,0 +1,629 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sv_user.c -- server code for moving users
+
+#include "quakedef.h"
+
+edict_t	*sv_player;
+
+extern	cvar_t	sv_friction;
+cvar_t	sv_edgefriction = {"edgefriction", "2"};
+extern	cvar_t	sv_stopspeed;
+
+static	vec3_t		forward, right, up;
+
+vec3_t	wishdir;
+float	wishspeed;
+
+// world
+float	*angles;
+float	*origin;
+float	*velocity;
+
+qboolean	onground;
+
+usercmd_t	cmd;
+
+cvar_t	sv_idealpitchscale = {"sv_idealpitchscale","0.8"};
+
+
+/*
+===============
+SV_SetIdealPitch
+===============
+*/
+#define	MAX_FORWARD	6
+void SV_SetIdealPitch (void)
+{
+	float	angleval, sinval, cosval;
+	trace_t	tr;
+	vec3_t	top, bottom;
+	float	z[MAX_FORWARD];
+	int		i, j;
+	int		step, dir, steps;
+
+	if (!((int)sv_player->v.flags & FL_ONGROUND))
+		return;
+		
+	angleval = sv_player->v.angles[YAW] * M_PI*2 / 360;
+	sinval = sin(angleval);
+	cosval = cos(angleval);
+
+	for (i=0 ; i<MAX_FORWARD ; i++)
+	{
+		top[0] = sv_player->v.origin[0] + cosval*(i+3)*12;
+		top[1] = sv_player->v.origin[1] + sinval*(i+3)*12;
+		top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2];
+		
+		bottom[0] = top[0];
+		bottom[1] = top[1];
+		bottom[2] = top[2] - 160;
+		
+		tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player);
+		if (tr.allsolid)
+			return;	// looking at a wall, leave ideal the way is was
+
+		if (tr.fraction == 1)
+			return;	// near a dropoff
+		
+		z[i] = top[2] + tr.fraction*(bottom[2]-top[2]);
+	}
+	
+	dir = 0;
+	steps = 0;
+	for (j=1 ; j<i ; j++)
+	{
+		step = z[j] - z[j-1];
+		if (step > -ON_EPSILON && step < ON_EPSILON)
+			continue;
+
+		if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
+			return;		// mixed changes
+
+		steps++;	
+		dir = step;
+	}
+	
+	if (!dir)
+	{
+		sv_player->v.idealpitch = 0;
+		return;
+	}
+	
+	if (steps < 2)
+		return;
+	sv_player->v.idealpitch = -dir * sv_idealpitchscale.value;
+}
+
+
+/*
+==================
+SV_UserFriction
+
+==================
+*/
+void SV_UserFriction (void)
+{
+	float	*vel;
+	float	speed, newspeed, control;
+	vec3_t	start, stop;
+	float	friction;
+	trace_t	trace;
+	
+	vel = velocity;
+	
+	speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
+	if (!speed)
+		return;
+
+// if the leading edge is over a dropoff, increase friction
+	start[0] = stop[0] = origin[0] + vel[0]/speed*16;
+	start[1] = stop[1] = origin[1] + vel[1]/speed*16;
+	start[2] = origin[2] + sv_player->v.mins[2];
+	stop[2] = start[2] - 34;
+
+	trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player);
+
+	if (trace.fraction == 1.0)
+		friction = sv_friction.value*sv_edgefriction.value;
+	else
+		friction = sv_friction.value;
+
+// apply friction	
+	control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
+	newspeed = speed - host_frametime*control*friction;
+	
+	if (newspeed < 0)
+		newspeed = 0;
+	newspeed /= speed;
+
+	vel[0] = vel[0] * newspeed;
+	vel[1] = vel[1] * newspeed;
+	vel[2] = vel[2] * newspeed;
+}
+
+/*
+==============
+SV_Accelerate
+==============
+*/
+cvar_t	sv_maxspeed = {"sv_maxspeed", "320", false, true};
+cvar_t	sv_accelerate = {"sv_accelerate", "10"};
+#if 0
+void SV_Accelerate (vec3_t wishvel)
+{
+	int			i;
+	float		addspeed, accelspeed;
+	vec3_t		pushvec;
+
+	if (wishspeed == 0)
+		return;
+
+	VectorSubtract (wishvel, velocity, pushvec);
+	addspeed = VectorNormalize (pushvec);
+
+	accelspeed = sv_accelerate.value*host_frametime*addspeed;
+	if (accelspeed > addspeed)
+		accelspeed = addspeed;
+	
+	for (i=0 ; i<3 ; i++)
+		velocity[i] += accelspeed*pushvec[i];	
+}
+#endif
+void SV_Accelerate (void)
+{
+	int			i;
+	float		addspeed, accelspeed, currentspeed;
+
+	currentspeed = DotProduct (velocity, wishdir);
+	addspeed = wishspeed - currentspeed;
+	if (addspeed <= 0)
+		return;
+	accelspeed = sv_accelerate.value*host_frametime*wishspeed;
+	if (accelspeed > addspeed)
+		accelspeed = addspeed;
+	
+	for (i=0 ; i<3 ; i++)
+		velocity[i] += accelspeed*wishdir[i];	
+}
+
+void SV_AirAccelerate (vec3_t wishveloc)
+{
+	int			i;
+	float		addspeed, wishspd, accelspeed, currentspeed;
+		
+	wishspd = VectorNormalize (wishveloc);
+	if (wishspd > 30)
+		wishspd = 30;
+	currentspeed = DotProduct (velocity, wishveloc);
+	addspeed = wishspd - currentspeed;
+	if (addspeed <= 0)
+		return;
+//	accelspeed = sv_accelerate.value * host_frametime;
+	accelspeed = sv_accelerate.value*wishspeed * host_frametime;
+	if (accelspeed > addspeed)
+		accelspeed = addspeed;
+	
+	for (i=0 ; i<3 ; i++)
+		velocity[i] += accelspeed*wishveloc[i];	
+}
+
+
+void DropPunchAngle (void)
+{
+	float	len;
+	
+	len = VectorNormalize (sv_player->v.punchangle);
+	
+	len -= 10*host_frametime;
+	if (len < 0)
+		len = 0;
+	VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle);
+}
+
+/*
+===================
+SV_WaterMove
+
+===================
+*/
+void SV_WaterMove (void)
+{
+	int		i;
+	vec3_t	wishvel;
+	float	speed, newspeed, wishspeed, addspeed, accelspeed;
+
+//
+// user intentions
+//
+	AngleVectors (sv_player->v.v_angle, forward, right, up);
+
+	for (i=0 ; i<3 ; i++)
+		wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
+
+	if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
+		wishvel[2] -= 60;		// drift towards bottom
+	else
+		wishvel[2] += cmd.upmove;
+
+	wishspeed = Length(wishvel);
+	if (wishspeed > sv_maxspeed.value)
+	{
+		VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
+		wishspeed = sv_maxspeed.value;
+	}
+	wishspeed *= 0.7;
+
+//
+// water friction
+//
+	speed = Length (velocity);
+	if (speed)
+	{
+		newspeed = speed - host_frametime * speed * sv_friction.value;
+		if (newspeed < 0)
+			newspeed = 0;	
+		VectorScale (velocity, newspeed/speed, velocity);
+	}
+	else
+		newspeed = 0;
+	
+//
+// water acceleration
+//
+	if (!wishspeed)
+		return;
+
+	addspeed = wishspeed - newspeed;
+	if (addspeed <= 0)
+		return;
+
+	VectorNormalizeNoRet (wishvel);
+	accelspeed = sv_accelerate.value * wishspeed * host_frametime;
+	if (accelspeed > addspeed)
+		accelspeed = addspeed;
+
+	for (i=0 ; i<3 ; i++)
+		velocity[i] += accelspeed * wishvel[i];
+}
+
+void SV_WaterJump (void)
+{
+	if (sv.time > sv_player->v.teleport_time
+	|| !sv_player->v.waterlevel)
+	{
+		sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP;
+		sv_player->v.teleport_time = 0;
+	}
+	sv_player->v.velocity[0] = sv_player->v.movedir[0];
+	sv_player->v.velocity[1] = sv_player->v.movedir[1];
+}
+
+
+/*
+===================
+SV_AirMove
+
+===================
+*/
+void SV_AirMove (void)
+{
+	int			i;
+	vec3_t		wishvel;
+	float		fmove, smove;
+
+	AngleVectors (sv_player->v.angles, forward, right, up);
+
+	fmove = cmd.forwardmove;
+	smove = cmd.sidemove;
+	
+// hack to not let you back into teleporter
+	if (sv.time < sv_player->v.teleport_time && fmove < 0)
+		fmove = 0;
+		
+	for (i=0 ; i<3 ; i++)
+		wishvel[i] = forward[i]*fmove + right[i]*smove;
+
+	if ( (int)sv_player->v.movetype != MOVETYPE_WALK)
+		wishvel[2] = cmd.upmove;
+	else
+		wishvel[2] = 0;
+
+	VectorCopy (wishvel, wishdir);
+	wishspeed = VectorNormalize(wishdir);
+	if (wishspeed > sv_maxspeed.value)
+	{
+		VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
+		wishspeed = sv_maxspeed.value;
+	}
+	
+	if ( sv_player->v.movetype == MOVETYPE_NOCLIP)
+	{	// noclip
+		VectorCopy (wishvel, velocity);
+	}
+	else if ( onground )
+	{
+		SV_UserFriction ();
+		SV_Accelerate ();
+	}
+	else
+	{	// not on ground, so little effect on velocity
+		SV_AirAccelerate (wishvel);
+	}		
+}
+
+/*
+===================
+SV_ClientThink
+
+the move fields specify an intended velocity in pix/sec
+the angle fields specify an exact angular motion in degrees
+===================
+*/
+void SV_ClientThink (void)
+{
+	vec3_t		v_angle;
+
+	if (sv_player->v.movetype == MOVETYPE_NONE)
+		return;
+	
+	onground = (int)sv_player->v.flags & FL_ONGROUND;
+
+	origin = sv_player->v.origin;
+	velocity = sv_player->v.velocity;
+
+	DropPunchAngle ();
+	
+//
+// if dead, behave differently
+//
+	if (sv_player->v.health <= 0)
+		return;
+
+//
+// angles
+// show 1/3 the pitch angle and all the roll angle
+	cmd = host_client->cmd;
+	angles = sv_player->v.angles;
+	
+	VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle);
+	angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4;
+	if (!sv_player->v.fixangle)
+	{
+		angles[PITCH] = -v_angle[PITCH]/3;
+		angles[YAW] = v_angle[YAW];
+	}
+
+	if ( (int)sv_player->v.flags & FL_WATERJUMP )
+	{
+		SV_WaterJump ();
+		return;
+	}
+//
+// walk
+//
+	if ( (sv_player->v.waterlevel >= 2)
+	&& (sv_player->v.movetype != MOVETYPE_NOCLIP) )
+	{
+		SV_WaterMove ();
+		return;
+	}
+
+	SV_AirMove ();	
+}
+
+
+/*
+===================
+SV_ReadClientMove
+===================
+*/
+void SV_ReadClientMove (usercmd_t *move)
+{
+	int		i;
+	vec3_t	angle;
+	int		bits;
+	
+// read ping time
+	host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
+		= sv.time - MSG_ReadFloat ();
+	host_client->num_pings++;
+
+// read current angles	
+	for (i=0 ; i<3 ; i++)
+		angle[i] = MSG_ReadAngle ();
+
+	VectorCopy (angle, host_client->edict->v.v_angle);
+		
+// read movement
+	move->forwardmove = MSG_ReadShort ();
+	move->sidemove = MSG_ReadShort ();
+	move->upmove = MSG_ReadShort ();
+	
+// read buttons
+	bits = MSG_ReadByte ();
+	host_client->edict->v.button0 = bits & 1;
+	host_client->edict->v.button2 = (bits & 2)>>1;
+
+	i = MSG_ReadByte ();
+	if (i)
+		host_client->edict->v.impulse = i;
+
+#ifdef QUAKE2
+// read light level
+	host_client->edict->v.light_level = MSG_ReadByte ();
+#endif
+}
+
+/*
+===================
+SV_ReadClientMessage
+
+Returns false if the client should be killed
+===================
+*/
+qboolean SV_ReadClientMessage (void)
+{
+	int		ret;
+	int		cmd;
+	char		*s;
+	
+	do
+	{
+nextmsg:
+		ret = NET_GetMessage (host_client->netconnection);
+		if (ret == -1)
+		{
+			Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n");
+			return false;
+		}
+		if (!ret)
+			return true;
+					
+		MSG_BeginReading ();
+		
+		while (1)
+		{
+			if (!host_client->active)
+				return false;	// a command caused an error
+
+			if (msg_badread)
+			{
+				Sys_Printf ("SV_ReadClientMessage: badread\n");
+				return false;
+			}	
+	
+			cmd = MSG_ReadChar ();
+			
+			switch (cmd)
+			{
+			case -1:
+				goto nextmsg;		// end of message
+				
+			default:
+				Sys_Printf ("SV_ReadClientMessage: unknown command char\n");
+				return false;
+							
+			case clc_nop:
+//				Sys_Printf ("clc_nop\n");
+				break;
+				
+			case clc_stringcmd:	
+				s = MSG_ReadString ();
+				if (host_client->privileged)
+					ret = 2;
+				else
+					ret = 0;
+				if (Q_strncasecmp(s, "status", 6) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "god", 3) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "notarget", 8) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "fly", 3) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "name", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "noclip", 6) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "say", 3) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "say_team", 8) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "tell", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "color", 5) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "kill", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "pause", 5) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "spawn", 5) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "begin", 5) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "prespawn", 8) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "kick", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "ping", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "give", 4) == 0)
+					ret = 1;
+				else if (Q_strncasecmp(s, "ban", 3) == 0)
+					ret = 1;
+				if (ret == 2)
+					Cbuf_InsertText (s);
+				else if (ret == 1)
+					Cmd_ExecuteString (s, src_client);
+				else
+					Con_DPrintf("%s tried to %s\n", host_client->name, s);
+				break;
+				
+			case clc_disconnect:
+//				Sys_Printf ("SV_ReadClientMessage: client disconnected\n");
+				return false;
+			
+			case clc_move:
+				SV_ReadClientMove (&host_client->cmd);
+				break;
+			}
+		}
+	} while (ret == 1);
+	
+	return true;
+}
+
+
+/*
+==================
+SV_RunClients
+==================
+*/
+void SV_RunClients (void)
+{
+	int				i;
+	
+	for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
+	{
+		if (!host_client->active)
+			continue;
+	
+		sv_player = host_client->edict;
+
+		if (!SV_ReadClientMessage ())
+		{
+			SV_DropClient (false);	// client misbehaved...
+			continue;
+		}
+
+		if (!host_client->spawned)
+		{
+		// clear client movement until a new packet is received
+			memset (&host_client->cmd, 0, sizeof(host_client->cmd));
+			continue;
+		}
+
+// always pause in single player if in console or menus
+		if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
+			SV_ClientThink ();
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/sys.h b/apps/plugins/sdl/progs/quake/sys.h
new file mode 100644
index 0000000..55a97ae
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sys.h
@@ -0,0 +1,74 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sys.h -- non-portable functions
+
+/* frees file handles */
+void Sys_Shutdown(void);
+
+//
+// file IO
+//
+
+// returns the file size
+// return -1 if file is not present
+// the file should be in BINARY mode for stupid OSs that care
+int Sys_FileOpenRead (char *path, int *hndl);
+
+int Sys_FileOpenWrite (char *path);
+void Sys_FileClose (int handle);
+void Sys_FileSeek (int handle, int position);
+int Sys_FileRead (int handle, void *dest, int count);
+int Sys_FileWrite (int handle, void *data, int count);
+int	Sys_FileTime (char *path);
+void Sys_mkdir (char *path);
+
+//
+// memory protection
+//
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length);
+
+//
+// system IO
+//
+void Sys_DebugLog(char *file, char *fmt, ...);
+
+void Sys_Error (char *error, ...);
+// an error will cause the entire program to exit
+
+void Sys_Printf (char *fmt, ...);
+// send text to the console
+
+void Sys_Quit (void);
+
+double Sys_FloatTime (void);
+
+char *Sys_ConsoleInput (void);
+
+void Sys_Sleep (void);
+// called to yield for a little bit so as
+// not to hog cpu when paused or debugging
+
+void Sys_SendKeyEvents (void);
+// Perform Key_Event () callbacks until the input que is empty
+
+void Sys_LowFPPrecision (void);
+void Sys_HighFPPrecision (void);
+void Sys_SetFPCW (void);
+
diff --git a/apps/plugins/sdl/progs/quake/sys_linux.c b/apps/plugins/sdl/progs/quake/sys_linux.c
new file mode 100644
index 0000000..8a29cfa
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sys_linux.c
@@ -0,0 +1,457 @@
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "quakedef.h"
+
+qboolean			isDedicated;
+
+int nostdout = 0;
+
+char *basedir = ".";
+char *cachedir = "/tmp";
+
+cvar_t  sys_linerefresh = {"sys_linerefresh","0"};// set for entity display
+
+// =======================================================================
+// General routines
+// =======================================================================
+
+void Sys_DebugNumber(int y, int val)
+{
+}
+
+/*
+void Sys_Printf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		text[1024];
+	
+	va_start (argptr,fmt);
+	vsprintf (text,fmt,argptr);
+	va_end (argptr);
+	fprintf(stderr, "%s", text);
+	
+	Con_Print (text);
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+
+    va_list     argptr;
+    char        text[1024], *t_p;
+    int         l, r;
+
+	if (nostdout)
+		return;
+
+    va_start (argptr,fmt);
+    vsprintf (text,fmt,argptr);
+    va_end (argptr);
+
+    l = strlen(text);
+    t_p = text;
+
+// make sure everything goes through, even though we are non-blocking
+    while (l)
+    {
+        r = write (1, text, l);
+        if (r != l)
+            sleep (0);
+        if (r > 0)
+        {
+            t_p += r;
+            l -= r;
+        }
+    }
+
+}
+*/
+
+void Sys_Printf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		text[1024];
+	unsigned char		*p;
+
+	va_start (argptr,fmt);
+	vsprintf (text,fmt,argptr);
+	va_end (argptr);
+
+	if (strlen(text) > sizeof(text))
+		Sys_Error("memory overwrite in Sys_Printf");
+
+    if (nostdout)
+        return;
+
+	for (p = (unsigned char *)text; *p; p++) {
+		*p &= 0x7f;
+		if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
+			printf("[%02x]", *p);
+		else
+			putc(*p, stdout);
+	}
+}
+
+#if 0
+static char end1[] =
+"\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H                QUAKE: The Doomed Dimension \x1b[33mby \x1b[44mid\x1b[41m Software                      \x1b[2;1H  ----------------------------------------------------------------------------  \x1b[3;1H           CALL 1-800-IDGAMES TO ORDER OR FOR TECHNICAL SUPPORT                 \x1b[4;1H             PRICE: $45.00 (PRICES MAY VARY OUTSIDE THE US.)                    \x1b[5;1H                                                                                \x1b[6;1H  \x1b[37mYes! You only have one fourth of this incredible epic. That is because most   \x1b[7;1H   of you have paid us nothing or at most, very little. You could steal the     \x1b[8;1H   game from a friend. But we both know you'll be punished by God if you do.    \x1b[9;1H        \x1b[33mWHY RISK ETERNAL DAMNATION? CALL 1-800-IDGAMES AND BUY NOW!             \x1b[10;1H             \x1b[37mRemember, we love you almost as much as He does.                   \x1b[11;1H                                                                                \x1b[12;1H            \x1b[33mProgramming: \x1b[37mJohn Carmack, Michael Abrash, John Cash                \x1b[13;1H       \x1b[33mDesign: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits         \x1b[14;1H                     \x1b[33mArt: \x1b[37mAdrian Carmack, Kevin Cloud                           \x1b[15;1H               \x1b[33mBiz: \x1b[37mJay Wilbur, Mike Wilson, Donna Jackson                      \x1b[16;1H            \x1b[33mProjects: \x1b[37mShawn Green   \x1b[33mSupport: \x1b[37mBarrett Alexander                  \x1b[17;1H              \x1b[33mSound Effects: \x1b[37mTrent Reznor and Nine Inch Nails                   \x1b[18;1H  For other information or details on ordering outside the US, check out the    \x1b[19;1H     files accompanying QUAKE or our website at http://www.idsoftware.com.      \x1b[20;1H    \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc.        \x1b[21;1H     All rights reserved. NIN logo is a registered trademark licensed to        \x1b[22;1H                 Nothing Interactive, Inc. All rights reserved.                 \x1b[40m\x1b[23;1H\x1b[0m";
+static char end2[] =
+"\x1b[?7h\x1b[40m\x1b[2J\x1b[0;1;41m\x1b[1;1H        QUAKE \x1b[33mby \x1b[44mid\x1b[41m Software                                                    \x1b[2;1H -----------------------------------------------------------------------------  \x1b[3;1H        \x1b[37mWhy did you quit from the registered version of QUAKE? Did the          \x1b[4;1H        scary monsters frighten you? Or did Mr. Sandman tug at your             \x1b[5;1H        little lids? No matter! What is important is you love our               \x1b[6;1H        game, and gave us your money. Congratulations, you are probably         \x1b[7;1H        not a thief.                                                            \x1b[8;1H                                                           Thank You.           \x1b[9;1H        \x1b[33;44mid\x1b[41m Software is:                                                         \x1b[10;1H        PROGRAMMING: \x1b[37mJohn Carmack, Michael Abrash, John Cash                    \x1b[11;1H        \x1b[33mDESIGN: \x1b[37mJohn Romero, Sandy Petersen, American McGee, Tim Willits        \x1b[12;1H        \x1b[33mART: \x1b[37mAdrian Carmack, Kevin Cloud                                        \x1b[13;1H        \x1b[33mBIZ: \x1b[37mJay Wilbur, Mike Wilson     \x1b[33mPROJECTS MAN: \x1b[37mShawn Green              \x1b[14;1H        \x1b[33mBIZ ASSIST: \x1b[37mDonna Jackson        \x1b[33mSUPPORT: \x1b[37mBarrett Alexander             \x1b[15;1H        \x1b[33mSOUND EFFECTS AND MUSIC: \x1b[37mTrent Reznor and Nine Inch Nails               \x1b[16;1H                                                                                \x1b[17;1H        If you need help running QUAKE refer to the text files in the           \x1b[18;1H        QUAKE directory, or our website at http://www.idsoftware.com.           \x1b[19;1H        If all else fails, call our technical support at 1-800-IDGAMES.         \x1b[20;1H      \x1b[0;41mQuake is a trademark of Id Software, inc., (c)1996 Id Software, inc.      \x1b[21;1H        All rights reserved. NIN logo is a registered trademark licensed        \x1b[22;1H             to Nothing Interactive, Inc. All rights reserved.                  \x1b[23;1H\x1b[40m\x1b[0m";
+
+#endif
+void Sys_Quit (void)
+{
+	Host_Shutdown();
+    fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+#if 0
+	if (registered.value)
+		printf("%s", end2);
+	else
+		printf("%s", end1);
+#endif
+	fflush(stdout);
+	exit(0);
+}
+
+void Sys_Init(void)
+{
+#if id386
+	Sys_SetFPCW();
+#endif
+}
+
+void Sys_Error (char *error, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+
+// change stdin to non blocking
+    fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
+    
+    va_start (argptr,error);
+    vsprintf (string,error,argptr);
+    va_end (argptr);
+	fprintf(stderr, "Error: %s\n", string);
+
+	Host_Shutdown ();
+	exit (1);
+
+} 
+
+void Sys_Warn (char *warning, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+    
+    va_start (argptr,warning);
+    vsprintf (string,warning,argptr);
+    va_end (argptr);
+	fprintf(stderr, "Warning: %s", string);
+} 
+
+/*
+============
+Sys_FileTime
+
+returns -1 if not present
+============
+*/
+int	Sys_FileTime (char *path)
+{
+	struct	stat	buf;
+	
+	if (stat (path,&buf) == -1)
+		return -1;
+	
+	return buf.st_mtime;
+}
+
+
+void Sys_mkdir (char *path)
+{
+    mkdir (path, 0777);
+}
+
+int Sys_FileOpenRead (char *path, int *handle)
+{
+	int	h;
+	struct stat	fileinfo;
+    
+	
+	h = open (path, O_RDONLY, 0666);
+	*handle = h;
+	if (h == -1)
+		return -1;
+	
+	if (fstat (h,&fileinfo) == -1)
+		Sys_Error ("Error fstating %s", path);
+
+	return fileinfo.st_size;
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+	int     handle;
+
+	umask (0);
+	
+	handle = open(path,O_RDWR | O_CREAT | O_TRUNC
+	, 0666);
+
+	if (handle == -1)
+		Sys_Error ("Error opening %s: %s", path,strerror(errno));
+
+	return handle;
+}
+
+int Sys_FileWrite (int handle, void *src, int count)
+{
+	return write (handle, src, count);
+}
+
+void Sys_FileClose (int handle)
+{
+	close (handle);
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+	lseek (handle, position, SEEK_SET);
+}
+
+int Sys_FileRead (int handle, void *dest, int count)
+{
+    return read (handle, dest, count);
+}
+
+void Sys_DebugLog(char *file, char *fmt, ...)
+{
+    va_list argptr; 
+    static char data[1024];
+    int fd;
+    
+    va_start(argptr, fmt);
+    vsprintf(data, fmt, argptr);
+    va_end(argptr);
+//    fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666);
+    fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    write(fd, data, strlen(data));
+    close(fd);
+}
+
+void Sys_EditFile(char *filename)
+{
+
+	char cmd[256];
+	char *term;
+	char *editor;
+
+	term = getenv("TERM");
+	if (term && !strcmp(term, "xterm"))
+	{
+		editor = getenv("VISUAL");
+		if (!editor)
+			editor = getenv("EDITOR");
+		if (!editor)
+			editor = getenv("EDIT");
+		if (!editor)
+			editor = "vi";
+		sprintf(cmd, "xterm -e %s %s", editor, filename);
+		system(cmd);
+	}
+
+}
+
+double Sys_FloatTime (void)
+{
+    struct timeval tp;
+    struct timezone tzp; 
+    static int      secbase; 
+    
+    gettimeofday(&tp, &tzp);  
+
+    if (!secbase)
+    {
+        secbase = tp.tv_sec;
+        return tp.tv_usec/1000000.0;
+    }
+
+    return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+}
+
+// =======================================================================
+// Sleeps for microseconds
+// =======================================================================
+
+static volatile int oktogo;
+
+void alarm_handler(int x)
+{
+	oktogo=1;
+}
+
+void Sys_LineRefresh(void)
+{
+}
+
+void floating_point_exception_handler(int whatever)
+{
+//	Sys_Warn("floating point exception\n");
+	signal(SIGFPE, floating_point_exception_handler);
+}
+
+char *Sys_ConsoleInput(void)
+{
+    static char text[256];
+    int     len;
+	fd_set	fdset;
+    struct timeval timeout;
+
+	if (cls.state == ca_dedicated) {
+		FD_ZERO(&fdset);
+		FD_SET(0, &fdset); // stdin
+		timeout.tv_sec = 0;
+		timeout.tv_usec = 0;
+		if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
+			return NULL;
+
+		len = read (0, text, sizeof(text));
+		if (len < 1)
+			return NULL;
+		text[len-1] = 0;    // rip off the /n and terminate
+
+		return text;
+	}
+	return NULL;
+}
+
+#if !id386
+void Sys_HighFPPrecision (void)
+{
+}
+
+void Sys_LowFPPrecision (void)
+{
+}
+#endif
+
+int main (int c, char **v)
+{
+
+	double		time, oldtime, newtime;
+	quakeparms_t parms;
+	extern int vcrFile;
+	extern int recording;
+	int j;
+
+//	static char cwd[1024];
+
+//	signal(SIGFPE, floating_point_exception_handler);
+	signal(SIGFPE, SIG_IGN);
+
+	memset(&parms, 0, sizeof(parms));
+
+	COM_InitArgv(c, v);
+	parms.argc = com_argc;
+	parms.argv = com_argv;
+
+#ifdef GLQUAKE
+	parms.memsize = 16*1024*1024;
+#else
+	parms.memsize = 8*1024*1024;
+#endif
+
+	j = COM_CheckParm("-mem");
+	if (j)
+		parms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024);
+	parms.membase = malloc (parms.memsize);
+
+	parms.basedir = basedir;
+// caching is disabled by default, use -cachedir to enable
+//	parms.cachedir = cachedir;
+
+	fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
+
+    Host_Init(&parms);
+
+	Sys_Init();
+
+	if (COM_CheckParm("-nostdout"))
+		nostdout = 1;
+	else {
+		fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
+		printf ("Linux Quake -- Version %0.3f\n", LINUX_VERSION);
+	}
+
+    oldtime = Sys_FloatTime () - 0.1;
+    while (1)
+    {
+// find time spent rendering last frame
+        newtime = Sys_FloatTime ();
+        time = newtime - oldtime;
+
+        if (cls.state == ca_dedicated)
+        {   // play vcrfiles at max speed
+            if (time < sys_ticrate.value && (vcrFile == -1 || recording) )
+            {
+				usleep(1);
+                continue;       // not time to run a server only tic yet
+            }
+            time = sys_ticrate.value;
+        }
+
+        if (time > sys_ticrate.value*2)
+            oldtime = newtime;
+        else
+            oldtime += time;
+
+        Host_Frame (time);
+
+// graphic debugging aids
+        if (sys_linerefresh.value)
+            Sys_LineRefresh ();
+    }
+
+}
+
+
+/*
+================
+Sys_MakeCodeWriteable
+================
+*/
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+
+	int r;
+	unsigned long addr;
+	int psize = getpagesize();
+
+	addr = (startaddr & ~(psize-1)) - psize;
+
+//	fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr,
+//			addr, startaddr+length, length);
+
+	r = mprotect((char*)addr, length + startaddr - addr + psize, 7);
+
+	if (r < 0)
+    		Sys_Error("Protection change failed\n");
+
+}
+
diff --git a/apps/plugins/sdl/progs/quake/sys_null.c b/apps/plugins/sdl/progs/quake/sys_null.c
new file mode 100644
index 0000000..057d61d
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sys_null.c
@@ -0,0 +1,232 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// sys_null.h -- null system driver to aid porting efforts
+
+#include "quakedef.h"
+#include "errno.h"
+
+/*
+===============================================================================
+
+FILE IO
+
+===============================================================================
+*/
+
+#define MAX_HANDLES             10
+FILE    *sys_handles[MAX_HANDLES];
+
+int             findhandle (void)
+{
+	int             i;
+	
+	for (i=1 ; i<MAX_HANDLES ; i++)
+		if (!sys_handles[i])
+			return i;
+	Sys_Error ("out of handles");
+	return -1;
+}
+
+/*
+================
+filelength
+================
+*/
+int filelength (FILE *f)
+{
+	int             pos;
+	int             end;
+
+	pos = ftell (f);
+	fseek (f, 0, SEEK_END);
+	end = ftell (f);
+	fseek (f, pos, SEEK_SET);
+
+	return end;
+}
+
+int Sys_FileOpenRead (char *path, int *hndl)
+{
+	FILE    *f;
+	int             i;
+	
+	i = findhandle ();
+
+	f = fopen(path, "rb");
+	if (!f)
+	{
+		*hndl = -1;
+		return -1;
+	}
+	sys_handles[i] = f;
+	*hndl = i;
+	
+	return filelength(f);
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+	FILE    *f;
+	int             i;
+	
+	i = findhandle ();
+
+	f = fopen(path, "wb");
+	if (!f)
+		Sys_Error ("Error opening %s: %s", path,strerror(errno));
+	sys_handles[i] = f;
+	
+	return i;
+}
+
+void Sys_FileClose (int handle)
+{
+	fclose (sys_handles[handle]);
+	sys_handles[handle] = NULL;
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+	fseek (sys_handles[handle], position, SEEK_SET);
+}
+
+int Sys_FileRead (int handle, void *dest, int count)
+{
+	return fread (dest, 1, count, sys_handles[handle]);
+}
+
+int Sys_FileWrite (int handle, void *data, int count)
+{
+	return fwrite (data, 1, count, sys_handles[handle]);
+}
+
+int     Sys_FileTime (char *path)
+{
+	FILE    *f;
+	
+	f = fopen(path, "rb");
+	if (f)
+	{
+		fclose(f);
+		return 1;
+	}
+	
+	return -1;
+}
+
+void Sys_mkdir (char *path)
+{
+}
+
+
+/*
+===============================================================================
+
+SYSTEM IO
+
+===============================================================================
+*/
+
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+}
+
+
+void Sys_Error (char *error, ...)
+{
+	va_list         argptr;
+
+	printf ("Sys_Error: ");   
+	va_start (argptr,error);
+	vprintf (error,argptr);
+	va_end (argptr);
+	printf ("\n");
+
+	exit (1);
+}
+
+void Sys_Printf (char *fmt, ...)
+{
+	va_list         argptr;
+	
+	va_start (argptr,fmt);
+	vprintf (fmt,argptr);
+	va_end (argptr);
+}
+
+void Sys_Quit (void)
+{
+	exit (0);
+}
+
+double Sys_FloatTime (void)
+{
+	static double t;
+	
+	t += 0.1;
+	
+	return t;
+}
+
+char *Sys_ConsoleInput (void)
+{
+	return NULL;
+}
+
+void Sys_Sleep (void)
+{
+}
+
+void Sys_SendKeyEvents (void)
+{
+}
+
+void Sys_HighFPPrecision (void)
+{
+}
+
+void Sys_LowFPPrecision (void)
+{
+}
+
+//=============================================================================
+
+void main (int argc, char **argv)
+{
+	static quakeparms_t    parms;
+
+	parms.memsize = 8*1024*1024;
+	parms.membase = malloc (parms.memsize);
+	parms.basedir = ".";
+
+	COM_InitArgv (argc, argv);
+
+	parms.argc = com_argc;
+	parms.argv = com_argv;
+
+	printf ("Host_Init\n");
+	Host_Init (&parms);
+	while (1)
+	{
+		Host_Frame (0.1);
+	}
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/sys_sdl.c b/apps/plugins/sdl/progs/quake/sys_sdl.c
new file mode 100644
index 0000000..3cc3122
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/sys_sdl.c
@@ -0,0 +1,436 @@
+#include <limits.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "quakedef.h"
+
+qboolean			isDedicated;
+
+int noconinput = 0;
+
+char *basedir = "/.rockbox/quake";
+char *cachedir = NULL;
+
+cvar_t  sys_linerefresh = {"sys_linerefresh","0"};// set for entity display
+cvar_t  sys_nostdout = {"sys_nostdout","0"};
+
+// =======================================================================
+// General routines
+// =======================================================================
+
+void Sys_DebugNumber(int y, int val)
+{
+}
+
+int enable_printf = 1;
+
+void Sys_Printf (char *fmt, ...)
+{
+	va_list		argptr;
+	char		text[1024];
+	
+	va_start (argptr,fmt);
+	vsprintf (text,fmt,argptr);
+	va_end (argptr);
+        if(enable_printf)
+        {
+            printf("%s", text);
+        }
+        LOGF("%s", text);
+	
+	//Con_Print (text);
+}
+
+void Sys_Quit (void)
+{
+	Host_Shutdown();
+	exit(0);
+}
+
+void Sys_Init(void)
+{
+#if id386
+	Sys_SetFPCW();
+#endif
+}
+
+#if !id386
+
+/*
+================
+Sys_LowFPPrecision
+================
+*/
+void Sys_LowFPPrecision (void)
+{
+// causes weird problems on Nextstep
+}
+
+
+/*
+================
+Sys_HighFPPrecision
+================
+*/
+void Sys_HighFPPrecision (void)
+{
+// causes weird problems on Nextstep
+}
+
+#endif	// !id386
+
+
+void Sys_Error (char *error, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+
+    va_start (argptr,error);
+    vsprintf (string,error,argptr);
+    va_end (argptr);
+    rb->splashf(HZ*5, "Error: %s\n", string);
+
+	Host_Shutdown ();
+	exit (1);
+
+} 
+
+void Sys_Warn (char *warning, ...)
+{ 
+    va_list     argptr;
+    char        string[1024];
+    
+    va_start (argptr,warning);
+    vsprintf (string,warning,argptr);
+    va_end (argptr);
+	rb->splashf(HZ*2, "Warning: %s", string);
+} 
+
+/*
+===============================================================================
+
+FILE IO
+
+===============================================================================
+*/
+
+#define	MAX_HANDLES		10
+static FILE	*sys_handles[MAX_HANDLES];
+
+void Sys_Shutdown(void)
+{
+    for(int i = 0; i < MAX_HANDLES; i++)
+    {
+        FILE *f = sys_handles[i];
+        if(f)
+            fclose(f);
+        sys_handles[i] = NULL;
+    }
+}
+
+int		findhandle (void)
+{
+	int		i;
+	
+	for (i=1 ; i<MAX_HANDLES ; i++)
+		if (!sys_handles[i])
+			return i;
+	Sys_Error ("out of handles");
+	return -1;
+}
+
+/*
+================
+Qfilelength
+================
+*/
+static int Qfilelength (FILE *f)
+{
+	int		pos;
+	int		end;
+
+	pos = ftell (f);
+	fseek (f, 0, SEEK_END);
+	end = ftell (f);
+	fseek (f, pos, SEEK_SET);
+
+	return end;
+}
+
+int Sys_FileOpenRead (char *path, int *hndl)
+{
+	FILE	*f;
+	int		i;
+	
+	i = findhandle ();
+
+	f = fopen(path, "rb");
+	if (!f)
+	{
+		*hndl = -1;
+		return -1;
+	}
+	sys_handles[i] = f;
+	*hndl = i;
+
+        //rb->splashf(HZ*2, "Allocating handle %d to %s", i, path);
+        
+	
+	return Qfilelength(f);
+}
+
+int Sys_FileOpenWrite (char *path)
+{
+	FILE	*f;
+	int		i;
+	
+	i = findhandle ();
+
+	f = fopen(path, "wb");
+	if (!f)
+		Sys_Error ("Error opening %s: %s", path,strerror(errno));
+	sys_handles[i] = f;
+	
+	return i;
+}
+
+void Sys_FileClose (int handle)
+{
+	if ( handle >= 0 ) {
+            //rb->splashf(HZ, "Close handle %d", handle);
+		fclose (sys_handles[handle]);
+		sys_handles[handle] = NULL;
+	}
+}
+
+void Sys_FileSeek (int handle, int position)
+{
+	if ( handle >= 0 ) {
+		fseek (sys_handles[handle], position, SEEK_SET);
+	}
+}
+
+int Sys_FileRead (int handle, void *dst, int count)
+{
+	char *data;
+	int size, done;
+
+	size = 0;
+	if ( handle >= 0 ) {
+		data = dst;
+		while ( count > 0 ) {
+			done = fread (data, 1, count, sys_handles[handle]);
+			if ( done == 0 ) {
+				break;
+			}
+                        else if(done < 0)
+                        {
+                            Sys_Error("stream error %d, file is %d = %d", done, handle, sys_handles[handle]);
+                        }
+			data += done;
+			count -= done;
+			size += done;
+		}
+	}
+	return size;
+		
+}
+
+int Sys_FileWrite (int handle, void *src, int count)
+{
+	char *data;
+	int size, done;
+
+	size = 0;
+	if ( handle >= 0 ) {
+		data = src;
+		while ( count > 0 ) {
+			done = fread (data, 1, count, sys_handles[handle]);
+			if ( done == 0 ) {
+				break;
+			}
+			data += done;
+			count -= done;
+			size += done;
+		}
+	}
+	return size;
+}
+
+int	Sys_FileTime (char *path)
+{
+	FILE	*f;
+	
+	f = fopen(path, "rb");
+	if (f)
+	{
+		fclose(f);
+		return 1;
+	}
+	
+	return -1;
+}
+
+void Sys_mkdir (char *path)
+{
+#ifdef __WIN32__
+    mkdir (path);
+#else
+    mkdir (path);
+#endif
+}
+
+void Sys_DebugLog(char *file, char *fmt, ...)
+{
+    va_list argptr; 
+    static char data[1024];
+    FILE *fp;
+    
+    va_start(argptr, fmt);
+    vsprintf(data, fmt, argptr);
+    va_end(argptr);
+    fp = fopen(file, "a");
+    fwrite(data, strlen(data), 1, fp);
+    fclose(fp);
+}
+
+double Sys_FloatTime (void)
+{
+	static int starttime = 0;
+
+	if ( ! starttime )
+		starttime = *rb->current_tick;
+
+	return (*rb->current_tick - starttime) / ((double)HZ);
+}
+
+// =======================================================================
+// Sleeps for microseconds
+// =======================================================================
+
+static volatile int oktogo;
+
+void alarm_handler(int x)
+{
+	oktogo=1;
+}
+
+byte *Sys_ZoneBase (int *size)
+{
+
+	char *QUAKEOPT = getenv("QUAKEOPT");
+
+	*size = 0xc00000;
+	if (QUAKEOPT)
+	{
+		while (*QUAKEOPT)
+			if (tolower(*QUAKEOPT++) == 'm')
+			{
+				*size = atof(QUAKEOPT) * 1024*1024;
+				break;
+			}
+	}
+	return malloc (*size);
+
+}
+
+void Sys_LineRefresh(void)
+{
+}
+
+void Sys_Sleep(void)
+{
+	SDL_Delay(1);
+}
+
+void floating_point_exception_handler(int whatever)
+{
+//	Sys_Warn("floating point exception\n");
+}
+
+void moncontrol(int x)
+{
+}
+
+int my_main (int c, char **v)
+{
+	double		time, oldtime, newtime;
+	quakeparms_t parms;
+	extern int vcrFile;
+	extern int recording;
+	static int frame;
+
+	moncontrol(0);
+
+//	signal(SIGFPE, floating_point_exception_handler);
+
+        //rb->splash(0, "quake 1");
+        
+	parms.memsize = 8*1024*1024;
+	parms.membase = malloc (parms.memsize);
+	parms.basedir = basedir;
+	parms.cachedir = cachedir;
+
+	COM_InitArgv(c, v);
+	parms.argc = com_argc;
+	parms.argv = com_argv;
+
+	Sys_Init();
+        //rb->splash(0, "quake 2");
+
+    Host_Init(&parms);
+    //rb->splash(0, "quake 3");
+        
+	//Cvar_RegisterVariable (&sys_nostdout);
+    //rb->splash(0, "quake 4");
+
+    oldtime = Sys_FloatTime () - 0.1;
+    while (1)
+    {
+// find time spent rendering last frame
+        newtime = Sys_FloatTime ();
+        time = newtime - oldtime;
+
+        if (cls.state == ca_dedicated)
+        {   // play vcrfiles at max speed
+            if (time < sys_ticrate.value && (vcrFile == -1 || recording) )
+            {
+                rb->yield();
+                continue;       // not time to run a server only tic yet
+            }
+            time = sys_ticrate.value;
+        }
+
+        if (time > sys_ticrate.value*2)
+            oldtime = newtime;
+        else
+            oldtime += time;
+
+        if (++frame > 10)
+            moncontrol(1);      // profile only while we do each Quake frame
+        Host_Frame (time);
+        moncontrol(0);
+
+// graphic debugging aids
+        if (sys_linerefresh.value)
+            Sys_LineRefresh ();
+
+        rb->yield();
+    }
+
+}
+
+
+/*
+================
+Sys_MakeCodeWriteable
+================
+*/
+void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
+{
+
+    Sys_Error("Protection change failed\n");
+
+}
+
diff --git a/apps/plugins/sdl/progs/quake/vgamodes.h b/apps/plugins/sdl/progs/quake/vgamodes.h
new file mode 100644
index 0000000..4d609fc
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vgamodes.h
@@ -0,0 +1,599 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// vgamodes.h: VGA mode set tables
+//
+
+#include "vregset.h"
+
+int		VGA_InitMode (viddef_t *vid, vmode_t *pcurrentmode);
+void	VGA_SwapBuffers (viddef_t *vid, vmode_t *pcurrentmode, vrect_t *rects);
+void	VGA_SetPalette (viddef_t *vid, vmode_t *pcurrentmode,
+						unsigned char *pal);
+
+///////////////////////////////////////////////////////////////////////////
+// the following base mode descriptors plus extra data together provide all
+// the data needed to do VGA mode sets
+///////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+	int		vidbuffer;
+	int		*pregset;
+} vextra_t;
+
+int	vrsnull[] = {
+	VRS_END,
+};
+
+int vrs320x200x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE,
+	VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04,
+	VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE,
+	VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00,
+	VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS,
+	VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40,
+
+	VRS_END,
+};
+
+int vrs360x200x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_WORD_OUT, SC_INDEX, 0x0604,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0x67,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA,  ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA,  ~0x00, 0x40,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x6B00,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5901,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5A02,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8E03,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5E04,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8A05,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3013,
+
+	VRS_END,
+};
+
+int vrs320x240x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE,
+	VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04,
+	VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE,
+	VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00,
+	VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS,
+	VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x0D06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3E07,
+	VRS_WORD_OUT, CRTC_INDEX, 0x4109,
+	VRS_WORD_OUT, CRTC_INDEX, 0xEA10,
+	VRS_WORD_OUT, CRTC_INDEX, 0xAC11,
+	VRS_WORD_OUT, CRTC_INDEX, 0xDF12,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0014,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE715,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0616,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE317,
+
+	VRS_END,
+};
+
+int vrs360x240x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_WORD_OUT, SC_INDEX, 0x0604,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0xE7,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x6B00,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5901,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5A02,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8E03,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5E04,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8A05,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0D06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3E07,
+	VRS_WORD_OUT, CRTC_INDEX, 0x4109,
+	VRS_WORD_OUT, CRTC_INDEX, 0xEA10,
+	VRS_WORD_OUT, CRTC_INDEX, 0xAC11,
+	VRS_WORD_OUT, CRTC_INDEX, 0xDF12,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3013,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0014,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE715,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0616,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE317,
+
+	VRS_END,
+};
+
+int vrs320x350x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE,
+	VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04,
+	VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE,
+	VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00,
+	VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS,
+	VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0xA3,	// 350-scan-line scan rate
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// stop scanning each line twice
+//
+	VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40,
+
+//
+// set the vertical counts for 350-scan-line mode
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0xBF06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x1F07,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8310,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8511,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5D12,
+	VRS_WORD_OUT, CRTC_INDEX, 0x6315,
+	VRS_WORD_OUT, CRTC_INDEX, 0xBA16,
+
+	VRS_END,
+};
+
+int vrs360x350x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_WORD_OUT, SC_INDEX, 0x0604,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0xA7,	// 350-scan-line scan rate
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// stop scanning each line twice
+//
+	VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40,
+
+//
+// set the vertical counts for 350-scan-line mode and 360 pixels across
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x6B00,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5901,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5A02,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8E03,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5E04,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8A05,
+	VRS_WORD_OUT, CRTC_INDEX, 0xBF06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x1F07,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8310,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8511,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5D12,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3013,
+	VRS_WORD_OUT, CRTC_INDEX, 0x6315,
+	VRS_WORD_OUT, CRTC_INDEX, 0xBA16,
+
+	VRS_END,
+};
+
+int vrs320x400x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+
+	VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE,
+	VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04,
+	VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE,
+	VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00,
+	VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS,
+	VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// stop scanning each line twice
+//
+	VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40,
+
+	VRS_END,
+};
+
+int vrs360x400x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_WORD_OUT, SC_INDEX, 0x0604,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0x67,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// stop scanning each line twice
+//
+	VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA,  ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA,  ~0x00, 0x40,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x6B00,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5901,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5A02,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8E03,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5E04,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8A05,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3013,
+
+	VRS_END,
+};
+
+int vrs320x480x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE,
+	VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04,
+	VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE,
+	VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00,
+	VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS,
+	VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// stop scanning each line twice
+//
+	VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00,
+
+//
+// change the CRTC from doubleword to byte mode
+//
+	VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00,
+	VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x0D06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3E07,
+	VRS_WORD_OUT, CRTC_INDEX, 0xEA10,
+	VRS_WORD_OUT, CRTC_INDEX, 0xAC11,
+	VRS_WORD_OUT, CRTC_INDEX, 0xDF12,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE715,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0616,
+
+	VRS_END,
+};
+
+int vrs360x480x256planar[] = {
+//
+// switch to linear, non-chain4 mode
+//
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  1,
+
+	VRS_WORD_OUT, SC_INDEX, 0x0604,
+	VRS_BYTE_OUT, MISC_OUTPUT, 0xE7,
+
+	VRS_BYTE_OUT, SC_INDEX, SYNC_RESET,
+	VRS_BYTE_OUT, SC_DATA,  3,
+
+//
+// unprotect CRTC0 through CRTC0
+//
+	VRS_BYTE_OUT, CRTC_INDEX, 0x11,
+	VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00,
+
+//
+// set up the CRT Controller
+//
+	VRS_WORD_OUT, CRTC_INDEX, 0x6B00,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5901,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5A02,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8E03,
+	VRS_WORD_OUT, CRTC_INDEX, 0x5E04,
+	VRS_WORD_OUT, CRTC_INDEX, 0x8A05,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0D06,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3E07,
+	VRS_WORD_OUT, CRTC_INDEX, 0x4009,
+	VRS_WORD_OUT, CRTC_INDEX, 0xEA10,
+	VRS_WORD_OUT, CRTC_INDEX, 0xAC11,
+	VRS_WORD_OUT, CRTC_INDEX, 0xDF12,
+	VRS_WORD_OUT, CRTC_INDEX, 0x3013,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0014,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE715,
+	VRS_WORD_OUT, CRTC_INDEX, 0x0616,
+	VRS_WORD_OUT, CRTC_INDEX, 0xE317,
+
+	VRS_END,
+};
+
+//
+// extra VGA-specific data for vgavidmodes
+//
+vextra_t	extra320x200x256linear = {
+	1, vrsnull
+};
+vextra_t	extra320x200x256planar = {
+	1, vrs320x200x256planar
+};
+vextra_t	extra360x200x256planar = {
+	1, vrs360x200x256planar
+};
+vextra_t	extra320x240x256planar = {
+	1, vrs320x240x256planar
+};
+vextra_t	extra360x240x256planar = {
+	1, vrs360x240x256planar
+};
+vextra_t	extra320x350x256planar = {
+	1, vrs320x350x256planar
+};
+vextra_t	extra360x350x256planar = {
+	1, vrs360x350x256planar
+};
+vextra_t	extra320x400x256planar = {
+	1, vrs320x400x256planar
+};
+vextra_t	extra360x400x256planar = {
+	1, vrs360x400x256planar
+};
+vextra_t	extra320x480x256planar = {
+	1, vrs320x480x256planar
+};
+vextra_t	extra360x480x256planar = {
+	1, vrs360x480x256planar
+};
+
+//
+// base mode descriptors, in ascending order of number of pixels
+//
+
+vmode_t	vgavidmodes[] = {
+{
+	NULL,
+	"320x200", "    ***** standard VGA modes *****    ",
+	320, 200, (200.0/320.0)*(320.0/240.0), 320, 0, 1, &extra320x200x256linear,
+	VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette,
+	VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"320x200", "    ***** Mode X-style modes *****    ",
+	320, 200, (200.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x200x256planar,
+	VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette,
+	VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"360x200", NULL, 360, 200, (200.0/360.0)*(320.0/240.0),
+	384, 1, 1, &extra360x200x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"320x240", NULL, 320, 240, (240.0/320.0)*(320.0/240.0),
+	320, 1, 1, &extra320x240x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"360x240", NULL, 360, 240, (240.0/360.0)*(320.0/240.0),
+	384, 1, 1, &extra360x240x256planar,
+	VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette,
+	VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"320x350", NULL, 320, 350, (350.0/320.0)*(320.0/240.0),
+	320, 1, 1, &extra320x350x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"360x350", NULL, 360, 350, (350.0/360.0)*(320.0/240.0),
+	384, 1, 1, &extra360x350x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"320x400", NULL, 320, 400, (400.0/320.0)*(320.0/240.0), 320,
+	1, 1, &extra320x400x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"360x400", NULL, 360, 400, (400.0/360.0)*(320.0/240.0),
+	384, 1, 1, &extra360x400x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"320x480", NULL, 320, 480, (480.0/320.0)*(320.0/240.0),
+	320, 1, 1, &extra320x480x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+{
+	NULL,
+	"360x480", NULL, 360, 480, (480.0/360.0)*(320.0/240.0),
+	384, 1, 1, &extra360x480x256planar, VGA_InitMode,
+	VGA_SwapBuffers, 
+	VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect
+},
+};
+
diff --git a/apps/plugins/sdl/progs/quake/vid.h b/apps/plugins/sdl/progs/quake/vid.h
new file mode 100644
index 0000000..1708ba8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vid.h
@@ -0,0 +1,85 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// vid.h -- video driver defs
+
+#define VID_CBITS	6
+#define VID_GRADES	(1 << VID_CBITS)
+
+// a pixel can be one, two, or four bytes
+typedef byte pixel_t;
+
+typedef struct vrect_s
+{
+	int				x,y,width,height;
+	struct vrect_s	*pnext;
+} vrect_t;
+
+typedef struct
+{
+	pixel_t			*buffer;		// invisible buffer
+	pixel_t			*colormap;		// 256 * VID_GRADES size
+	unsigned short	*colormap16;	// 256 * VID_GRADES size
+	int				fullbright;		// index of first fullbright color
+	unsigned		rowbytes;	// may be > width if displayed in a window
+	unsigned		width;		
+	unsigned		height;
+	float			aspect;		// width / height -- < 0 is taller than wide
+	int				numpages;
+	int				recalc_refdef;	// if true, recalc vid-based stuff
+	pixel_t			*conbuffer;
+	int				conrowbytes;
+	unsigned		conwidth;
+	unsigned		conheight;
+	int				maxwarpwidth;
+	int				maxwarpheight;
+	pixel_t			*direct;		// direct drawing to framebuffer, if not
+									//  NULL
+} viddef_t;
+
+extern	viddef_t	vid;				// global video state
+extern	unsigned short	d_8to16table[256];
+extern	unsigned	d_8to24table[256];
+extern void (*vid_menudrawfn)(void);
+extern void (*vid_menukeyfn)(int key);
+
+void	VID_SetPalette (unsigned char *palette);
+// called at startup and after any gamma correction
+
+void	VID_ShiftPalette (unsigned char *palette);
+// called for bonus and pain flashes, and for underwater color changes
+
+void	VID_Init (unsigned char *palette);
+// Called at startup to set up translation tables, takes 256 8 bit RGB values
+// the palette data will go away after the call, so it must be copied off if
+// the video driver will need it again
+
+void	VID_Shutdown (void);
+// Called at shutdown
+
+void	VID_Update (vrect_t *rects);
+// flushes the given rectangles from the view buffer to the screen
+
+int VID_SetMode (int modenum, unsigned char *palette);
+// sets the mode; only used by the Quake engine for resetting to mode 0 (the
+// base mode) on memory allocation failures
+
+void VID_HandlePause (qboolean pause);
+// called only on Win32, when pause happens, so the mouse can be released
+
diff --git a/apps/plugins/sdl/progs/quake/vid_null.c b/apps/plugins/sdl/progs/quake/vid_null.c
new file mode 100644
index 0000000..225691b
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vid_null.c
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// vid_null.c -- null video driver to aid porting efforts
+
+#include "quakedef.h"
+#include "d_local.h"
+
+viddef_t	vid;				// global video state
+
+#define	BASEWIDTH	320
+#define	BASEHEIGHT	200
+
+byte	vid_buffer[BASEWIDTH*BASEHEIGHT];
+short	zbuffer[BASEWIDTH*BASEHEIGHT];
+byte	surfcache[256*1024];
+
+unsigned short	d_8to16table[256];
+unsigned	d_8to24table[256];
+
+void	VID_SetPalette (unsigned char *palette)
+{
+}
+
+void	VID_ShiftPalette (unsigned char *palette)
+{
+}
+
+void	VID_Init (unsigned char *palette)
+{
+	vid.maxwarpwidth = vid.width = vid.conwidth = BASEWIDTH;
+	vid.maxwarpheight = vid.height = vid.conheight = BASEHEIGHT;
+	vid.aspect = 1.0;
+	vid.numpages = 1;
+	vid.colormap = host_colormap;
+	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+	vid.buffer = vid.conbuffer = vid_buffer;
+	vid.rowbytes = vid.conrowbytes = BASEWIDTH;
+	
+	d_pzbuffer = zbuffer;
+	D_InitCaches (surfcache, sizeof(surfcache));
+}
+
+void	VID_Shutdown (void)
+{
+}
+
+void	VID_Update (vrect_t *rects)
+{
+}
+
+/*
+================
+D_BeginDirectRect
+================
+*/
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
+{
+}
+
+
+/*
+================
+D_EndDirectRect
+================
+*/
+void D_EndDirectRect (int x, int y, int width, int height)
+{
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/vid_sdl.c b/apps/plugins/sdl/progs/quake/vid_sdl.c
new file mode 100644
index 0000000..83d6a84
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vid_sdl.c
@@ -0,0 +1,389 @@
+// vid_sdl.h -- sdl video driver 
+
+#include "SDL.h"
+#include "quakedef.h"
+#include "d_local.h"
+
+viddef_t    vid;                // global video state
+unsigned short  d_8to16table[256];
+
+// The original defaults
+//#define    BASEWIDTH    320
+//#define    BASEHEIGHT   200
+// Much better for high resolution displays
+#define    BASEWIDTH    LCD_WIDTH
+#define    BASEHEIGHT   LCD_HEIGHT
+
+int    VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes = 0;
+byte    *VGA_pagebase;
+
+static SDL_Surface *screen = NULL;
+
+static qboolean mouse_avail;
+static float   mouse_x, mouse_y;
+static int mouse_oldbuttonstate = 0;
+
+// No support for option menus
+void (*vid_menudrawfn)(void) = NULL;
+void (*vid_menukeyfn)(int key) = NULL;
+
+void    VID_SetPalette (unsigned char *palette)
+{
+    int i;
+    SDL_Color colors[256];
+
+    for ( i=0; i<256; ++i )
+    {
+        colors[i].r = *palette++;
+        colors[i].g = *palette++;
+        colors[i].b = *palette++;
+    }
+    SDL_SetColors(screen, colors, 0, 256);
+}
+
+void    VID_ShiftPalette (unsigned char *palette)
+{
+    VID_SetPalette(palette);
+}
+
+void    VID_Init (unsigned char *palette)
+{
+    int pnum, chunk;
+    byte *cache;
+    int cachesize;
+    Uint8 video_bpp;
+    Uint16 video_w, video_h;
+    Uint32 flags;
+
+    // Load the SDL library
+    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
+        Sys_Error("VID: Couldn't load SDL: %s", SDL_GetError());
+
+    // Set up display mode (width and height)
+    vid.width = BASEWIDTH;
+    vid.height = BASEHEIGHT;
+    vid.maxwarpwidth = WARP_WIDTH;
+    vid.maxwarpheight = WARP_HEIGHT;
+    if ((pnum=COM_CheckParm("-winsize")))
+    {
+        if (pnum >= com_argc-2)
+            Sys_Error("VID: -winsize <width> <height>\n");
+        vid.width = Q_atoi(com_argv[pnum+1]);
+        vid.height = Q_atoi(com_argv[pnum+2]);
+        if (!vid.width || !vid.height)
+            Sys_Error("VID: Bad window width/height\n");
+    }
+
+    // Set video width, height and flags
+    flags = (SDL_SWSURFACE|SDL_HWPALETTE);
+    if ( COM_CheckParm ("-fullscreen") )
+        flags |= SDL_FULLSCREEN;
+
+    // Initialize display 
+    if (!(screen = SDL_SetVideoMode(vid.width, vid.height, 8, flags)))
+        Sys_Error("VID: Couldn't set video mode: %s\n", SDL_GetError());
+    VID_SetPalette(palette);
+    SDL_WM_SetCaption("sdlquake","sdlquake");
+    // now know everything we need to know about the buffer
+    VGA_width = vid.conwidth = vid.width;
+    VGA_height = vid.conheight = vid.height;
+    vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
+    vid.numpages = 1;
+    vid.colormap = host_colormap;
+    vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+    VGA_pagebase = vid.buffer = screen->pixels;
+    VGA_rowbytes = vid.rowbytes = screen->pitch;
+    vid.conbuffer = vid.buffer;
+    vid.conrowbytes = vid.rowbytes;
+    vid.direct = 0;
+    
+    // allocate z buffer and surface cache
+    chunk = vid.width * vid.height * sizeof (*d_pzbuffer);
+    cachesize = D_SurfaceCacheForRes (vid.width, vid.height);
+    chunk += cachesize;
+    d_pzbuffer = Hunk_HighAllocName(chunk, "video");
+    if (d_pzbuffer == NULL)
+        Sys_Error ("Not enough memory for video mode\n");
+
+    // initialize the cache memory 
+        cache = (byte *) d_pzbuffer
+                + vid.width * vid.height * sizeof (*d_pzbuffer);
+    D_InitCaches (cache, cachesize);
+
+    // initialize the mouse
+    SDL_ShowCursor(0);
+}
+
+void    VID_Shutdown (void)
+{
+    SDL_Quit();
+}
+
+void    VID_Update (vrect_t *rects)
+{
+    SDL_Rect *sdlrects;
+    int n, i;
+    vrect_t *rect;
+
+    // Two-pass system, since Quake doesn't do it the SDL way...
+
+    // First, count the number of rectangles
+    n = 0;
+    for (rect = rects; rect; rect = rect->pnext)
+        ++n;
+
+    // Second, copy them to SDL rectangles and update
+    if (!(sdlrects = (SDL_Rect *)alloca(n*sizeof(*sdlrects))))
+        Sys_Error("Out of memory");
+    i = 0;
+    for (rect = rects; rect; rect = rect->pnext)
+    {
+        sdlrects[i].x = rect->x;
+        sdlrects[i].y = rect->y;
+        sdlrects[i].w = rect->width;
+        sdlrects[i].h = rect->height;
+        ++i;
+    }
+    SDL_UpdateRects(screen, n, sdlrects);
+}
+
+/*
+================
+D_BeginDirectRect
+================
+*/
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
+{
+    Uint8 *offset;
+
+
+    if (!screen) return;
+    if ( x < 0 ) x = screen->w+x-1;
+    offset = (Uint8 *)screen->pixels + y*screen->pitch + x;
+    while ( height-- )
+    {
+        memcpy(offset, pbitmap, width);
+        offset += screen->pitch;
+        pbitmap += width;
+    }
+}
+
+
+/*
+================
+D_EndDirectRect
+================
+*/
+void D_EndDirectRect (int x, int y, int width, int height)
+{
+    if (!screen) return;
+    if (x < 0) x = screen->w+x-1;
+    SDL_UpdateRect(screen, x, y, width, height);
+}
+
+
+/*
+================
+Sys_SendKeyEvents
+================
+*/
+
+void Sys_SendKeyEvents(void)
+{
+    SDL_Event event;
+    int sym, state;
+     int modstate;
+
+    while (SDL_PollEvent(&event))
+    {
+        switch (event.type) {
+
+            case SDL_KEYDOWN:
+            case SDL_KEYUP:
+                sym = event.key.keysym.sym;
+                state = event.key.state;
+                modstate = SDL_GetModState();
+                switch(sym)
+                {
+                   case SDLK_DELETE: sym = K_DEL; break;
+                   case SDLK_BACKSPACE: sym = K_BACKSPACE; break;
+                   case SDLK_F1: sym = K_F1; break;
+                   case SDLK_F2: sym = K_F2; break;
+                   case SDLK_F3: sym = K_F3; break;
+                   case SDLK_F4: sym = K_F4; break;
+                   case SDLK_F5: sym = K_F5; break;
+                   case SDLK_F6: sym = K_F6; break;
+                   case SDLK_F7: sym = K_F7; break;
+                   case SDLK_F8: sym = K_F8; break;
+                   case SDLK_F9: sym = K_F9; break;
+                   case SDLK_F10: sym = K_F10; break;
+                   case SDLK_F11: sym = K_F11; break;
+                   case SDLK_F12: sym = K_F12; break;
+                   case SDLK_BREAK:
+                   case SDLK_PAUSE: sym = K_PAUSE; break;
+                   case SDLK_UP: sym = K_UPARROW; break;
+                   case SDLK_DOWN: sym = K_DOWNARROW; break;
+                   case SDLK_RIGHT: sym = K_RIGHTARROW; break;
+                   case SDLK_LEFT: sym = K_LEFTARROW; break;
+                   case SDLK_INSERT: sym = K_INS; break;
+                   case SDLK_HOME: sym = K_HOME; break;
+                   case SDLK_END: sym = K_END; break;
+                   case SDLK_PAGEUP: sym = K_PGUP; break;
+                   case SDLK_PAGEDOWN: sym = K_PGDN; break;
+                   case SDLK_RSHIFT:
+                   case SDLK_LSHIFT: sym = K_SHIFT; break;
+                   case SDLK_RCTRL:
+                   case SDLK_LCTRL: sym = K_CTRL; break;
+                   case SDLK_RALT:
+                   case SDLK_LALT: sym = K_ALT; break;
+                   case SDLK_KP0: 
+                       if(modstate & KMOD_NUM) sym = K_INS; 
+                       else sym = SDLK_0;
+                       break;
+                   case SDLK_KP1:
+                       if(modstate & KMOD_NUM) sym = K_END;
+                       else sym = SDLK_1;
+                       break;
+                   case SDLK_KP2:
+                       if(modstate & KMOD_NUM) sym = K_DOWNARROW;
+                       else sym = SDLK_2;
+                       break;
+                   case SDLK_KP3:
+                       if(modstate & KMOD_NUM) sym = K_PGDN;
+                       else sym = SDLK_3;
+                       break;
+                   case SDLK_KP4:
+                       if(modstate & KMOD_NUM) sym = K_LEFTARROW;
+                       else sym = SDLK_4;
+                       break;
+                   case SDLK_KP5: sym = SDLK_5; break;
+                   case SDLK_KP6:
+                       if(modstate & KMOD_NUM) sym = K_RIGHTARROW;
+                       else sym = SDLK_6;
+                       break;
+                   case SDLK_KP7:
+                       if(modstate & KMOD_NUM) sym = K_HOME;
+                       else sym = SDLK_7;
+                       break;
+                   case SDLK_KP8:
+                       if(modstate & KMOD_NUM) sym = K_UPARROW;
+                       else sym = SDLK_8;
+                       break;
+                   case SDLK_KP9:
+                       if(modstate & KMOD_NUM) sym = K_PGUP;
+                       else sym = SDLK_9;
+                       break;
+                   case SDLK_KP_PERIOD:
+                       if(modstate & KMOD_NUM) sym = K_DEL;
+                       else sym = SDLK_PERIOD;
+                       break;
+                   case SDLK_KP_DIVIDE: sym = SDLK_SLASH; break;
+                   case SDLK_KP_MULTIPLY: sym = SDLK_ASTERISK; break;
+                   case SDLK_KP_MINUS: sym = SDLK_MINUS; break;
+                   case SDLK_KP_PLUS: sym = SDLK_PLUS; break;
+                   case SDLK_KP_ENTER: sym = SDLK_RETURN; break;
+                   case SDLK_KP_EQUALS: sym = SDLK_EQUALS; break;
+                }
+                // If we're not directly handled and still above 255
+                // just force it to 0
+                if(sym > 255) sym = 0;
+                Key_Event(sym, state);
+                break;
+
+            case SDL_MOUSEMOTION:
+                if ( (event.motion.x != (vid.width/2)) ||
+                     (event.motion.y != (vid.height/2)) ) {
+                    mouse_x = event.motion.xrel*10;
+                    mouse_y = event.motion.yrel*10;
+                    if ( (event.motion.x < ((vid.width/2)-(vid.width/4))) ||
+                         (event.motion.x > ((vid.width/2)+(vid.width/4))) ||
+                         (event.motion.y < ((vid.height/2)-(vid.height/4))) ||
+                         (event.motion.y > ((vid.height/2)+(vid.height/4))) ) {
+                        SDL_WarpMouse(vid.width/2, vid.height/2);
+                    }
+                }
+                break;
+
+            case SDL_QUIT:
+                CL_Disconnect ();
+                Host_ShutdownServer(false);        
+                Sys_Quit ();
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+void IN_Init (void)
+{
+    if ( COM_CheckParm ("-nomouse") )
+        return;
+    mouse_x = mouse_y = 0.0;
+    mouse_avail = 1;
+}
+
+void IN_Shutdown (void)
+{
+    mouse_avail = 0;
+}
+
+void IN_Commands (void)
+{
+    int i;
+    int mouse_buttonstate;
+   
+    if (!mouse_avail) return;
+   
+    i = SDL_GetMouseState(NULL, NULL);
+    /* Quake swaps the second and third buttons */
+    mouse_buttonstate = (i & ~0x06) | ((i & 0x02)<<1) | ((i & 0x04)>>1);
+    for (i=0 ; i<3 ; i++) {
+        if ( (mouse_buttonstate & (1<<i)) && !(mouse_oldbuttonstate & (1<<i)) )
+            Key_Event (K_MOUSE1 + i, true);
+
+        if ( !(mouse_buttonstate & (1<<i)) && (mouse_oldbuttonstate & (1<<i)) )
+            Key_Event (K_MOUSE1 + i, false);
+    }
+    mouse_oldbuttonstate = mouse_buttonstate;
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+    if (!mouse_avail)
+        return;
+   
+    mouse_x *= sensitivity.value;
+    mouse_y *= sensitivity.value;
+   
+    if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+        cmd->sidemove += m_side.value * mouse_x;
+    else
+        cl.viewangles[YAW] -= m_yaw.value * mouse_x;
+    if (in_mlook.state & 1)
+        V_StopPitchDrift ();
+   
+    if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) {
+        cl.viewangles[PITCH] += m_pitch.value * mouse_y;
+        if (cl.viewangles[PITCH] > 80)
+            cl.viewangles[PITCH] = 80;
+        if (cl.viewangles[PITCH] < -70)
+            cl.viewangles[PITCH] = -70;
+    } else {
+        if ((in_strafe.state & 1) && noclip_anglehack)
+            cmd->upmove -= m_forward.value * mouse_y;
+        else
+            cmd->forwardmove -= m_forward.value * mouse_y;
+    }
+    mouse_x = mouse_y = 0.0;
+}
+
+/*
+================
+Sys_ConsoleInput
+================
+*/
+char *Sys_ConsoleInput (void)
+{
+    return 0;
+}
diff --git a/apps/plugins/sdl/progs/quake/vid_svgalib.c b/apps/plugins/sdl/progs/quake/vid_svgalib.c
new file mode 100644
index 0000000..e0fea3a
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vid_svgalib.c
@@ -0,0 +1,1003 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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 <termios.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/vt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include <asm/io.h>
+
+#include "vga.h"
+#include "vgakeyboard.h"
+#include "vgamouse.h"
+
+#include "quakedef.h"
+#include "d_local.h"
+
+#define stringify(m) { #m, m }
+
+unsigned short       d_8to16table[256];
+static byte		*vid_surfcache;
+static int		VID_highhunkmark;
+
+int num_modes;
+vga_modeinfo *modes;
+int current_mode;
+
+int num_shades=32;
+
+struct
+{
+	char *name;
+	int num;
+} mice[] =
+{
+	stringify(MOUSE_MICROSOFT),
+	stringify(MOUSE_MOUSESYSTEMS),
+	stringify(MOUSE_MMSERIES),
+	stringify(MOUSE_LOGITECH),
+	stringify(MOUSE_BUSMOUSE),
+	stringify(MOUSE_PS2),
+};
+
+static unsigned char scantokey[128];
+static byte vid_current_palette[768];
+
+int num_mice = sizeof (mice) / sizeof(mice[0]);
+
+int	d_con_indirect = 0;
+
+int		svgalib_inited=0;
+int		UseMouse = 1;
+int		UseDisplay = 1;
+int		UseKeyboard = 1;
+
+int		mouserate = MOUSE_DEFAULTSAMPLERATE;
+
+cvar_t		vid_mode = {"vid_mode","5",false};
+cvar_t		vid_redrawfull = {"vid_redrawfull","0",false};
+cvar_t		vid_waitforrefresh = {"vid_waitforrefresh","0",true};
+ 
+char	*framebuffer_ptr;
+
+cvar_t  mouse_button_commands[3] =
+{
+    {"mouse1","+attack"},
+    {"mouse2","+strafe"},
+    {"mouse3","+forward"},
+};
+
+int     mouse_buttons;
+int     mouse_buttonstate;
+int     mouse_oldbuttonstate;
+float   mouse_x, mouse_y;
+float	old_mouse_x, old_mouse_y;
+int		mx, my;
+
+cvar_t	m_filter = {"m_filter","0"};
+
+static byte     backingbuf[48*24];
+
+int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar;
+byte	*VGA_pagebase;
+
+void VGA_UpdatePlanarScreen (void *srcbuffer);
+
+void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
+{
+	int i, j, k, plane, reps, repshift, offset, vidpage, off;
+
+	if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return;
+
+	if (vid.aspect > 1.5)
+	{
+		reps = 2;
+		repshift = 1;
+	} else {
+		reps = 1;
+		repshift = 0;
+	}
+
+	vidpage = 0;
+	vga_setpage(0);
+
+	if (VGA_planar)
+	{
+		for (plane=0 ; plane<4 ; plane++)
+		{
+		// select the correct plane for reading and writing
+			outb(0x02, 0x3C4);
+			outb(1 << plane, 0x3C5);
+			outb(4, 0x3CE);
+			outb(plane, 0x3CF);
+
+			for (i=0 ; i<(height << repshift) ; i += reps)
+			{
+				for (k=0 ; k<reps ; k++)
+				{
+					for (j=0 ; j<(width >> 2) ; j++)
+					{
+						backingbuf[(i + k) * 24 + (j << 2) + plane] =
+								vid.direct[(y + i + k) * VGA_rowbytes +
+								(x >> 2) + j];
+						vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
+								pbitmap[(i >> repshift) * 24 +
+								(j << 2) + plane];
+					}
+				}
+			}
+		}
+	} else {
+		for (i=0 ; i<(height << repshift) ; i += reps)
+		{
+			for (j=0 ; j<reps ; j++)
+			{
+				offset = x + ((y << repshift) + i + j) * vid.rowbytes;
+				off = offset % 0x10000;
+				if ((offset / 0x10000) != vidpage) {
+					vidpage=offset / 0x10000;
+					vga_setpage(vidpage);
+				}
+				memcpy (&backingbuf[(i + j) * 24],
+						vid.direct + off, width);
+				memcpy (vid.direct + off,
+						&pbitmap[(i >> repshift)*width], width);
+			}
+		}
+	}
+}
+
+void D_EndDirectRect (int x, int y, int width, int height)
+{
+	int i, j, k, plane, reps, repshift, offset, vidpage, off;
+
+	if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return;
+
+	if (vid.aspect > 1.5)
+	{
+		reps = 2;
+		repshift = 1;
+	} else {
+		reps = 1;
+		repshift = 0;
+	}
+
+	vidpage = 0;
+	vga_setpage(0);
+
+	if (VGA_planar)
+	{
+		for (plane=0 ; plane<4 ; plane++)
+		{
+		// select the correct plane for writing
+			outb(2, 0x3C4);
+			outb(1 << plane, 0x3C5);
+			outb(4, 0x3CE);
+			outb(plane, 0x3CF);
+
+			for (i=0 ; i<(height << repshift) ; i += reps)
+			{
+				for (k=0 ; k<reps ; k++)
+				{
+					for (j=0 ; j<(width >> 2) ; j++)
+					{
+						vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
+								backingbuf[(i + k) * 24 + (j << 2) + plane];
+					}
+				}
+			}
+		}
+	} else {
+		for (i=0 ; i<(height << repshift) ; i += reps)
+		{
+			for (j=0 ; j<reps ; j++)
+			{
+				offset = x + ((y << repshift) + i + j) * vid.rowbytes;
+				off = offset % 0x10000;
+				if ((offset / 0x10000) != vidpage) {
+					vidpage=offset / 0x10000;
+					vga_setpage(vidpage);
+				}
+				memcpy (vid.direct + off, 
+						&backingbuf[(i +j)*24],
+						width);
+			}
+		}
+	}
+}
+
+/*
+=================
+VID_Gamma_f
+
+Keybinding command
+=================
+*/
+void VID_Gamma_f (void)
+{
+	float	gamma, f, inf;
+	unsigned char	palette[768];
+	int		i;
+
+	if (Cmd_Argc () == 2)
+	{
+		gamma = Q_atof (Cmd_Argv(1));
+
+		for (i=0 ; i<768 ; i++)
+		{
+			f = pow ( (host_basepal[i]+1)/256.0 , gamma );
+			inf = f*255 + 0.5;
+			if (inf < 0)
+				inf = 0;
+			if (inf > 255)
+				inf = 255;
+			palette[i] = inf;
+		}
+
+		VID_SetPalette (palette);
+
+		vid.recalc_refdef = 1;				// force a surface cache flush
+	}
+}
+
+void VID_DescribeMode_f (void)
+{
+	int modenum;
+	
+	modenum = Q_atoi (Cmd_Argv(1));
+	if ((modenum >= num_modes) || (modenum < 0 ) || !modes[modenum].width)
+		Con_Printf("Invalid video mode: %d!\n",modenum);
+	Con_Printf("%d: %d x %d - ",modenum,modes[modenum].width,modes[modenum].height);
+	if (modes[modenum].bytesperpixel == 0)
+		Con_Printf("ModeX\n");
+	else
+		Con_Printf("%d bpp\n", modes[modenum].bytesperpixel<<3);
+}
+
+void VID_DescribeModes_f (void)
+{
+	int i;
+	
+	for (i=0;i<num_modes;i++)
+		if (modes[i].width) {
+			Con_Printf("%d: %d x %d - ", i, modes[i].width,modes[i].height);
+			if (modes[i].bytesperpixel == 0)
+				Con_Printf("ModeX\n");
+			else
+				Con_Printf("%d bpp\n", modes[i].bytesperpixel<<3);
+		}
+}
+
+/*
+================
+VID_NumModes
+================
+*/
+int VID_NumModes ()
+{
+	int i,i1=0;
+	
+	for (i=0;i<num_modes;i++)
+		i1+=(modes[i].width?1:0);
+	return (i1);
+}
+
+void VID_NumModes_f (void)
+{
+	Con_Printf("%d modes\n",VID_NumModes());
+}
+
+void VID_Debug_f (void)
+{
+	Con_Printf("mode: %d\n",current_mode);
+	Con_Printf("height x width: %d x %d\n",vid.height,vid.width);
+	Con_Printf("bpp: %d\n",modes[current_mode].bytesperpixel*8);
+	Con_Printf("vid.aspect: %f\n",vid.aspect);
+}
+
+
+
+void VID_InitModes(void)
+{
+
+	int i;
+
+// get complete information on all modes
+
+	num_modes = vga_lastmodenumber()+1;
+	modes = Z_Malloc(num_modes * sizeof(vga_modeinfo));
+	for (i=0 ; i<num_modes ; i++)
+	{
+		if (vga_hasmode(i))
+			Q_memcpy(&modes[i], vga_getmodeinfo(i), sizeof (vga_modeinfo));
+		else
+			modes[i].width = 0; // means not available
+	}
+
+// filter for modes i don't support
+
+	for (i=0 ; i<num_modes ; i++)
+	{
+		if (modes[i].bytesperpixel != 1 && modes[i].colors != 256) 
+			modes[i].width = 0;
+	}
+
+}
+
+int get_mode(char *name, int width, int height, int depth)
+{
+
+	int i;
+	int ok, match;
+
+	match = (!!width) + (!!height)*2 + (!!depth)*4;
+
+	if (name)
+	{
+		i = vga_getmodenumber(name);
+		if (!modes[i].width)
+		{
+			Sys_Printf("Mode [%s] not supported\n", name);
+			i = G320x200x256;
+		}
+	}
+	else
+	{
+		for (i=0 ; i<num_modes ; i++)
+			if (modes[i].width)
+			{
+				ok = (modes[i].width == width)
+					+ (modes[i].height == height)*2
+					+ (modes[i].bytesperpixel == depth/8)*4;
+				if ((ok & match) == ok)
+					break;
+			}
+		if (i==num_modes)
+		{
+			Sys_Printf("Mode %dx%d (%d bits) not supported\n",
+				width, height, depth);
+			i = G320x200x256;
+		}
+	}
+
+	return i;
+
+}
+
+int matchmouse(int mouse, char *name)
+{
+	int i;
+	for (i=0 ; i<num_mice ; i++)
+		if (!strcmp(mice[i].name, name))
+			return i;
+	return mouse;
+}
+
+#if 0
+
+void vtswitch(int newconsole)
+{
+
+	int fd;
+	struct vt_stat x;
+
+// switch consoles and wait until reactivated
+	fd = open("/dev/console", O_RDONLY);
+	ioctl(fd, VT_GETSTATE, &x);
+	ioctl(fd, VT_ACTIVATE, newconsole);
+	ioctl(fd, VT_WAITACTIVE, x.v_active);
+	close(fd);
+
+}
+
+#endif
+
+void keyhandler(int scancode, int state)
+{
+	
+	int sc;
+
+	sc = scancode & 0x7f;
+//	Con_Printf("scancode=%x (%d%s)\n", scancode, sc, scancode&0x80?"+128":"");
+	Key_Event(scantokey[sc], state == KEY_EVENTPRESS);
+
+}
+
+void VID_Shutdown(void)
+{
+
+	if (!svgalib_inited) return;
+
+//	printf("shutdown graphics called\n");
+	if (UseKeyboard)
+		keyboard_close();
+	if (UseDisplay)
+		vga_setmode(TEXT);
+//	printf("shutdown graphics finished\n");
+
+	svgalib_inited = 0;
+
+}
+
+void VID_ShiftPalette(unsigned char *p)
+{
+	VID_SetPalette(p);
+}
+
+void VID_SetPalette(byte *palette)
+{
+
+	static int tmppal[256*3];
+	int *tp;
+	int i;
+
+	if (!svgalib_inited)
+		return;
+
+	memcpy(vid_current_palette, palette, sizeof(vid_current_palette));
+
+	if (vga_getcolors() == 256)
+	{
+
+		tp = tmppal;
+		for (i=256*3 ; i ; i--)
+			*(tp++) = *(palette++) >> 2;
+
+		if (UseDisplay && vga_oktowrite())
+			vga_setpalvec(0, 256, tmppal);
+
+	}
+}
+
+int VID_SetMode (int modenum, unsigned char *palette)
+{
+	int bsize, zsize, tsize;
+
+	if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width)
+	{
+		Cvar_SetValue ("vid_mode", (float)current_mode);
+		
+		Con_Printf("No such video mode: %d\n",modenum);
+		
+		return 0;
+	}
+
+	Cvar_SetValue ("vid_mode", (float)modenum);
+	
+	current_mode=modenum;
+
+	vid.width = modes[current_mode].width;
+	vid.height = modes[current_mode].height;
+
+	VGA_width = modes[current_mode].width;
+	VGA_height = modes[current_mode].height;
+	VGA_planar = modes[current_mode].bytesperpixel == 0;
+	VGA_rowbytes = modes[current_mode].linewidth;
+	vid.rowbytes = modes[current_mode].linewidth;
+	if (VGA_planar) {
+		VGA_bufferrowbytes = modes[current_mode].linewidth * 4;
+		vid.rowbytes = modes[current_mode].linewidth*4;
+	}
+
+	vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
+	vid.colormap = (pixel_t *) host_colormap;
+	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
+	vid.conrowbytes = vid.rowbytes;
+	vid.conwidth = vid.width;
+	vid.conheight = vid.height;
+	vid.numpages = 1;
+	
+	vid.maxwarpwidth = WARP_WIDTH;
+	vid.maxwarpheight = WARP_HEIGHT;
+
+	// alloc zbuffer and surface cache
+	if (d_pzbuffer) {
+		D_FlushCaches();
+		Hunk_FreeToHighMark (VID_highhunkmark);
+		d_pzbuffer = NULL;
+		vid_surfcache = NULL;
+	}
+
+	bsize = vid.rowbytes * vid.height;
+	tsize = D_SurfaceCacheForRes (vid.width, vid.height);
+	zsize = vid.width * vid.height * sizeof(*d_pzbuffer);
+
+	VID_highhunkmark = Hunk_HighMark ();
+
+	d_pzbuffer = Hunk_HighAllocName (bsize+tsize+zsize, "video");
+
+	vid_surfcache = ((byte *)d_pzbuffer) + zsize;
+
+	vid.conbuffer = vid.buffer = (pixel_t *)(((byte *)d_pzbuffer) + zsize + tsize);
+
+	D_InitCaches (vid_surfcache, tsize);
+
+// get goin'
+
+	vga_setmode(current_mode);
+	VID_SetPalette(palette);
+
+	VGA_pagebase = vid.direct = framebuffer_ptr = (char *) vga_getgraphmem();
+//		if (vga_setlinearaddressing()>0)
+//			framebuffer_ptr = (char *) vga_getgraphmem();
+	if (!framebuffer_ptr)
+		Sys_Error("This mode isn't hapnin'\n");
+
+	vga_setpage(0);
+
+	svgalib_inited=1;
+
+	vid.recalc_refdef = 1;				// force a surface cache flush
+
+	return 0;
+}
+
+void VID_Init(unsigned char *palette)
+{
+
+	int i;
+	int w, h, d;
+
+	if (svgalib_inited)
+		return;
+
+//	Cmd_AddCommand ("gamma", VID_Gamma_f);
+
+	if (UseDisplay)
+	{
+		vga_init();
+
+		VID_InitModes();
+
+		Cvar_RegisterVariable (&vid_mode);
+		Cvar_RegisterVariable (&vid_redrawfull);
+		Cvar_RegisterVariable (&vid_waitforrefresh);
+		
+		Cmd_AddCommand("vid_nummodes", VID_NumModes_f);
+		Cmd_AddCommand("vid_describemode", VID_DescribeMode_f);
+		Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f);
+		Cmd_AddCommand("vid_debug", VID_Debug_f);
+
+	// interpret command-line params
+
+		w = h = d = 0;
+		if (getenv("GSVGAMODE"))
+			current_mode = get_mode(getenv("GSVGAMODE"), w, h, d);
+		else if (COM_CheckParm("-mode"))
+			current_mode = get_mode(com_argv[COM_CheckParm("-mode")+1], w, h, d);
+		else if (COM_CheckParm("-w") || COM_CheckParm("-h")
+			|| COM_CheckParm("-d"))
+		{
+			if (COM_CheckParm("-w"))
+				w = Q_atoi(com_argv[COM_CheckParm("-w")+1]);
+			if (COM_CheckParm("-h"))
+				h = Q_atoi(com_argv[COM_CheckParm("-h")+1]);
+			if (COM_CheckParm("-d"))
+				d = Q_atoi(com_argv[COM_CheckParm("-d")+1]);
+			current_mode = get_mode(0, w, h, d);
+		}
+		else
+			current_mode = G320x200x256;
+
+	// set vid parameters
+		VID_SetMode(current_mode, palette);
+
+		VID_SetPalette(palette);
+
+		// we do want to run in the background when switched away
+		vga_runinbackground(1);	
+	}
+
+	if (COM_CheckParm("-nokbd")) UseKeyboard = 0;
+
+	if (UseKeyboard)
+	{
+		for (i=0 ; i<128 ; i++)
+			scantokey[i] = ' ';
+
+		scantokey[42] = K_SHIFT;
+		scantokey[54] = K_SHIFT;
+		scantokey[72] = K_UPARROW;
+		scantokey[103] = K_UPARROW;
+		scantokey[80] = K_DOWNARROW;
+		scantokey[108] = K_DOWNARROW;
+		scantokey[75] = K_LEFTARROW;
+		scantokey[105] = K_LEFTARROW;
+		scantokey[77] = K_RIGHTARROW;
+		scantokey[106] = K_RIGHTARROW;
+		scantokey[29] = K_CTRL;
+		scantokey[97] = K_CTRL;
+		scantokey[56] = K_ALT;
+		scantokey[100] = K_ALT;
+//		scantokey[58] = JK_CAPS;
+//		scantokey[69] = JK_NUM_LOCK;
+		scantokey[71] = K_HOME;
+		scantokey[73] = K_PGUP;
+		scantokey[79] = K_END;
+		scantokey[81] = K_PGDN;
+		scantokey[82] = K_INS;
+		scantokey[83] = K_DEL;
+		scantokey[1 ] = K_ESCAPE;
+		scantokey[28] = K_ENTER;
+		scantokey[15] = K_TAB;
+		scantokey[14] = K_BACKSPACE;
+		scantokey[119] = K_PAUSE;
+    	scantokey[57] = ' ';
+
+		scantokey[102] = K_HOME;
+		scantokey[104] = K_PGUP;
+		scantokey[107] = K_END;
+		scantokey[109] = K_PGDN;
+		scantokey[110] = K_INS;
+		scantokey[111] = K_DEL;
+
+		scantokey[2] = '1';
+		scantokey[3] = '2';
+		scantokey[4] = '3';
+		scantokey[5] = '4';
+		scantokey[6] = '5';
+		scantokey[7] = '6';
+		scantokey[8] = '7';
+		scantokey[9] = '8';
+		scantokey[10] = '9';
+		scantokey[11] = '0';
+		scantokey[12] = '-';
+		scantokey[13] = '=';
+		scantokey[41] = '`';
+		scantokey[26] = '[';
+		scantokey[27] = ']';
+		scantokey[39] = ';';
+		scantokey[40] = '\'';
+		scantokey[51] = ',';
+		scantokey[52] = '.';
+		scantokey[53] = '/';
+		scantokey[43] = '\\';
+
+		scantokey[59] = K_F1;
+		scantokey[60] = K_F2;
+		scantokey[61] = K_F3;
+		scantokey[62] = K_F4;
+		scantokey[63] = K_F5;
+		scantokey[64] = K_F6;
+		scantokey[65] = K_F7;
+		scantokey[66] = K_F8;
+		scantokey[67] = K_F9;
+		scantokey[68] = K_F10;
+		scantokey[87] = K_F11;
+		scantokey[88] = K_F12;
+		scantokey[30] = 'a';
+		scantokey[48] = 'b';
+		scantokey[46] = 'c';
+        scantokey[32] = 'd';       
+        scantokey[18] = 'e';       
+        scantokey[33] = 'f';       
+        scantokey[34] = 'g';       
+        scantokey[35] = 'h';       
+        scantokey[23] = 'i';       
+        scantokey[36] = 'j';       
+        scantokey[37] = 'k';       
+        scantokey[38] = 'l';       
+        scantokey[50] = 'm';       
+        scantokey[49] = 'n';       
+        scantokey[24] = 'o';       
+        scantokey[25] = 'p';       
+        scantokey[16] = 'q';       
+        scantokey[19] = 'r';       
+        scantokey[31] = 's';       
+        scantokey[20] = 't';       
+        scantokey[22] = 'u';       
+        scantokey[47] = 'v';       
+        scantokey[17] = 'w';       
+        scantokey[45] = 'x';       
+        scantokey[21] = 'y';       
+        scantokey[44] = 'z';       
+
+		if (keyboard_init())
+			Sys_Error("keyboard_init() failed");
+		keyboard_seteventhandler(keyhandler);
+	}
+
+}
+
+void VID_Update(vrect_t *rects)
+{
+	if (!svgalib_inited)
+		return;
+
+	if (!vga_oktowrite())
+		return; // can't update screen if it's not active
+
+	if (vid_waitforrefresh.value)
+		vga_waitretrace();
+
+	if (VGA_planar)
+		VGA_UpdatePlanarScreen (vid.buffer);
+
+	else if (vid_redrawfull.value) {
+		int total = vid.rowbytes * vid.height;
+		int offset;
+
+		for (offset=0;offset<total;offset+=0x10000) {
+			vga_setpage(offset/0x10000);
+			memcpy(framebuffer_ptr,
+					vid.buffer + offset,
+					((total-offset>0x10000)?0x10000:(total-offset)));
+		}
+	} else {
+		int ycount;
+		int offset;
+		int vidpage=0;
+
+		vga_setpage(0);
+
+		while (rects)
+		{
+			ycount = rects->height;
+			offset = rects->y * vid.rowbytes + rects->x;
+			while (ycount--)
+			{
+				register int i = offset % 0x10000;
+	
+				if ((offset / 0x10000) != vidpage) {
+					vidpage=offset / 0x10000;
+					vga_setpage(vidpage);
+				}
+				if (rects->width + i > 0x10000) {
+					memcpy(framebuffer_ptr + i, 
+							vid.buffer + offset, 
+							0x10000 - i);
+					vga_setpage(++vidpage);
+					memcpy(framebuffer_ptr,
+							vid.buffer + offset + 0x10000 - i, 
+							rects->width - 0x10000 + i);
+				} else
+					memcpy(framebuffer_ptr + i, 
+							vid.buffer + offset, 
+							rects->width);
+				offset += vid.rowbytes;
+			}
+	
+			rects = rects->pnext;
+		}
+	}
+	
+	if (vid_mode.value != current_mode)
+		VID_SetMode ((int)vid_mode.value, vid_current_palette);
+}
+
+static int dither;
+
+void VID_DitherOn(void)
+{
+    if (dither == 0)
+    {
+//		R_ViewChanged (&vrect, sb_lines, vid.aspect);
+        dither = 1;
+    }
+}
+
+void VID_DitherOff(void)
+{
+    if (dither)
+    {
+//		R_ViewChanged (&vrect, sb_lines, vid.aspect);
+        dither = 0;
+    }
+}
+
+void Sys_SendKeyEvents(void)
+{
+	if (!svgalib_inited)
+		return;
+
+	if (UseKeyboard)
+		while (keyboard_update());
+}
+
+void Force_CenterView_f (void)
+{
+	cl.viewangles[PITCH] = 0;
+}
+
+
+void mousehandler(int buttonstate, int dx, int dy)
+{
+	mouse_buttonstate = buttonstate;
+	mx += dx;
+	my += dy;
+}
+
+void IN_Init(void)
+{
+
+	int mtype;
+	char *mousedev;
+	int mouserate;
+
+	if (UseMouse)
+	{
+
+		Cvar_RegisterVariable (&mouse_button_commands[0]);
+		Cvar_RegisterVariable (&mouse_button_commands[1]);
+		Cvar_RegisterVariable (&mouse_button_commands[2]);
+		Cvar_RegisterVariable (&m_filter);
+		Cmd_AddCommand ("force_centerview", Force_CenterView_f);
+
+		mouse_buttons = 3;
+
+		mtype = vga_getmousetype();
+
+		mousedev = "/dev/mouse";
+		if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV");
+		if (COM_CheckParm("-mdev"))
+			mousedev = com_argv[COM_CheckParm("-mdev")+1];
+
+		mouserate = 1200;
+		if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE"));
+		if (COM_CheckParm("-mrate"))
+			mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]);
+
+//		printf("Mouse: dev=%s,type=%s,speed=%d\n",
+//			mousedev, mice[mtype].name, mouserate);
+		if (mouse_init(mousedev, mtype, mouserate))
+		{
+			Con_Printf("No mouse found\n");
+			UseMouse = 0;
+		}
+		else
+			mouse_seteventhandler(mousehandler);
+
+	}
+
+}
+
+void IN_Shutdown(void)
+{
+	if (UseMouse)
+		mouse_close();
+}
+
+/*
+===========
+IN_Commands
+===========
+*/
+void IN_Commands (void)
+{
+	if (UseMouse && cls.state != ca_dedicated)
+	{
+		// poll mouse values
+		while (mouse_update())
+			;
+
+		// perform button actions
+		if ((mouse_buttonstate & MOUSE_LEFTBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_LEFTBUTTON))
+			Key_Event (K_MOUSE1, true);
+		else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_LEFTBUTTON))
+			Key_Event (K_MOUSE1, false);
+
+		if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON))
+			Key_Event (K_MOUSE2, true);
+		else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON))
+			Key_Event (K_MOUSE2, false);
+
+		if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) &&
+			!(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON))
+			Key_Event (K_MOUSE3, true);
+		else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) &&
+			(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON))
+			Key_Event (K_MOUSE3, false);
+
+		mouse_oldbuttonstate = mouse_buttonstate;
+	}
+}
+
+/*
+===========
+IN_Move
+===========
+*/
+void IN_MouseMove (usercmd_t *cmd)
+{
+	if (!UseMouse)
+		return;
+
+	// poll mouse values
+	while (mouse_update())
+		;
+
+	if (m_filter.value)
+	{
+		mouse_x = (mx + old_mouse_x) * 0.5;
+		mouse_y = (my + old_mouse_y) * 0.5;
+	}
+	else
+	{
+		mouse_x = mx;
+		mouse_y = my;
+	}
+	old_mouse_x = mx;
+	old_mouse_y = my;
+	mx = my = 0; // clear for next update
+
+	mouse_x *= sensitivity.value;
+	mouse_y *= sensitivity.value;
+
+// add mouse X/Y movement to cmd
+	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
+		cmd->sidemove += m_side.value * mouse_x;
+	else
+		cl.viewangles[YAW] -= m_yaw.value * mouse_x;
+	
+	if (in_mlook.state & 1)
+		V_StopPitchDrift ();
+		
+	if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
+	{
+		cl.viewangles[PITCH] += m_pitch.value * mouse_y;
+		if (cl.viewangles[PITCH] > 80)
+			cl.viewangles[PITCH] = 80;
+		if (cl.viewangles[PITCH] < -70)
+			cl.viewangles[PITCH] = -70;
+	}
+	else
+	{
+		if ((in_strafe.state & 1) && noclip_anglehack)
+			cmd->upmove -= m_forward.value * mouse_y;
+		else
+			cmd->forwardmove -= m_forward.value * mouse_y;
+	}
+}
+
+void IN_Move (usercmd_t *cmd)
+{
+	IN_MouseMove(cmd);
+}
+
+
+/*
+================
+VID_ModeInfo
+================
+*/
+char *VID_ModeInfo (int modenum)
+{
+	static char	*badmodestr = "Bad mode number";
+	static char modestr[40];
+
+	if (modenum == 0)
+	{
+		sprintf (modestr, "%d x %d, %d bpp",
+				 vid.width, vid.height, modes[current_mode].bytesperpixel*8);
+		return (modestr);
+	}
+	else
+	{
+		return (badmodestr);
+	}
+}
+
diff --git a/apps/plugins/sdl/progs/quake/vid_vga.c b/apps/plugins/sdl/progs/quake/vid_vga.c
new file mode 100644
index 0000000..6fbec9d
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/vid_vga.c
@@ -0,0 +1,478 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+//
+// vid_vga.c: VGA-specific DOS video stuff
+//
+
+// TODO: proper handling of page-swap failure
+
+#include <dos.h>
+
+#include "quakedef.h"
+#include "d_local.h"
+#include "dosisms.h"
+#include "vid_dos.h"
+#include <dpmi.h>
+
+extern regs_t regs;
+
+int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes;
+byte	*VGA_pagebase;
+vmode_t	*VGA_pcurmode;
+
+static int		VGA_planar;
+static int		VGA_numpages;
+static int		VGA_buffersize;
+
+void	*vid_surfcache;
+int		vid_surfcachesize;
+
+int		VGA_highhunkmark;
+
+#include "vgamodes.h"
+
+#define NUMVIDMODES		(sizeof(vgavidmodes) / sizeof(vgavidmodes[0]))
+
+void VGA_UpdatePlanarScreen (void *srcbuffer);
+
+static byte	backingbuf[48*24];
+
+/*
+================
+VGA_BeginDirectRect
+================
+*/
+void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x,
+	int y, byte *pbitmap, int width, int height)
+{
+	int		i, j, k, plane, reps, repshift;
+
+	if (!lvid->direct)
+		return;
+
+	if (lvid->aspect > 1.5)
+	{
+		reps = 2;
+		repshift = 1;
+	}
+	else
+	{
+		reps = 1;
+		repshift = 0;
+	}
+
+	if (pcurrentmode->planar)
+	{
+		for (plane=0 ; plane<4 ; plane++)
+		{
+		// select the correct plane for reading and writing
+			outportb (SC_INDEX, MAP_MASK);
+			outportb (SC_DATA, 1 << plane);
+			outportb (GC_INDEX, READ_MAP);
+			outportb (GC_DATA, plane);
+
+			for (i=0 ; i<(height << repshift) ; i += reps)
+			{
+				for (k=0 ; k<reps ; k++)
+				{
+					for (j=0 ; j<(width >> 2) ; j++)
+					{
+						backingbuf[(i + k) * 24 + (j << 2) + plane] =
+								lvid->direct[(y + i + k) * VGA_rowbytes +
+								(x >> 2) + j];
+						lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
+								pbitmap[(i >> repshift) * 24 +
+								(j << 2) + plane];
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		for (i=0 ; i<(height << repshift) ; i += reps)
+		{
+			for (j=0 ; j<reps ; j++)
+			{
+				memcpy (&backingbuf[(i + j) * 24],
+						lvid->direct + x + ((y << repshift) + i + j) *
+						 VGA_rowbytes,
+						width);
+				memcpy (lvid->direct + x + ((y << repshift) + i + j) *
+						 VGA_rowbytes,
+						&pbitmap[(i >> repshift) * width],
+						width);
+			}
+		}
+	}
+}
+
+
+/*
+================
+VGA_EndDirectRect
+================
+*/
+void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x,
+	int y, int width, int height)
+{
+	int		i, j, k, plane, reps, repshift;
+
+	if (!lvid->direct)
+		return;
+
+	if (lvid->aspect > 1.5)
+	{
+		reps = 2;
+		repshift = 1;
+	}
+	else
+	{
+		reps = 1;
+		repshift = 0;
+	}
+
+	if (pcurrentmode->planar)
+	{
+		for (plane=0 ; plane<4 ; plane++)
+		{
+		// select the correct plane for writing
+			outportb (SC_INDEX, MAP_MASK);
+			outportb (SC_DATA, 1 << plane);
+
+			for (i=0 ; i<(height << repshift) ; i += reps)
+			{
+				for (k=0 ; k<reps ; k++)
+				{
+					for (j=0 ; j<(width >> 2) ; j++)
+					{
+						lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
+								backingbuf[(i + k) * 24 + (j << 2) + plane];
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		for (i=0 ; i<(height << repshift) ; i += reps)
+		{
+			for (j=0 ; j<reps ; j++)
+			{
+				memcpy (lvid->direct + x + ((y << repshift) + i + j) *
+						 VGA_rowbytes,
+						&backingbuf[(i + j) * 24],
+						width);
+			}
+		}
+	}
+}
+
+
+/*
+================
+VGA_Init
+================
+*/
+void VGA_Init (void)
+{
+	int		i;
+
+// link together all the VGA modes
+	for (i=0 ; i<(NUMVIDMODES - 1) ; i++)
+	{
+		vgavidmodes[i].pnext = &vgavidmodes[i+1];
+	}
+
+// add the VGA modes at the start of the mode list
+	vgavidmodes[NUMVIDMODES-1].pnext = pvidmodes;
+	pvidmodes = &vgavidmodes[0];
+
+	numvidmodes += NUMVIDMODES;
+}
+
+
+/*
+================
+VGA_WaitVsync
+================
+*/
+void VGA_WaitVsync (void)
+{
+	while ((inportb (0x3DA) & 0x08) == 0)
+		;
+}
+
+
+/*
+================
+VGA_ClearVideoMem
+================
+*/
+void VGA_ClearVideoMem (int planar)
+{
+
+	if (planar)
+	{
+	// enable all planes for writing
+		outportb (SC_INDEX, MAP_MASK);
+		outportb (SC_DATA, 0x0F);
+	}
+
+	Q_memset (VGA_pagebase, 0, VGA_rowbytes * VGA_height);
+}
+
+/*
+================
+VGA_FreeAndAllocVidbuffer
+================
+*/
+qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer)
+{
+	int		tsize, tbuffersize;
+
+	if (allocnewbuffer)
+	{
+	// alloc an extra line in case we want to wrap, and allocate the z-buffer
+		tbuffersize = (lvid->rowbytes * (lvid->height + 1)) +
+				(lvid->width * lvid->height * sizeof (*d_pzbuffer));
+	}
+	else
+	{
+	// just allocate the z-buffer
+		tbuffersize = lvid->width * lvid->height * sizeof (*d_pzbuffer);
+	}
+
+	tsize = D_SurfaceCacheForRes (lvid->width, lvid->height);
+
+	tbuffersize += tsize;
+
+// see if there's enough memory, allowing for the normal mode 0x13 pixel,
+// z, and surface buffers
+	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
+		 0x10000 * 3) < minimum_memory)
+	{
+		Con_Printf ("Not enough memory for video mode\n");
+		VGA_pcurmode = NULL;	// so no further accesses to the buffer are
+								//  attempted, particularly when clearing
+		return false;		// not enough memory for mode
+	}
+
+	VGA_buffersize = tbuffersize;
+	vid_surfcachesize = tsize;
+
+	if (d_pzbuffer)
+	{
+		D_FlushCaches ();
+		Hunk_FreeToHighMark (VGA_highhunkmark);
+		d_pzbuffer = NULL;
+	}
+
+	VGA_highhunkmark = Hunk_HighMark ();
+
+	d_pzbuffer = Hunk_HighAllocName (VGA_buffersize, "video");
+
+	vid_surfcache = (byte *)d_pzbuffer
+		+ lvid->width * lvid->height * sizeof (*d_pzbuffer);
+	
+	if (allocnewbuffer)
+	{
+		lvid->buffer = (void *)( (byte *)vid_surfcache + vid_surfcachesize);
+		lvid->conbuffer = lvid->buffer;
+	}
+
+	return true;
+}
+
+
+/*
+================
+VGA_CheckAdequateMem
+================
+*/
+qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes,
+	int allocnewbuffer)
+{
+	int		tbuffersize;
+
+	tbuffersize = width * height * sizeof (*d_pzbuffer);
+
+	if (allocnewbuffer)
+	{
+	// alloc an extra line in case we want to wrap, and allocate the z-buffer
+		tbuffersize += (rowbytes * (height + 1));
+	}
+
+	tbuffersize += D_SurfaceCacheForRes (width, height);
+
+// see if there's enough memory, allowing for the normal mode 0x13 pixel,
+// z, and surface buffers
+	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
+		 0x10000 * 3) < minimum_memory)
+	{
+		return false;		// not enough memory for mode
+	}
+
+	return true;
+}
+
+
+/*
+================
+VGA_InitMode
+================
+*/
+int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode)
+{
+	vextra_t		*pextra;
+
+	pextra = pcurrentmode->pextradata;
+
+	if (!VGA_FreeAndAllocVidbuffer (lvid, pextra->vidbuffer))
+		return -1;	// memory alloc failed
+
+	if (VGA_pcurmode)
+		VGA_ClearVideoMem (VGA_pcurmode->planar);
+
+// mode 0x13 is the base for all the Mode X-class mode sets
+	regs.h.ah = 0;
+	regs.h.al = 0x13;
+	dos_int86(0x10);
+
+	VGA_pagebase = (void *)real2ptr(0xa0000);
+	lvid->direct = (pixel_t *)VGA_pagebase;
+
+// set additional registers as needed
+	VideoRegisterSet (pextra->pregset);
+
+	VGA_numpages = 1;
+	lvid->numpages = VGA_numpages;
+
+	VGA_width = (lvid->width + 0x1F) & ~0x1F;
+	VGA_height = lvid->height;
+	VGA_planar = pcurrentmode->planar;
+	if (VGA_planar)
+		VGA_rowbytes = lvid->rowbytes / 4;
+	else
+		VGA_rowbytes = lvid->rowbytes;
+	VGA_bufferrowbytes = lvid->rowbytes;
+	lvid->colormap = host_colormap;
+	lvid->fullbright = 256 - LittleLong (*((int *)lvid->colormap + 2048));
+
+	lvid->maxwarpwidth = WARP_WIDTH;
+	lvid->maxwarpheight = WARP_HEIGHT;
+
+	lvid->conbuffer = lvid->buffer;
+	lvid->conrowbytes = lvid->rowbytes;
+	lvid->conwidth = lvid->width;
+	lvid->conheight = lvid->height;
+
+	VGA_pcurmode = pcurrentmode;
+
+	VGA_ClearVideoMem (pcurrentmode->planar);
+
+	if (_vid_wait_override.value)
+	{
+		Cvar_SetValue ("vid_wait", (float)VID_WAIT_VSYNC);
+	}
+	else
+	{
+		Cvar_SetValue ("vid_wait", (float)VID_WAIT_NONE);
+	}
+
+	D_InitCaches (vid_surfcache, vid_surfcachesize);
+
+	return 1;
+}
+
+
+/*
+================
+VGA_SetPalette
+================
+*/
+void VGA_SetPalette(viddef_t *lvid, vmode_t *pcurrentmode, unsigned char *pal)
+{
+	int shiftcomponents=2;
+	int i;
+
+	UNUSED(lvid);
+	UNUSED(pcurrentmode);
+
+	dos_outportb(0x3c8, 0);
+	for (i=0 ; i<768 ; i++)
+		outportb(0x3c9, pal[i]>>shiftcomponents);
+}
+
+
+/*
+================
+VGA_SwapBuffersCopy
+================
+*/
+void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode,
+	vrect_t *rects)
+{
+
+	UNUSED(pcurrentmode);
+
+// TODO: can write a dword at a time
+// TODO: put in ASM
+// TODO: copy only specified rectangles
+	if (VGA_planar)
+	{
+
+	// TODO: copy only specified rectangles
+
+		VGA_UpdatePlanarScreen (lvid->buffer);
+	}
+	else
+	{
+		while (rects)
+		{
+			VGA_UpdateLinearScreen (
+					lvid->buffer + rects->x + (rects->y * lvid->rowbytes),
+		 			VGA_pagebase + rects->x + (rects->y * VGA_rowbytes),
+					rects->width,
+					rects->height,
+					lvid->rowbytes,
+					VGA_rowbytes);
+
+			rects = rects->pnext;
+		}
+	}
+}
+
+
+/*
+================
+VGA_SwapBuffers
+================
+*/
+void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects)
+{
+	UNUSED(lvid);
+
+	if (vid_wait.value == VID_WAIT_VSYNC)
+		VGA_WaitVsync ();
+
+	VGA_SwapBuffersCopy (lvid, pcurrentmode, rects);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/view.c b/apps/plugins/sdl/progs/quake/view.c
new file mode 100644
index 0000000..f621c3c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/view.c
@@ -0,0 +1,1113 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// view.c -- player eye positioning
+
+#include "quakedef.h"
+#include "r_local.h"
+
+/*
+
+The view is allowed to move slightly from it's true position for bobbing,
+but if it exceeds 8 pixels linear distance (spherical, not box), the list of
+entities sent from the server may not include everything in the pvs, especially
+when crossing a water boudnary.
+
+*/
+
+cvar_t		lcd_x = {"lcd_x","0"};
+cvar_t		lcd_yaw = {"lcd_yaw","0"};
+
+cvar_t	scr_ofsx = {"scr_ofsx","0", false};
+cvar_t	scr_ofsy = {"scr_ofsy","0", false};
+cvar_t	scr_ofsz = {"scr_ofsz","0", false};
+
+cvar_t	cl_rollspeed = {"cl_rollspeed", "200"};
+cvar_t	cl_rollangle = {"cl_rollangle", "2.0"};
+
+cvar_t	cl_bob = {"cl_bob","0.02", false};
+cvar_t	cl_bobcycle = {"cl_bobcycle","0.6", false};
+cvar_t	cl_bobup = {"cl_bobup","0.5", false};
+
+cvar_t	v_kicktime = {"v_kicktime", "0.5", false};
+cvar_t	v_kickroll = {"v_kickroll", "0.6", false};
+cvar_t	v_kickpitch = {"v_kickpitch", "0.6", false};
+
+cvar_t	v_iyaw_cycle = {"v_iyaw_cycle", "2", false};
+cvar_t	v_iroll_cycle = {"v_iroll_cycle", "0.5", false};
+cvar_t	v_ipitch_cycle = {"v_ipitch_cycle", "1", false};
+cvar_t	v_iyaw_level = {"v_iyaw_level", "0.3", false};
+cvar_t	v_iroll_level = {"v_iroll_level", "0.1", false};
+cvar_t	v_ipitch_level = {"v_ipitch_level", "0.3", false};
+
+cvar_t	v_idlescale = {"v_idlescale", "0", false};
+
+cvar_t	crosshair = {"crosshair", "0", true};
+cvar_t	cl_crossx = {"cl_crossx", "0", false};
+cvar_t	cl_crossy = {"cl_crossy", "0", false};
+
+cvar_t	gl_cshiftpercent = {"gl_cshiftpercent", "100", false};
+
+float	v_dmg_time, v_dmg_roll, v_dmg_pitch;
+
+extern	int			in_forward, in_forward2, in_back;
+
+
+/*
+===============
+V_CalcRoll
+
+Used by view and sv_user
+===============
+*/
+vec3_t	forward, right, up;
+
+float V_CalcRoll (vec3_t angles, vec3_t velocity)
+{
+	float	sign;
+	float	side;
+	float	value;
+	
+	AngleVectors (angles, forward, right, up);
+	side = DotProduct (velocity, right);
+	sign = side < 0 ? -1 : 1;
+	side = fabs(side);
+	
+	value = cl_rollangle.value;
+//	if (cl.inwater)
+//		value *= 6;
+
+	if (side < cl_rollspeed.value)
+		side = side * value / cl_rollspeed.value;
+	else
+		side = value;
+	
+	return side*sign;
+	
+}
+
+
+/*
+===============
+V_CalcBob
+
+===============
+*/
+float V_CalcBob (void)
+{
+	float	bob;
+	float	cycle;
+	
+	cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value;
+	cycle /= cl_bobcycle.value;
+	if (cycle < cl_bobup.value)
+		cycle = M_PI * cycle / cl_bobup.value;
+	else
+		cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
+
+// bob is proportional to velocity in the xy plane
+// (don't count Z, or jumping messes it up)
+
+	bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
+//Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
+	bob = bob*0.3 + bob*0.7*sin(cycle);
+	if (bob > 4)
+		bob = 4;
+	else if (bob < -7)
+		bob = -7;
+	return bob;
+	
+}
+
+
+//=============================================================================
+
+
+cvar_t	v_centermove = {"v_centermove", "0.15", false};
+cvar_t	v_centerspeed = {"v_centerspeed","500"};
+
+
+void V_StartPitchDrift (void)
+{
+#if 1
+	if (cl.laststop == cl.time)
+	{
+		return;		// something else is keeping it from drifting
+	}
+#endif
+	if (cl.nodrift || !cl.pitchvel)
+	{
+		cl.pitchvel = v_centerspeed.value;
+		cl.nodrift = false;
+		cl.driftmove = 0;
+	}
+}
+
+void V_StopPitchDrift (void)
+{
+	cl.laststop = cl.time;
+	cl.nodrift = true;
+	cl.pitchvel = 0;
+}
+
+/*
+===============
+V_DriftPitch
+
+Moves the client pitch angle towards cl.idealpitch sent by the server.
+
+If the user is adjusting pitch manually, either with lookup/lookdown,
+mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
+
+Drifting is enabled when the center view key is hit, mlook is released and
+lookspring is non 0, or when 
+===============
+*/
+void V_DriftPitch (void)
+{
+	float		delta, move;
+
+	if (noclip_anglehack || !cl.onground || cls.demoplayback )
+	{
+		cl.driftmove = 0;
+		cl.pitchvel = 0;
+		return;
+	}
+
+// don't count small mouse motion
+	if (cl.nodrift)
+	{
+		if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
+			cl.driftmove = 0;
+		else
+			cl.driftmove += host_frametime;
+	
+		if ( cl.driftmove > v_centermove.value)
+		{
+			V_StartPitchDrift ();
+		}
+		return;
+	}
+	
+	delta = cl.idealpitch - cl.viewangles[PITCH];
+
+	if (!delta)
+	{
+		cl.pitchvel = 0;
+		return;
+	}
+
+	move = host_frametime * cl.pitchvel;
+	cl.pitchvel += host_frametime * v_centerspeed.value;
+	
+//Con_Printf ("move: %f (%f)\n", move, host_frametime);
+
+	if (delta > 0)
+	{
+		if (move > delta)
+		{
+			cl.pitchvel = 0;
+			move = delta;
+		}
+		cl.viewangles[PITCH] += move;
+	}
+	else if (delta < 0)
+	{
+		if (move > -delta)
+		{
+			cl.pitchvel = 0;
+			move = -delta;
+		}
+		cl.viewangles[PITCH] -= move;
+	}
+}
+
+
+
+
+
+/*
+============================================================================== 
+ 
+						PALETTE FLASHES 
+ 
+============================================================================== 
+*/ 
+ 
+ 
+cshift_t	cshift_empty = { {130,80,50}, 0 };
+cshift_t	cshift_water = { {130,80,50}, 128 };
+cshift_t	cshift_slime = { {0,25,5}, 150 };
+cshift_t	cshift_lava = { {255,80,0}, 150 };
+
+cvar_t		v_gamma = {"gamma", "1", true};
+
+byte		gammatable[256];	// palette is sent through this
+
+#ifdef	GLQUAKE
+byte		ramps[3][256];
+float		v_blend[4];		// rgba 0.0 - 1.0
+#endif	// GLQUAKE
+
+void BuildGammaTable (float g)
+{
+	int		i, inf;
+	
+	if (g == 1.0)
+	{
+		for (i=0 ; i<256 ; i++)
+			gammatable[i] = i;
+		return;
+	}
+	
+	for (i=0 ; i<256 ; i++)
+	{
+		inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
+		if (inf < 0)
+			inf = 0;
+		if (inf > 255)
+			inf = 255;
+		gammatable[i] = inf;
+	}
+}
+
+/*
+=================
+V_CheckGamma
+=================
+*/
+qboolean V_CheckGamma (void)
+{
+	static float oldgammavalue;
+	
+	if (v_gamma.value == oldgammavalue)
+		return false;
+	oldgammavalue = v_gamma.value;
+	
+	BuildGammaTable (v_gamma.value);
+	vid.recalc_refdef = 1;				// force a surface cache flush
+	
+	return true;
+}
+
+
+
+/*
+===============
+V_ParseDamage
+===============
+*/
+void V_ParseDamage (void)
+{
+	int		armor, blood;
+	vec3_t	from;
+	int		i;
+	vec3_t	forward, right, up;
+	entity_t	*ent;
+	float	side;
+	float	count;
+	
+	armor = MSG_ReadByte ();
+	blood = MSG_ReadByte ();
+	for (i=0 ; i<3 ; i++)
+		from[i] = MSG_ReadCoord ();
+
+	count = blood*0.5 + armor*0.5;
+	if (count < 10)
+		count = 10;
+
+	cl.faceanimtime = cl.time + 0.2;		// but sbar face into pain frame
+
+	cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
+	if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
+		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+	if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
+		cl.cshifts[CSHIFT_DAMAGE].percent = 150;
+
+	if (armor > blood)		
+	{
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
+	}
+	else if (armor)
+	{
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
+	}
+	else
+	{
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
+		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
+	}
+
+//
+// calculate view angle kicks
+//
+	ent = &cl_entities[cl.viewentity];
+	
+	VectorSubtract (from, ent->origin, from);
+	VectorNormalizeNoRet (from);
+	
+	AngleVectors (ent->angles, forward, right, up);
+
+	side = DotProduct (from, right);
+	v_dmg_roll = count*side*v_kickroll.value;
+	
+	side = DotProduct (from, forward);
+	v_dmg_pitch = count*side*v_kickpitch.value;
+
+	v_dmg_time = v_kicktime.value;
+}
+
+
+/*
+==================
+V_cshift_f
+==================
+*/
+void V_cshift_f (void)
+{
+	cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
+	cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
+	cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
+	cshift_empty.percent = atoi(Cmd_Argv(4));
+}
+
+
+/*
+==================
+V_BonusFlash_f
+
+When you run over an item, the server sends this command
+==================
+*/
+void V_BonusFlash_f (void)
+{
+	cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
+	cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
+	cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
+	cl.cshifts[CSHIFT_BONUS].percent = 50;
+}
+
+/*
+=============
+V_SetContentsColor
+
+Underwater, lava, etc each has a color shift
+=============
+*/
+void V_SetContentsColor (int contents)
+{
+	switch (contents)
+	{
+	case CONTENTS_EMPTY:
+	case CONTENTS_SOLID:
+		cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
+		break;
+	case CONTENTS_LAVA:
+		cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
+		break;
+	case CONTENTS_SLIME:
+		cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
+		break;
+	default:
+		cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
+	}
+}
+
+/*
+=============
+V_CalcPowerupCshift
+=============
+*/
+void V_CalcPowerupCshift (void)
+{
+	if (cl.items & IT_QUAD)
+	{
+		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
+		cl.cshifts[CSHIFT_POWERUP].percent = 30;
+	}
+	else if (cl.items & IT_SUIT)
+	{
+		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
+		cl.cshifts[CSHIFT_POWERUP].percent = 20;
+	}
+	else if (cl.items & IT_INVISIBILITY)
+	{
+		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
+		cl.cshifts[CSHIFT_POWERUP].percent = 100;
+	}
+	else if (cl.items & IT_INVULNERABILITY)
+	{
+		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
+		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
+		cl.cshifts[CSHIFT_POWERUP].percent = 30;
+	}
+	else
+		cl.cshifts[CSHIFT_POWERUP].percent = 0;
+}
+
+/*
+=============
+V_CalcBlend
+=============
+*/
+#ifdef	GLQUAKE
+void V_CalcBlend (void)
+{
+	float	r, g, b, a, a2;
+	int		j;
+
+	r = 0;
+	g = 0;
+	b = 0;
+	a = 0;
+
+	for (j=0 ; j<NUM_CSHIFTS ; j++)	
+	{
+		if (!gl_cshiftpercent.value)
+			continue;
+
+		a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
+
+//		a2 = cl.cshifts[j].percent/255.0;
+		if (!a2)
+			continue;
+		a = a + a2*(1-a);
+//Con_Printf ("j:%i a:%f\n", j, a);
+		a2 = a2/a;
+		r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2;
+		g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2;
+		b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2;
+	}
+
+	v_blend[0] = r/255.0;
+	v_blend[1] = g/255.0;
+	v_blend[2] = b/255.0;
+	v_blend[3] = a;
+	if (v_blend[3] > 1)
+		v_blend[3] = 1;
+	if (v_blend[3] < 0)
+		v_blend[3] = 0;
+}
+#endif
+
+/*
+=============
+V_UpdatePalette
+=============
+*/
+#ifdef	GLQUAKE
+void V_UpdatePalette (void)
+{
+	int		i, j;
+	qboolean	new;
+	byte	*basepal, *newpal;
+	byte	pal[768];
+	float	r,g,b,a;
+	int		ir, ig, ib;
+	qboolean force;
+
+	V_CalcPowerupCshift ();
+	
+	new = false;
+	
+	for (i=0 ; i<NUM_CSHIFTS ; i++)
+	{
+		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
+		{
+			new = true;
+			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
+		}
+		for (j=0 ; j<3 ; j++)
+			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
+			{
+				new = true;
+				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
+			}
+	}
+	
+// drop the damage value
+	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
+	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
+		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+
+// drop the bonus value
+	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
+	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
+		cl.cshifts[CSHIFT_BONUS].percent = 0;
+
+	force = V_CheckGamma ();
+	if (!new && !force)
+		return;
+
+	V_CalcBlend ();
+
+	a = v_blend[3];
+	r = 255*v_blend[0]*a;
+	g = 255*v_blend[1]*a;
+	b = 255*v_blend[2]*a;
+
+	a = 1-a;
+	for (i=0 ; i<256 ; i++)
+	{
+		ir = i*a + r;
+		ig = i*a + g;
+		ib = i*a + b;
+		if (ir > 255)
+			ir = 255;
+		if (ig > 255)
+			ig = 255;
+		if (ib > 255)
+			ib = 255;
+
+		ramps[0][i] = gammatable[ir];
+		ramps[1][i] = gammatable[ig];
+		ramps[2][i] = gammatable[ib];
+	}
+
+	basepal = host_basepal;
+	newpal = pal;
+	
+	for (i=0 ; i<256 ; i++)
+	{
+		ir = basepal[0];
+		ig = basepal[1];
+		ib = basepal[2];
+		basepal += 3;
+		
+		newpal[0] = ramps[0][ir];
+		newpal[1] = ramps[1][ig];
+		newpal[2] = ramps[2][ib];
+		newpal += 3;
+	}
+
+	VID_ShiftPalette (pal);	
+}
+#else	// !GLQUAKE
+void V_UpdatePalette (void)
+{
+	int		i, j;
+	qboolean	new;
+	byte	*basepal, *newpal;
+	byte	pal[768];
+	int		r,g,b;
+	qboolean force;
+
+	V_CalcPowerupCshift ();
+	
+	new = false;
+	
+	for (i=0 ; i<NUM_CSHIFTS ; i++)
+	{
+		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
+		{
+			new = true;
+			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
+		}
+		for (j=0 ; j<3 ; j++)
+			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
+			{
+				new = true;
+				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
+			}
+	}
+	
+// drop the damage value
+	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
+	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
+		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+
+// drop the bonus value
+	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
+	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
+		cl.cshifts[CSHIFT_BONUS].percent = 0;
+
+	force = V_CheckGamma ();
+	if (!new && !force)
+		return;
+			
+	basepal = host_basepal;
+	newpal = pal;
+	
+	for (i=0 ; i<256 ; i++)
+	{
+		r = basepal[0];
+		g = basepal[1];
+		b = basepal[2];
+		basepal += 3;
+	
+		for (j=0 ; j<NUM_CSHIFTS ; j++)	
+		{
+			r += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[0]-r))>>8;
+			g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8;
+			b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8;
+		}
+		
+		newpal[0] = gammatable[r];
+		newpal[1] = gammatable[g];
+		newpal[2] = gammatable[b];
+		newpal += 3;
+	}
+
+	VID_ShiftPalette (pal);	
+}
+#endif	// !GLQUAKE
+
+
+/* 
+============================================================================== 
+ 
+						VIEW RENDERING 
+ 
+============================================================================== 
+*/ 
+
+float angledelta (float a)
+{
+	a = anglemod(a);
+	if (a > 180)
+		a -= 360;
+	return a;
+}
+
+/*
+==================
+CalcGunAngle
+==================
+*/
+void CalcGunAngle (void)
+{	
+	float	yaw, pitch, move;
+	static float oldyaw = 0;
+	static float oldpitch = 0;
+	
+	yaw = r_refdef.viewangles[YAW];
+	pitch = -r_refdef.viewangles[PITCH];
+
+	yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
+	if (yaw > 10)
+		yaw = 10;
+	if (yaw < -10)
+		yaw = -10;
+	pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
+	if (pitch > 10)
+		pitch = 10;
+	if (pitch < -10)
+		pitch = -10;
+	move = host_frametime*20;
+	if (yaw > oldyaw)
+	{
+		if (oldyaw + move < yaw)
+			yaw = oldyaw + move;
+	}
+	else
+	{
+		if (oldyaw - move > yaw)
+			yaw = oldyaw - move;
+	}
+	
+	if (pitch > oldpitch)
+	{
+		if (oldpitch + move < pitch)
+			pitch = oldpitch + move;
+	}
+	else
+	{
+		if (oldpitch - move > pitch)
+			pitch = oldpitch - move;
+	}
+	
+	oldyaw = yaw;
+	oldpitch = pitch;
+
+	cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
+	cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
+
+	cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
+	cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
+	cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
+}
+
+/*
+==============
+V_BoundOffsets
+==============
+*/
+void V_BoundOffsets (void)
+{
+	entity_t	*ent;
+	
+	ent = &cl_entities[cl.viewentity];
+
+// absolutely bound refresh reletive to entity clipping hull
+// so the view can never be inside a solid wall
+
+	if (r_refdef.vieworg[0] < ent->origin[0] - 14)
+		r_refdef.vieworg[0] = ent->origin[0] - 14;
+	else if (r_refdef.vieworg[0] > ent->origin[0] + 14)
+		r_refdef.vieworg[0] = ent->origin[0] + 14;
+	if (r_refdef.vieworg[1] < ent->origin[1] - 14)
+		r_refdef.vieworg[1] = ent->origin[1] - 14;
+	else if (r_refdef.vieworg[1] > ent->origin[1] + 14)
+		r_refdef.vieworg[1] = ent->origin[1] + 14;
+	if (r_refdef.vieworg[2] < ent->origin[2] - 22)
+		r_refdef.vieworg[2] = ent->origin[2] - 22;
+	else if (r_refdef.vieworg[2] > ent->origin[2] + 30)
+		r_refdef.vieworg[2] = ent->origin[2] + 30;
+}
+
+/*
+==============
+V_AddIdle
+
+Idle swaying
+==============
+*/
+void V_AddIdle (void)
+{
+	r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
+	r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
+	r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
+}
+
+
+/*
+==============
+V_CalcViewRoll
+
+Roll is induced by movement and damage
+==============
+*/
+void V_CalcViewRoll (void)
+{
+	float		side;
+		
+	side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity);
+	r_refdef.viewangles[ROLL] += side;
+
+	if (v_dmg_time > 0)
+	{
+		r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
+		r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
+		v_dmg_time -= host_frametime;
+	}
+
+	if (cl.stats[STAT_HEALTH] <= 0)
+	{
+		r_refdef.viewangles[ROLL] = 80;	// dead view angle
+		return;
+	}
+
+}
+
+
+/*
+==================
+V_CalcIntermissionRefdef
+
+==================
+*/
+void V_CalcIntermissionRefdef (void)
+{
+	entity_t	*ent, *view;
+	float		old;
+
+// ent is the player model (visible when out of body)
+	ent = &cl_entities[cl.viewentity];
+// view is the weapon model (only visible from inside body)
+	view = &cl.viewent;
+
+	VectorCopy (ent->origin, r_refdef.vieworg);
+	VectorCopy (ent->angles, r_refdef.viewangles);
+	view->model = NULL;
+
+// allways idle in intermission
+	old = v_idlescale.value;
+	v_idlescale.value = 1;
+	V_AddIdle ();
+	v_idlescale.value = old;
+}
+
+/*
+==================
+V_CalcRefdef
+
+==================
+*/
+void V_CalcRefdef (void)
+{
+	entity_t	*ent, *view;
+	int			i;
+	vec3_t		forward, right, up;
+	vec3_t		angles;
+	float		bob;
+	static float oldz = 0;
+
+	V_DriftPitch ();
+
+// ent is the player model (visible when out of body)
+	ent = &cl_entities[cl.viewentity];
+// view is the weapon model (only visible from inside body)
+	view = &cl.viewent;
+	
+
+// transform the view offset by the model's matrix to get the offset from
+// model origin for the view
+	ent->angles[YAW] = cl.viewangles[YAW];	// the model should face
+										// the view dir
+	ent->angles[PITCH] = -cl.viewangles[PITCH];	// the model should face
+										// the view dir
+										
+	
+	bob = V_CalcBob ();
+	
+// refresh position
+	VectorCopy (ent->origin, r_refdef.vieworg);
+	r_refdef.vieworg[2] += cl.viewheight + bob;
+
+// never let it sit exactly on a node line, because a water plane can
+// dissapear when viewed with the eye exactly on it.
+// the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
+	r_refdef.vieworg[0] += 1.0/32;
+	r_refdef.vieworg[1] += 1.0/32;
+	r_refdef.vieworg[2] += 1.0/32;
+
+	VectorCopy (cl.viewangles, r_refdef.viewangles);
+	V_CalcViewRoll ();
+	V_AddIdle ();
+
+// offsets
+	angles[PITCH] = -ent->angles[PITCH];	// because entity pitches are
+											//  actually backward
+	angles[YAW] = ent->angles[YAW];
+	angles[ROLL] = ent->angles[ROLL];
+
+	AngleVectors (angles, forward, right, up);
+
+	for (i=0 ; i<3 ; i++)
+		r_refdef.vieworg[i] += scr_ofsx.value*forward[i]
+			+ scr_ofsy.value*right[i]
+			+ scr_ofsz.value*up[i];
+	
+	
+	V_BoundOffsets ();
+		
+// set up gun position
+	VectorCopy (cl.viewangles, view->angles);
+	
+	CalcGunAngle ();
+
+	VectorCopy (ent->origin, view->origin);
+	view->origin[2] += cl.viewheight;
+
+	for (i=0 ; i<3 ; i++)
+	{
+		view->origin[i] += forward[i]*bob*0.4;
+//		view->origin[i] += right[i]*bob*0.4;
+//		view->origin[i] += up[i]*bob*0.8;
+	}
+	view->origin[2] += bob;
+
+// fudge position around to keep amount of weapon visible
+// roughly equal with different FOV
+
+#if 0
+	if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name,  "progs/v_shot2.mdl"))
+#endif
+	if (scr_viewsize.value == 110)
+		view->origin[2] += 1;
+	else if (scr_viewsize.value == 100)
+		view->origin[2] += 2;
+	else if (scr_viewsize.value == 90)
+		view->origin[2] += 1;
+	else if (scr_viewsize.value == 80)
+		view->origin[2] += 0.5;
+
+	view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
+	view->frame = cl.stats[STAT_WEAPONFRAME];
+	view->colormap = vid.colormap;
+
+// set up the refresh position
+	VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
+
+// smooth out stair step ups
+if (cl.onground && ent->origin[2] - oldz > 0)
+{
+	float steptime;
+	
+	steptime = cl.time - cl.oldtime;
+	if (steptime < 0)
+//FIXME		I_Error ("steptime < 0");
+		steptime = 0;
+
+	oldz += steptime * 80;
+	if (oldz > ent->origin[2])
+		oldz = ent->origin[2];
+	if (ent->origin[2] - oldz > 12)
+		oldz = ent->origin[2] - 12;
+	r_refdef.vieworg[2] += oldz - ent->origin[2];
+	view->origin[2] += oldz - ent->origin[2];
+}
+else
+	oldz = ent->origin[2];
+
+	if (chase_active.value)
+		Chase_Update ();
+}
+
+/*
+==================
+V_RenderView
+
+The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
+the entity origin, so any view position inside that will be valid
+==================
+*/
+extern vrect_t	scr_vrect;
+
+void V_RenderView (void)
+{
+	if (con_forcedup)
+		return;
+
+// don't allow cheats in multiplayer
+	if (cl.maxclients > 1)
+	{
+		Cvar_Set ("scr_ofsx", "0");
+		Cvar_Set ("scr_ofsy", "0");
+		Cvar_Set ("scr_ofsz", "0");
+	}
+
+	if (cl.intermission)
+	{	// intermission / finale rendering
+		V_CalcIntermissionRefdef ();	
+	}
+	else
+	{
+		if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ )
+			V_CalcRefdef ();
+	}
+
+	R_PushDlights ();
+
+	if (lcd_x.value)
+	{
+		//
+		// render two interleaved views
+		//
+		int		i;
+
+		vid.rowbytes <<= 1;
+		vid.aspect *= 0.5;
+
+		r_refdef.viewangles[YAW] -= lcd_yaw.value;
+		for (i=0 ; i<3 ; i++)
+			r_refdef.vieworg[i] -= right[i]*lcd_x.value;
+		R_RenderView ();
+
+		vid.buffer += vid.rowbytes>>1;
+
+		R_PushDlights ();
+
+		r_refdef.viewangles[YAW] += lcd_yaw.value*2;
+		for (i=0 ; i<3 ; i++)
+			r_refdef.vieworg[i] += 2*right[i]*lcd_x.value;
+		R_RenderView ();
+
+		vid.buffer -= vid.rowbytes>>1;
+
+		r_refdef.vrect.height <<= 1;
+
+		vid.rowbytes >>= 1;
+		vid.aspect *= 2;
+	}
+	else
+	{
+		R_RenderView ();
+	}
+
+#ifndef GLQUAKE
+	if (crosshair.value)
+		Draw_Character (scr_vrect.x + scr_vrect.width/2 + cl_crossx.value, 
+			scr_vrect.y + scr_vrect.height/2 + cl_crossy.value, '+');
+#endif
+		
+}
+
+//============================================================================
+
+/*
+=============
+V_Init
+=============
+*/
+void V_Init (void)
+{
+	Cmd_AddCommand ("v_cshift", V_cshift_f);	
+	Cmd_AddCommand ("bf", V_BonusFlash_f);
+	Cmd_AddCommand ("centerview", V_StartPitchDrift);
+
+	Cvar_RegisterVariable (&lcd_x);
+	Cvar_RegisterVariable (&lcd_yaw);
+
+	Cvar_RegisterVariable (&v_centermove);
+	Cvar_RegisterVariable (&v_centerspeed);
+
+	Cvar_RegisterVariable (&v_iyaw_cycle);
+	Cvar_RegisterVariable (&v_iroll_cycle);
+	Cvar_RegisterVariable (&v_ipitch_cycle);
+	Cvar_RegisterVariable (&v_iyaw_level);
+	Cvar_RegisterVariable (&v_iroll_level);
+	Cvar_RegisterVariable (&v_ipitch_level);
+
+	Cvar_RegisterVariable (&v_idlescale);
+	Cvar_RegisterVariable (&crosshair);
+	Cvar_RegisterVariable (&cl_crossx);
+	Cvar_RegisterVariable (&cl_crossy);
+	Cvar_RegisterVariable (&gl_cshiftpercent);
+
+	Cvar_RegisterVariable (&scr_ofsx);
+	Cvar_RegisterVariable (&scr_ofsy);
+	Cvar_RegisterVariable (&scr_ofsz);
+	Cvar_RegisterVariable (&cl_rollspeed);
+	Cvar_RegisterVariable (&cl_rollangle);
+	Cvar_RegisterVariable (&cl_bob);
+	Cvar_RegisterVariable (&cl_bobcycle);
+	Cvar_RegisterVariable (&cl_bobup);
+
+	Cvar_RegisterVariable (&v_kicktime);
+	Cvar_RegisterVariable (&v_kickroll);
+	Cvar_RegisterVariable (&v_kickpitch);	
+	
+	BuildGammaTable (1.0);	// no gamma yet
+	Cvar_RegisterVariable (&v_gamma);
+}
+
+
diff --git a/apps/plugins/sdl/progs/quake/view.h b/apps/plugins/sdl/progs/quake/view.h
new file mode 100644
index 0000000..82123a1
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/view.h
@@ -0,0 +1,35 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// view.h
+
+extern	cvar_t		v_gamma;
+
+extern	byte		gammatable[256];	// palette is sent through this
+extern	byte		ramps[3][256];
+extern float v_blend[4];
+
+extern cvar_t lcd_x;
+
+
+void V_Init (void);
+void V_RenderView (void);
+float V_CalcRoll (vec3_t angles, vec3_t velocity);
+void V_UpdatePalette (void);
+
diff --git a/apps/plugins/sdl/progs/quake/wad.c b/apps/plugins/sdl/progs/quake/wad.c
new file mode 100644
index 0000000..c84f72c
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/wad.c
@@ -0,0 +1,158 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// wad.c
+
+#include "quakedef.h"
+
+int			wad_numlumps;
+lumpinfo_t	*wad_lumps;
+byte		*wad_base;
+
+void SwapPic (qpic_t *pic);
+
+/*
+==================
+W_CleanupName
+
+Lowercases name and pads with spaces and a terminating 0 to the length of
+lumpinfo_t->name.
+Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time
+Space padding is so names can be printed nicely in tables.
+Can safely be performed in place.
+==================
+*/
+void W_CleanupName (char *in, char *out)
+{
+	int		i;
+	int		c;
+	
+	for (i=0 ; i<16 ; i++ )
+	{
+		c = in[i];
+		if (!c)
+			break;
+			
+		if (c >= 'A' && c <= 'Z')
+			c += ('a' - 'A');
+		out[i] = c;
+	}
+	
+	for ( ; i< 16 ; i++ )
+		out[i] = 0;
+}
+
+
+
+/*
+====================
+W_LoadWadFile
+====================
+*/
+void W_LoadWadFile (char *filename)
+{
+	lumpinfo_t		*lump_p;
+	wadinfo_t		*header;
+	unsigned		i;
+	int				infotableofs;
+	
+	wad_base = COM_LoadHunkFile (filename);
+	if (!wad_base)
+		Sys_Error ("W_LoadWadFile: couldn't load %s", filename);
+
+	header = (wadinfo_t *)wad_base;
+	
+	if (header->identification[0] != 'W'
+	|| header->identification[1] != 'A'
+	|| header->identification[2] != 'D'
+	|| header->identification[3] != '2')
+		Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename);
+		
+	wad_numlumps = LittleLong(header->numlumps);
+	infotableofs = LittleLong(header->infotableofs);
+	wad_lumps = (lumpinfo_t *)(wad_base + infotableofs);
+	
+	for (i=0, lump_p = wad_lumps ; i<wad_numlumps ; i++,lump_p++)
+	{
+		lump_p->filepos = LittleLong(lump_p->filepos);
+		lump_p->size = LittleLong(lump_p->size);
+		W_CleanupName (lump_p->name, lump_p->name);
+		if (lump_p->type == TYP_QPIC)
+			SwapPic ( (qpic_t *)(wad_base + lump_p->filepos));
+	}
+}
+
+
+/*
+=============
+W_GetLumpinfo
+=============
+*/
+lumpinfo_t	*W_GetLumpinfo (char *name)
+{
+	int		i;
+	lumpinfo_t	*lump_p;
+	char	clean[16];
+	
+	W_CleanupName (name, clean);
+	
+	for (lump_p=wad_lumps, i=0 ; i<wad_numlumps ; i++,lump_p++)
+	{
+		if (!strcmp(clean, lump_p->name))
+			return lump_p;
+	}
+	
+	Sys_Error ("W_GetLumpinfo: %s not found", name);
+	return NULL;
+}
+
+void *W_GetLumpName (char *name)
+{
+	lumpinfo_t	*lump;
+	
+	lump = W_GetLumpinfo (name);
+	
+	return (void *)(wad_base + lump->filepos);
+}
+
+void *W_GetLumpNum (int num)
+{
+	lumpinfo_t	*lump;
+	
+	if (num < 0 || num > wad_numlumps)
+		Sys_Error ("W_GetLumpNum: bad number: %i", num);
+		
+	lump = wad_lumps + num;
+	
+	return (void *)(wad_base + lump->filepos);
+}
+
+/*
+=============================================================================
+
+automatic byte swapping
+
+=============================================================================
+*/
+
+void SwapPic (qpic_t *pic)
+{
+	pic->width = LittleLong(pic->width);
+	pic->height = LittleLong(pic->height);	
+}
diff --git a/apps/plugins/sdl/progs/quake/wad.h b/apps/plugins/sdl/progs/quake/wad.h
new file mode 100644
index 0000000..9a740d9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/wad.h
@@ -0,0 +1,75 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// wad.h
+
+//===============
+//   TYPES
+//===============
+
+#define	CMP_NONE		0
+#define	CMP_LZSS		1
+
+#define	TYP_NONE		0
+#define	TYP_LABEL		1
+
+#define	TYP_LUMPY		64				// 64 + grab command number
+#define	TYP_PALETTE		64
+#define	TYP_QTEX		65
+#define	TYP_QPIC		66
+#define	TYP_SOUND		67
+#define	TYP_MIPTEX		68
+
+typedef struct
+{
+	int			width, height;
+	byte		data[4];			// variably sized
+} qpic_t;
+
+
+
+typedef struct
+{
+	char		identification[4];		// should be WAD2 or 2DAW
+	int			numlumps;
+	int			infotableofs;
+} wadinfo_t;
+
+typedef struct
+{
+	int			filepos;
+	int			disksize;
+	int			size;					// uncompressed
+	char		type;
+	char		compression;
+	char		pad1, pad2;
+	char		name[16];				// must be null terminated
+} lumpinfo_t;
+
+extern	int			wad_numlumps;
+extern	lumpinfo_t	*wad_lumps;
+extern	byte		*wad_base;
+
+void	W_LoadWadFile (char *filename);
+void	W_CleanupName (char *in, char *out);
+lumpinfo_t	*W_GetLumpinfo (char *name);
+void	*W_GetLumpName (char *name);
+void	*W_GetLumpNum (int num);
+
+void SwapPic (qpic_t *pic);
diff --git a/apps/plugins/sdl/progs/quake/winquake.h b/apps/plugins/sdl/progs/quake/winquake.h
new file mode 100644
index 0000000..a956984
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/winquake.h
@@ -0,0 +1,115 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// winquake.h: Win32-specific Quake header file
+
+#pragma warning( disable : 4229 )  // mgraph gets this
+
+#include <windows.h>
+#define WM_MOUSEWHEEL                   0x020A
+
+#ifndef SERVERONLY
+#include <ddraw.h>
+#include <dsound.h>
+#ifndef GLQUAKE
+#include <mgraph.h>
+#endif
+#endif
+
+extern	HINSTANCE	global_hInstance;
+extern	int			global_nCmdShow;
+
+#ifndef SERVERONLY
+
+extern LPDIRECTDRAW		lpDD;
+extern qboolean			DDActive;
+extern LPDIRECTDRAWSURFACE	lpPrimary;
+extern LPDIRECTDRAWSURFACE	lpFrontBuffer;
+extern LPDIRECTDRAWSURFACE	lpBackBuffer;
+extern LPDIRECTDRAWPALETTE	lpDDPal;
+extern LPDIRECTSOUND pDS;
+extern LPDIRECTSOUNDBUFFER pDSBuf;
+
+extern DWORD gSndBufSize;
+//#define SNDBUFSIZE 65536
+
+void	VID_LockBuffer (void);
+void	VID_UnlockBuffer (void);
+
+#endif
+
+typedef int modestate_t;
+enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT};
+
+extern modestate_t	modestate;
+
+extern HWND			mainwindow;
+extern qboolean		ActiveApp, Minimized;
+
+extern qboolean	WinNT;
+
+int VID_ForceUnlockedAndReturnState (void);
+void VID_ForceLockState (int lk);
+
+void IN_ShowMouse (void);
+void IN_DeactivateMouse (void);
+void IN_HideMouse (void);
+void IN_ActivateMouse (void);
+void IN_RestoreOriginalMouseState (void);
+void IN_SetQuakeMouseState (void);
+void IN_MouseEvent (int mstate);
+
+extern qboolean	winsock_lib_initialized;
+
+extern cvar_t		_windowed_mouse;
+
+extern int		window_center_x, window_center_y;
+extern RECT		window_rect;
+
+extern qboolean	mouseinitialized;
+extern HWND		hwnd_dialog;
+
+extern HANDLE	hinput, houtput;
+
+void IN_UpdateClipCursor (void);
+void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify);
+
+void S_BlockSound (void);
+void S_UnblockSound (void);
+
+void VID_SetDefaultMode (void);
+
+int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);
+int (PASCAL FAR *pWSACleanup)(void);
+int (PASCAL FAR *pWSAGetLastError)(void);
+SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol);
+int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);
+int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname,
+							  const char FAR * optval, int optlen);
+int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags,
+							struct sockaddr FAR *from, int FAR * fromlen);
+int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags,
+						  const struct sockaddr FAR *to, int tolen);
+int (PASCAL FAR *pclosesocket)(SOCKET s);
+int (PASCAL FAR *pgethostname)(char FAR * name, int namelen);
+struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name);
+struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr,
+												  int len, int type);
+int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name,
+							   int FAR * namelen);
diff --git a/apps/plugins/sdl/progs/quake/world.c b/apps/plugins/sdl/progs/quake/world.c
new file mode 100644
index 0000000..e1acfea
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/world.c
@@ -0,0 +1,962 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// world.c -- world query functions
+
+#include "quakedef.h"
+
+/*
+
+entities never clip against themselves, or their owner
+
+line of sight checks trace->crosscontent, but bullets don't
+
+*/
+
+
+typedef struct
+{
+	vec3_t		boxmins, boxmaxs;// enclose the test object along entire move
+	float		*mins, *maxs;	// size of the moving object
+	vec3_t		mins2, maxs2;	// size when clipping against mosnters
+	float		*start, *end;
+	trace_t		trace;
+	int			type;
+	edict_t		*passedict;
+} moveclip_t;
+
+
+int SV_HullPointContents (hull_t *hull, int num, vec3_t p);
+
+/*
+===============================================================================
+
+HULL BOXES
+
+===============================================================================
+*/
+
+
+static	hull_t		box_hull;
+static	dclipnode_t	box_clipnodes[6];
+static	mplane_t	box_planes[6];
+
+/*
+===================
+SV_InitBoxHull
+
+Set up the planes and clipnodes so that the six floats of a bounding box
+can just be stored out and get a proper hull_t structure.
+===================
+*/
+void SV_InitBoxHull (void)
+{
+	int		i;
+	int		side;
+
+	box_hull.clipnodes = box_clipnodes;
+	box_hull.planes = box_planes;
+	box_hull.firstclipnode = 0;
+	box_hull.lastclipnode = 5;
+
+	for (i=0 ; i<6 ; i++)
+	{
+		box_clipnodes[i].planenum = i;
+		
+		side = i&1;
+		
+		box_clipnodes[i].children[side] = CONTENTS_EMPTY;
+		if (i != 5)
+			box_clipnodes[i].children[side^1] = i + 1;
+		else
+			box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
+		
+		box_planes[i].type = i>>1;
+		box_planes[i].normal[i>>1] = 1;
+	}
+	
+}
+
+
+/*
+===================
+SV_HullForBox
+
+To keep everything totally uniform, bounding boxes are turned into small
+BSP trees instead of being compared directly.
+===================
+*/
+hull_t	*SV_HullForBox (vec3_t mins, vec3_t maxs)
+{
+	box_planes[0].dist = maxs[0];
+	box_planes[1].dist = mins[0];
+	box_planes[2].dist = maxs[1];
+	box_planes[3].dist = mins[1];
+	box_planes[4].dist = maxs[2];
+	box_planes[5].dist = mins[2];
+
+	return &box_hull;
+}
+
+
+
+/*
+================
+SV_HullForEntity
+
+Returns a hull that can be used for testing or clipping an object of mins/maxs
+size.
+Offset is filled in to contain the adjustment that must be added to the
+testing object's origin to get a point to use with the returned hull.
+================
+*/
+hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
+{
+	model_t		*model;
+	vec3_t		size;
+	vec3_t		hullmins, hullmaxs;
+	hull_t		*hull;
+
+// decide which clipping hull to use, based on the size
+	if (ent->v.solid == SOLID_BSP)
+	{	// explicit hulls in the BSP model
+		if (ent->v.movetype != MOVETYPE_PUSH)
+			Sys_Error ("SOLID_BSP without MOVETYPE_PUSH");
+
+		model = sv.models[ (int)ent->v.modelindex ];
+
+		if (!model || model->type != mod_brush)
+			Sys_Error ("MOVETYPE_PUSH with a non bsp model");
+
+		VectorSubtract (maxs, mins, size);
+		if (size[0] < 3)
+			hull = &model->hulls[0];
+		else if (size[0] <= 32)
+			hull = &model->hulls[1];
+		else
+			hull = &model->hulls[2];
+
+// calculate an offset value to center the origin
+		VectorSubtract (hull->clip_mins, mins, offset);
+		VectorAdd (offset, ent->v.origin, offset);
+	}
+	else
+	{	// create a temp hull from bounding box sizes
+
+		VectorSubtract (ent->v.mins, maxs, hullmins);
+		VectorSubtract (ent->v.maxs, mins, hullmaxs);
+		hull = SV_HullForBox (hullmins, hullmaxs);
+		
+		VectorCopy (ent->v.origin, offset);
+	}
+
+
+	return hull;
+}
+
+/*
+===============================================================================
+
+ENTITY AREA CHECKING
+
+===============================================================================
+*/
+
+typedef struct areanode_s
+{
+	int		axis;		// -1 = leaf node
+	float	dist;
+	struct areanode_s	*children[2];
+	link_t	trigger_edicts;
+	link_t	solid_edicts;
+} areanode_t;
+
+#define	AREA_DEPTH	4
+#define	AREA_NODES	32
+
+static	areanode_t	sv_areanodes[AREA_NODES];
+static	int			sv_numareanodes;
+
+/*
+===============
+SV_CreateAreaNode
+
+===============
+*/
+areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
+{
+	areanode_t	*anode;
+	vec3_t		size;
+	vec3_t		mins1, maxs1, mins2, maxs2;
+
+	anode = &sv_areanodes[sv_numareanodes];
+	sv_numareanodes++;
+
+	ClearLink (&anode->trigger_edicts);
+	ClearLink (&anode->solid_edicts);
+	
+	if (depth == AREA_DEPTH)
+	{
+		anode->axis = -1;
+		anode->children[0] = anode->children[1] = NULL;
+		return anode;
+	}
+	
+	VectorSubtract (maxs, mins, size);
+	if (size[0] > size[1])
+		anode->axis = 0;
+	else
+		anode->axis = 1;
+	
+	anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
+	VectorCopy (mins, mins1);	
+	VectorCopy (mins, mins2);	
+	VectorCopy (maxs, maxs1);	
+	VectorCopy (maxs, maxs2);	
+	
+	maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
+	
+	anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2);
+	anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1);
+
+	return anode;
+}
+
+/*
+===============
+SV_ClearWorld
+
+===============
+*/
+void SV_ClearWorld (void)
+{
+	SV_InitBoxHull ();
+	
+	memset (sv_areanodes, 0, sizeof(sv_areanodes));
+	sv_numareanodes = 0;
+	SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs);
+}
+
+
+/*
+===============
+SV_UnlinkEdict
+
+===============
+*/
+void SV_UnlinkEdict (edict_t *ent)
+{
+	if (!ent->area.prev)
+		return;		// not linked in anywhere
+	RemoveLink (&ent->area);
+	ent->area.prev = ent->area.next = NULL;
+}
+
+
+/*
+====================
+SV_TouchLinks
+====================
+*/
+void SV_TouchLinks ( edict_t *ent, areanode_t *node )
+{
+	link_t		*l, *next;
+	edict_t		*touch;
+	int			old_self, old_other;
+
+// touch linked edicts
+	for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next)
+	{
+		next = l->next;
+		touch = EDICT_FROM_AREA(l);
+		if (touch == ent)
+			continue;
+		if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER)
+			continue;
+		if (ent->v.absmin[0] > touch->v.absmax[0]
+		|| ent->v.absmin[1] > touch->v.absmax[1]
+		|| ent->v.absmin[2] > touch->v.absmax[2]
+		|| ent->v.absmax[0] < touch->v.absmin[0]
+		|| ent->v.absmax[1] < touch->v.absmin[1]
+		|| ent->v.absmax[2] < touch->v.absmin[2] )
+			continue;
+		old_self = pr_global_struct->self;
+		old_other = pr_global_struct->other;
+
+		pr_global_struct->self = EDICT_TO_PROG(touch);
+		pr_global_struct->other = EDICT_TO_PROG(ent);
+		pr_global_struct->time = sv.time;
+		PR_ExecuteProgram (touch->v.touch);
+
+		pr_global_struct->self = old_self;
+		pr_global_struct->other = old_other;
+	}
+	
+// recurse down both sides
+	if (node->axis == -1)
+		return;
+	
+	if ( ent->v.absmax[node->axis] > node->dist )
+		SV_TouchLinks ( ent, node->children[0] );
+	if ( ent->v.absmin[node->axis] < node->dist )
+		SV_TouchLinks ( ent, node->children[1] );
+}
+
+
+/*
+===============
+SV_FindTouchedLeafs
+
+===============
+*/
+void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node)
+{
+	mplane_t	*splitplane;
+	mleaf_t		*leaf;
+	int			sides;
+	int			leafnum;
+
+	if (node->contents == CONTENTS_SOLID)
+		return;
+	
+// add an efrag if the node is a leaf
+
+	if ( node->contents < 0)
+	{
+		if (ent->num_leafs == MAX_ENT_LEAFS)
+			return;
+
+		leaf = (mleaf_t *)node;
+		leafnum = leaf - sv.worldmodel->leafs - 1;
+
+		ent->leafnums[ent->num_leafs] = leafnum;
+		ent->num_leafs++;			
+		return;
+	}
+	
+// NODE_MIXED
+
+	splitplane = node->plane;
+	sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane);
+	
+// recurse down the contacted sides
+	if (sides & 1)
+		SV_FindTouchedLeafs (ent, node->children[0]);
+		
+	if (sides & 2)
+		SV_FindTouchedLeafs (ent, node->children[1]);
+}
+
+/*
+===============
+SV_LinkEdict
+
+===============
+*/
+void SV_LinkEdict (edict_t *ent, qboolean touch_triggers)
+{
+	areanode_t	*node;
+
+	if (ent->area.prev)
+		SV_UnlinkEdict (ent);	// unlink from old position
+		
+	if (ent == sv.edicts)
+		return;		// don't add the world
+
+	if (ent->free)
+		return;
+
+// set the abs box
+
+#ifdef QUAKE2
+	if (ent->v.solid == SOLID_BSP && 
+	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+	{	// expand for rotation
+		float		max, v;
+		int			i;
+
+		max = 0;
+		for (i=0 ; i<3 ; i++)
+		{
+			v =fabs( ent->v.mins[i]);
+			if (v > max)
+				max = v;
+			v =fabs( ent->v.maxs[i]);
+			if (v > max)
+				max = v;
+		}
+		for (i=0 ; i<3 ; i++)
+		{
+			ent->v.absmin[i] = ent->v.origin[i] - max;
+			ent->v.absmax[i] = ent->v.origin[i] + max;
+		}
+	}
+	else
+#endif
+	{
+		VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);	
+		VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
+	}
+
+//
+// to make items easier to pick up and allow them to be grabbed off
+// of shelves, the abs sizes are expanded
+//
+	if ((int)ent->v.flags & FL_ITEM)
+	{
+		ent->v.absmin[0] -= 15;
+		ent->v.absmin[1] -= 15;
+		ent->v.absmax[0] += 15;
+		ent->v.absmax[1] += 15;
+	}
+	else
+	{	// because movement is clipped an epsilon away from an actual edge,
+		// we must fully check even when bounding boxes don't quite touch
+		ent->v.absmin[0] -= 1;
+		ent->v.absmin[1] -= 1;
+		ent->v.absmin[2] -= 1;
+		ent->v.absmax[0] += 1;
+		ent->v.absmax[1] += 1;
+		ent->v.absmax[2] += 1;
+	}
+	
+// link to PVS leafs
+	ent->num_leafs = 0;
+	if (ent->v.modelindex)
+		SV_FindTouchedLeafs (ent, sv.worldmodel->nodes);
+
+	if (ent->v.solid == SOLID_NOT)
+		return;
+
+// find the first node that the ent's box crosses
+	node = sv_areanodes;
+	while (1)
+	{
+		if (node->axis == -1)
+			break;
+		if (ent->v.absmin[node->axis] > node->dist)
+			node = node->children[0];
+		else if (ent->v.absmax[node->axis] < node->dist)
+			node = node->children[1];
+		else
+			break;		// crosses the node
+	}
+	
+// link it in	
+
+	if (ent->v.solid == SOLID_TRIGGER)
+		InsertLinkBefore (&ent->area, &node->trigger_edicts);
+	else
+		InsertLinkBefore (&ent->area, &node->solid_edicts);
+	
+// if touch_triggers, touch all entities at this node and decend for more
+	if (touch_triggers)
+		SV_TouchLinks ( ent, sv_areanodes );
+}
+
+
+
+/*
+===============================================================================
+
+POINT TESTING IN HULLS
+
+===============================================================================
+*/
+
+#if	!id386
+
+/*
+==================
+SV_HullPointContents
+
+==================
+*/
+int SV_HullPointContents (hull_t *hull, int num, vec3_t p)
+{
+	float		d;
+	dclipnode_t	*node;
+	mplane_t	*plane;
+
+	while (num >= 0)
+	{
+		if (num < hull->firstclipnode || num > hull->lastclipnode)
+			Sys_Error ("SV_HullPointContents: bad node number");
+	
+		node = hull->clipnodes + num;
+		plane = hull->planes + node->planenum;
+		
+		if (plane->type < 3)
+			d = p[plane->type] - plane->dist;
+		else
+			d = DotProduct (plane->normal, p) - plane->dist;
+		if (d < 0)
+			num = node->children[1];
+		else
+			num = node->children[0];
+	}
+	
+	return num;
+}
+
+#endif	// !id386
+
+
+/*
+==================
+SV_PointContents
+
+==================
+*/
+int SV_PointContents (vec3_t p)
+{
+	int		cont;
+
+	cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p);
+	if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN)
+		cont = CONTENTS_WATER;
+	return cont;
+}
+
+int SV_TruePointContents (vec3_t p)
+{
+	return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p);
+}
+
+//===========================================================================
+
+/*
+============
+SV_TestEntityPosition
+
+This could be a lot more efficient...
+============
+*/
+edict_t	*SV_TestEntityPosition (edict_t *ent)
+{
+	trace_t	trace;
+
+	trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent);
+	
+	if (trace.startsolid)
+		return sv.edicts;
+		
+	return NULL;
+}
+
+
+/*
+===============================================================================
+
+LINE TESTING IN HULLS
+
+===============================================================================
+*/
+
+// 1/32 epsilon to keep floating point happy
+#define	DIST_EPSILON	(0.03125)
+
+/*
+==================
+SV_RecursiveHullCheck
+
+==================
+*/
+qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
+{
+	dclipnode_t	*node;
+	mplane_t	*plane;
+	float		t1, t2;
+	float		frac;
+	int			i;
+	vec3_t		mid;
+	int			side;
+	float		midf;
+
+// check for empty
+	if (num < 0)
+	{
+		if (num != CONTENTS_SOLID)
+		{
+			trace->allsolid = false;
+			if (num == CONTENTS_EMPTY)
+				trace->inopen = true;
+			else
+				trace->inwater = true;
+		}
+		else
+			trace->startsolid = true;
+		return true;		// empty
+	}
+
+	if (num < hull->firstclipnode || num > hull->lastclipnode)
+		Sys_Error ("SV_RecursiveHullCheck: bad node number");
+
+//
+// find the point distances
+//
+	node = hull->clipnodes + num;
+	plane = hull->planes + node->planenum;
+
+	if (plane->type < 3)
+	{
+		t1 = p1[plane->type] - plane->dist;
+		t2 = p2[plane->type] - plane->dist;
+	}
+	else
+	{
+		t1 = DotProduct (plane->normal, p1) - plane->dist;
+		t2 = DotProduct (plane->normal, p2) - plane->dist;
+	}
+	
+#if 1
+	if (t1 >= 0 && t2 >= 0)
+		return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
+	if (t1 < 0 && t2 < 0)
+		return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
+#else
+	if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) )
+		return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);
+	if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) )
+		return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);
+#endif
+
+// put the crosspoint DIST_EPSILON pixels on the near side
+	if (t1 < 0)
+		frac = (t1 + DIST_EPSILON)/(t1-t2);
+	else
+		frac = (t1 - DIST_EPSILON)/(t1-t2);
+	if (frac < 0)
+		frac = 0;
+	if (frac > 1)
+		frac = 1;
+		
+	midf = p1f + (p2f - p1f)*frac;
+	for (i=0 ; i<3 ; i++)
+		mid[i] = p1[i] + frac*(p2[i] - p1[i]);
+
+	side = (t1 < 0);
+
+// move up to the node
+	if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )
+		return false;
+
+#ifdef PARANOID
+	if (SV_HullPointContents (sv_hullmodel, mid, node->children[side])
+	== CONTENTS_SOLID)
+	{
+		Con_Printf ("mid PointInHullSolid\n");
+		return false;
+	}
+#endif
+	
+	if (SV_HullPointContents (hull, node->children[side^1], mid)
+	!= CONTENTS_SOLID)
+// go past the node
+		return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
+	
+	if (trace->allsolid)
+		return false;		// never got out of the solid area
+		
+//==================
+// the other side of the node is solid, this is the impact point
+//==================
+	if (!side)
+	{
+		VectorCopy (plane->normal, trace->plane.normal);
+		trace->plane.dist = plane->dist;
+	}
+	else
+	{
+		VectorSubtract (vec3_origin, plane->normal, trace->plane.normal);
+		trace->plane.dist = -plane->dist;
+	}
+
+	while (SV_HullPointContents (hull, hull->firstclipnode, mid)
+	== CONTENTS_SOLID)
+	{ // shouldn't really happen, but does occasionally
+		frac -= 0.1;
+		if (frac < 0)
+		{
+			trace->fraction = midf;
+			VectorCopy (mid, trace->endpos);
+			Con_DPrintf ("backup past 0\n");
+			return false;
+		}
+		midf = p1f + (p2f - p1f)*frac;
+		for (i=0 ; i<3 ; i++)
+			mid[i] = p1[i] + frac*(p2[i] - p1[i]);
+	}
+
+	trace->fraction = midf;
+	VectorCopy (mid, trace->endpos);
+
+	return false;
+}
+
+
+/*
+==================
+SV_ClipMoveToEntity
+
+Handles selection or creation of a clipping hull, and offseting (and
+eventually rotation) of the end points
+==================
+*/
+trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
+{
+	trace_t		trace;
+	vec3_t		offset;
+	vec3_t		start_l, end_l;
+	hull_t		*hull;
+
+// fill in a default trace
+	memset (&trace, 0, sizeof(trace_t));
+	trace.fraction = 1;
+	trace.allsolid = true;
+	VectorCopy (end, trace.endpos);
+
+// get the clipping hull
+	hull = SV_HullForEntity (ent, mins, maxs, offset);
+
+	VectorSubtract (start, offset, start_l);
+	VectorSubtract (end, offset, end_l);
+
+#ifdef QUAKE2
+	// rotate start and end into the models frame of reference
+	if (ent->v.solid == SOLID_BSP && 
+	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+	{
+		vec3_t	a;
+		vec3_t	forward, right, up;
+		vec3_t	temp;
+
+		AngleVectors (ent->v.angles, forward, right, up);
+
+		VectorCopy (start_l, temp);
+		start_l[0] = DotProduct (temp, forward);
+		start_l[1] = -DotProduct (temp, right);
+		start_l[2] = DotProduct (temp, up);
+
+		VectorCopy (end_l, temp);
+		end_l[0] = DotProduct (temp, forward);
+		end_l[1] = -DotProduct (temp, right);
+		end_l[2] = DotProduct (temp, up);
+	}
+#endif
+
+// trace a line through the apropriate clipping hull
+	SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace);
+
+#ifdef QUAKE2
+	// rotate endpos back to world frame of reference
+	if (ent->v.solid == SOLID_BSP && 
+	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
+	{
+		vec3_t	a;
+		vec3_t	forward, right, up;
+		vec3_t	temp;
+
+		if (trace.fraction != 1)
+		{
+			VectorSubtract (vec3_origin, ent->v.angles, a);
+			AngleVectors (a, forward, right, up);
+
+			VectorCopy (trace.endpos, temp);
+			trace.endpos[0] = DotProduct (temp, forward);
+			trace.endpos[1] = -DotProduct (temp, right);
+			trace.endpos[2] = DotProduct (temp, up);
+
+			VectorCopy (trace.plane.normal, temp);
+			trace.plane.normal[0] = DotProduct (temp, forward);
+			trace.plane.normal[1] = -DotProduct (temp, right);
+			trace.plane.normal[2] = DotProduct (temp, up);
+		}
+	}
+#endif
+
+// fix trace up by the offset
+	if (trace.fraction != 1)
+		VectorAdd (trace.endpos, offset, trace.endpos);
+
+// did we clip the move?
+	if (trace.fraction < 1 || trace.startsolid  )
+		trace.ent = ent;
+
+	return trace;
+}
+
+//===========================================================================
+
+/*
+====================
+SV_ClipToLinks
+
+Mins and maxs enclose the entire area swept by the move
+====================
+*/
+void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
+{
+	link_t		*l, *next;
+	edict_t		*touch;
+	trace_t		trace;
+
+// touch linked edicts
+	for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
+	{
+		next = l->next;
+		touch = EDICT_FROM_AREA(l);
+		if (touch->v.solid == SOLID_NOT)
+			continue;
+		if (touch == clip->passedict)
+			continue;
+		if (touch->v.solid == SOLID_TRIGGER)
+			Sys_Error ("Trigger in clipping list");
+
+		if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
+			continue;
+
+		if (clip->boxmins[0] > touch->v.absmax[0]
+		|| clip->boxmins[1] > touch->v.absmax[1]
+		|| clip->boxmins[2] > touch->v.absmax[2]
+		|| clip->boxmaxs[0] < touch->v.absmin[0]
+		|| clip->boxmaxs[1] < touch->v.absmin[1]
+		|| clip->boxmaxs[2] < touch->v.absmin[2] )
+			continue;
+
+		if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])
+			continue;	// points never interact
+
+	// might intersect, so do an exact clip
+		if (clip->trace.allsolid)
+			return;
+		if (clip->passedict)
+		{
+		 	if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
+				continue;	// don't clip against own missiles
+			if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
+				continue;	// don't clip against owner
+		}
+
+		if ((int)touch->v.flags & FL_MONSTER)
+			trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end);
+		else
+			trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end);
+		if (trace.allsolid || trace.startsolid ||
+		trace.fraction < clip->trace.fraction)
+		{
+			trace.ent = touch;
+		 	if (clip->trace.startsolid)
+			{
+				clip->trace = trace;
+				clip->trace.startsolid = true;
+			}
+			else
+				clip->trace = trace;
+		}
+		else if (trace.startsolid)
+			clip->trace.startsolid = true;
+	}
+	
+// recurse down both sides
+	if (node->axis == -1)
+		return;
+
+	if ( clip->boxmaxs[node->axis] > node->dist )
+		SV_ClipToLinks ( node->children[0], clip );
+	if ( clip->boxmins[node->axis] < node->dist )
+		SV_ClipToLinks ( node->children[1], clip );
+}
+
+
+/*
+==================
+SV_MoveBounds
+==================
+*/
+void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
+{
+#if 0
+// debug to test against everything
+boxmins[0] = boxmins[1] = boxmins[2] = -9999;
+boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
+#else
+	int		i;
+	
+	for (i=0 ; i<3 ; i++)
+	{
+		if (end[i] > start[i])
+		{
+			boxmins[i] = start[i] + mins[i] - 1;
+			boxmaxs[i] = end[i] + maxs[i] + 1;
+		}
+		else
+		{
+			boxmins[i] = end[i] + mins[i] - 1;
+			boxmaxs[i] = start[i] + maxs[i] + 1;
+		}
+	}
+#endif
+}
+
+/*
+==================
+SV_Move
+==================
+*/
+trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict)
+{
+	moveclip_t	clip;
+	int			i;
+
+	memset ( &clip, 0, sizeof ( moveclip_t ) );
+
+// clip to world
+	clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end );
+
+	clip.start = start;
+	clip.end = end;
+	clip.mins = mins;
+	clip.maxs = maxs;
+	clip.type = type;
+	clip.passedict = passedict;
+
+	if (type == MOVE_MISSILE)
+	{
+		for (i=0 ; i<3 ; i++)
+		{
+			clip.mins2[i] = -15;
+			clip.maxs2[i] = 15;
+		}
+	}
+	else
+	{
+		VectorCopy (mins, clip.mins2);
+		VectorCopy (maxs, clip.maxs2);
+	}
+	
+// create the bounding box of the entire move
+	SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );
+
+// clip to entities
+	SV_ClipToLinks ( sv_areanodes, &clip );
+
+	return clip.trace;
+}
+
diff --git a/apps/plugins/sdl/progs/quake/world.h b/apps/plugins/sdl/progs/quake/world.h
new file mode 100644
index 0000000..cddb94f
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/world.h
@@ -0,0 +1,78 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// world.h
+
+typedef struct
+{
+	vec3_t	normal;
+	float	dist;
+} plane_t;
+
+typedef struct
+{
+	qboolean	allsolid;	// if true, plane is not valid
+	qboolean	startsolid;	// if true, the initial point was in a solid area
+	qboolean	inopen, inwater;
+	float	fraction;		// time completed, 1.0 = didn't hit anything
+	vec3_t	endpos;			// final position
+	plane_t	plane;			// surface normal at impact
+	edict_t	*ent;			// entity the surface is on
+} trace_t;
+
+
+#define	MOVE_NORMAL		0
+#define	MOVE_NOMONSTERS	1
+#define	MOVE_MISSILE	2
+
+
+void SV_ClearWorld (void);
+// called after the world model has been loaded, before linking any entities
+
+void SV_UnlinkEdict (edict_t *ent);
+// call before removing an entity, and before trying to move one,
+// so it doesn't clip against itself
+// flags ent->v.modified
+
+void SV_LinkEdict (edict_t *ent, qboolean touch_triggers);
+// Needs to be called any time an entity changes origin, mins, maxs, or solid
+// flags ent->v.modified
+// sets ent->v.absmin and ent->v.absmax
+// if touchtriggers, calls prog functions for the intersected triggers
+
+int SV_PointContents (vec3_t p);
+int SV_TruePointContents (vec3_t p);
+// returns the CONTENTS_* value from the world at the given point.
+// does not check any entities at all
+// the non-true version remaps the water current contents to content_water
+
+edict_t	*SV_TestEntityPosition (edict_t *ent);
+
+trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict);
+// mins and maxs are reletive
+
+// if the entire move stays in a solid volume, trace.allsolid will be set
+
+// if the starting point is in a solid, it will be allowed to move out
+// to an open area
+
+// nomonsters is used for line of sight or edge testing, where mosnters
+// shouldn't be considered solid objects
+
+// passedict is explicitly excluded from clipping checks (normally NULL)
diff --git a/apps/plugins/sdl/progs/quake/zone.c b/apps/plugins/sdl/progs/quake/zone.c
new file mode 100644
index 0000000..e30d7f8
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/zone.c
@@ -0,0 +1,935 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+// Z_zone.c
+
+#include "quakedef.h"
+
+#define	DYNAMIC_SIZE	0xc000
+
+#define	ZONEID	0x1d4a11
+#define MINFRAGMENT	64
+
+typedef struct memblock_s
+{
+	int		size;           // including the header and possibly tiny fragments
+	int     tag;            // a tag of 0 is a free block
+	int     id;        		// should be ZONEID
+	struct memblock_s       *next, *prev;
+	int		pad;			// pad to 64 bit boundary
+} memblock_t;
+
+typedef struct
+{
+	int		size;		// total bytes malloced, including header
+	memblock_t	blocklist;		// start / end cap for linked list
+	memblock_t	*rover;
+} memzone_t;
+
+void Cache_FreeLow (int new_low_hunk);
+void Cache_FreeHigh (int new_high_hunk);
+
+
+/*
+==============================================================================
+
+						ZONE MEMORY ALLOCATION
+
+There is never any space between memblocks, and there will never be two
+contiguous free memblocks.
+
+The rover can be left pointing at a non-empty block
+
+The zone calls are pretty much only used for small strings and structures,
+all big things are allocated on the hunk.
+==============================================================================
+*/
+
+memzone_t	*mainzone;
+
+void Z_ClearZone (memzone_t *zone, int size);
+
+
+/*
+========================
+Z_ClearZone
+========================
+*/
+void Z_ClearZone (memzone_t *zone, int size)
+{
+	memblock_t	*block;
+	
+// set the entire zone to one free block
+
+	zone->blocklist.next = zone->blocklist.prev = block =
+		(memblock_t *)( (byte *)zone + sizeof(memzone_t) );
+	zone->blocklist.tag = 1;	// in use block
+	zone->blocklist.id = 0;
+	zone->blocklist.size = 0;
+	zone->rover = block;
+	
+	block->prev = block->next = &zone->blocklist;
+	block->tag = 0;			// free block
+	block->id = ZONEID;
+	block->size = size - sizeof(memzone_t);
+}
+
+
+/*
+========================
+Z_Free
+========================
+*/
+void Z_Free (void *ptr)
+{
+	memblock_t	*block, *other;
+	
+	if (!ptr)
+		Sys_Error ("Z_Free: NULL pointer");
+
+	block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
+	if (block->id != ZONEID)
+		Sys_Error ("Z_Free: freed a pointer without ZONEID");
+	if (block->tag == 0)
+		Sys_Error ("Z_Free: freed a freed pointer");
+
+	block->tag = 0;		// mark as free
+	
+	other = block->prev;
+	if (!other->tag)
+	{	// merge with previous free block
+		other->size += block->size;
+		other->next = block->next;
+		other->next->prev = other;
+		if (block == mainzone->rover)
+			mainzone->rover = other;
+		block = other;
+	}
+	
+	other = block->next;
+	if (!other->tag)
+	{	// merge the next free block onto the end
+		block->size += other->size;
+		block->next = other->next;
+		block->next->prev = block;
+		if (other == mainzone->rover)
+			mainzone->rover = block;
+	}
+}
+
+
+/*
+========================
+Z_Malloc
+========================
+*/
+void *Z_Malloc (int size)
+{
+	void	*buf;
+	
+Z_CheckHeap ();	// DEBUG
+	buf = Z_TagMalloc (size, 1);
+	if (!buf)
+		Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
+	Q_memset (buf, 0, size);
+
+	return buf;
+}
+
+void *Z_TagMalloc (int size, int tag)
+{
+	int		extra;
+	memblock_t	*start, *rover, *new, *base;
+
+	if (!tag)
+		Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
+
+//
+// scan through the block list looking for the first free block
+// of sufficient size
+//
+	size += sizeof(memblock_t);	// account for size of block header
+	size += 4;					// space for memory trash tester
+	size = (size + 7) & ~7;		// align to 8-byte boundary
+	
+	base = rover = mainzone->rover;
+	start = base->prev;
+	
+	do
+	{
+		if (rover == start)	// scaned all the way around the list
+			return NULL;
+		if (rover->tag)
+			base = rover = rover->next;
+		else
+			rover = rover->next;
+	} while (base->tag || base->size < size);
+	
+//
+// found a block big enough
+//
+	extra = base->size - size;
+	if (extra >  MINFRAGMENT)
+	{	// there will be a free fragment after the allocated block
+		new = (memblock_t *) ((byte *)base + size );
+		new->size = extra;
+		new->tag = 0;			// free block
+		new->prev = base;
+		new->id = ZONEID;
+		new->next = base->next;
+		new->next->prev = new;
+		base->next = new;
+		base->size = size;
+	}
+	
+	base->tag = tag;				// no longer a free block
+	
+	mainzone->rover = base->next;	// next allocation will start looking here
+	
+	base->id = ZONEID;
+
+// marker for memory trash testing
+	*(int *)((byte *)base + base->size - 4) = ZONEID;
+
+	return (void *) ((byte *)base + sizeof(memblock_t));
+}
+
+
+/*
+========================
+Z_Print
+========================
+*/
+void Z_Print (memzone_t *zone)
+{
+	memblock_t	*block;
+	
+	Con_Printf ("zone size: %i  location: %p\n",mainzone->size,mainzone);
+	
+	for (block = zone->blocklist.next ; ; block = block->next)
+	{
+		Con_Printf ("block:%p    size:%7i    tag:%3i\n",
+			block, block->size, block->tag);
+		
+		if (block->next == &zone->blocklist)
+			break;			// all blocks have been hit	
+		if ( (byte *)block + block->size != (byte *)block->next)
+			Con_Printf ("ERROR: block size does not touch the next block\n");
+		if ( block->next->prev != block)
+			Con_Printf ("ERROR: next block doesn't have proper back link\n");
+		if (!block->tag && !block->next->tag)
+			Con_Printf ("ERROR: two consecutive free blocks\n");
+	}
+}
+
+
+/*
+========================
+Z_CheckHeap
+========================
+*/
+void Z_CheckHeap (void)
+{
+	memblock_t	*block;
+	
+	for (block = mainzone->blocklist.next ; ; block = block->next)
+	{
+		if (block->next == &mainzone->blocklist)
+			break;			// all blocks have been hit	
+		if ( (byte *)block + block->size != (byte *)block->next)
+			Sys_Error ("Z_CheckHeap: block size does not touch the next block\n");
+		if ( block->next->prev != block)
+			Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
+		if (!block->tag && !block->next->tag)
+			Sys_Error ("Z_CheckHeap: two consecutive free blocks\n");
+	}
+}
+
+//============================================================================
+
+#define	HUNK_SENTINAL	0x1df001ed
+
+typedef struct
+{
+	int		sentinal;
+	int		size;		// including sizeof(hunk_t), -1 = not allocated
+	char	name[8];
+} hunk_t;
+
+byte	*hunk_base;
+int		hunk_size;
+
+int		hunk_low_used;
+int		hunk_high_used;
+
+qboolean	hunk_tempactive;
+int		hunk_tempmark;
+
+void R_FreeTextures (void);
+
+/*
+==============
+Hunk_Check
+
+Run consistancy and sentinal trahing checks
+==============
+*/
+void Hunk_Check (void)
+{
+	hunk_t	*h;
+	
+	for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; )
+	{
+		if (h->sentinal != HUNK_SENTINAL)
+			Sys_Error ("Hunk_Check: trahsed sentinal");
+		if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
+			Sys_Error ("Hunk_Check: bad size");
+		h = (hunk_t *)((byte *)h+h->size);
+	}
+}
+
+/*
+==============
+Hunk_Print
+
+If "all" is specified, every single allocation is printed.
+Otherwise, allocations with the same name will be totaled up before printing.
+==============
+*/
+void Hunk_Print (qboolean all)
+{
+	hunk_t	*h, *next, *endlow, *starthigh, *endhigh;
+	int		count, sum;
+	int		totalblocks;
+	char	name[9];
+
+	name[8] = 0;
+	count = 0;
+	sum = 0;
+	totalblocks = 0;
+	
+	h = (hunk_t *)hunk_base;
+	endlow = (hunk_t *)(hunk_base + hunk_low_used);
+	starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
+	endhigh = (hunk_t *)(hunk_base + hunk_size);
+
+	Con_Printf ("          :%8i total hunk size\n", hunk_size);
+	Con_Printf ("-------------------------\n");
+
+	while (1)
+	{
+	//
+	// skip to the high hunk if done with low hunk
+	//
+		if ( h == endlow )
+		{
+			Con_Printf ("-------------------------\n");
+			Con_Printf ("          :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
+			Con_Printf ("-------------------------\n");
+			h = starthigh;
+		}
+		
+	//
+	// if totally done, break
+	//
+		if ( h == endhigh )
+			break;
+
+	//
+	// run consistancy checks
+	//
+		if (h->sentinal != HUNK_SENTINAL)
+			Sys_Error ("Hunk_Check: trahsed sentinal");
+		if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size)
+			Sys_Error ("Hunk_Check: bad size");
+			
+		next = (hunk_t *)((byte *)h+h->size);
+		count++;
+		totalblocks++;
+		sum += h->size;
+
+	//
+	// print the single block
+	//
+		memcpy (name, h->name, 8);
+		if (all)
+			Con_Printf ("%8p :%8i %8s\n",h, h->size, name);
+			
+	//
+	// print the total
+	//
+		if (next == endlow || next == endhigh || 
+		strncmp (h->name, next->name, 8) )
+		{
+			if (!all)
+				Con_Printf ("          :%8i %8s (TOTAL)\n",sum, name);
+			count = 0;
+			sum = 0;
+		}
+
+		h = next;
+	}
+
+	Con_Printf ("-------------------------\n");
+	Con_Printf ("%8i total blocks\n", totalblocks);
+	
+}
+
+/*
+===================
+Hunk_AllocName
+===================
+*/
+void *Hunk_AllocName (int size, char *name)
+{
+	hunk_t	*h;
+	
+#ifdef PARANOID
+	Hunk_Check ();
+#endif
+
+	if (size < 0)
+		Sys_Error ("Hunk_Alloc: bad size: %i", size);
+		
+	size = sizeof(hunk_t) + ((size+15)&~15);
+	
+	if (hunk_size - hunk_low_used - hunk_high_used < size)
+		Sys_Error ("Hunk_Alloc: failed on %i bytes",size);
+	
+	h = (hunk_t *)(hunk_base + hunk_low_used);
+	hunk_low_used += size;
+
+	Cache_FreeLow (hunk_low_used);
+
+	memset (h, 0, size);
+	
+	h->size = size;
+	h->sentinal = HUNK_SENTINAL;
+	Q_strncpy (h->name, name, 8);
+	
+	return (void *)(h+1);
+}
+
+/*
+===================
+Hunk_Alloc
+===================
+*/
+void *Hunk_Alloc (int size)
+{
+	return Hunk_AllocName (size, "unknown");
+}
+
+int	Hunk_LowMark (void)
+{
+	return hunk_low_used;
+}
+
+void Hunk_FreeToLowMark (int mark)
+{
+	if (mark < 0 || mark > hunk_low_used)
+		Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
+	memset (hunk_base + mark, 0, hunk_low_used - mark);
+	hunk_low_used = mark;
+}
+
+int	Hunk_HighMark (void)
+{
+	if (hunk_tempactive)
+	{
+		hunk_tempactive = false;
+		Hunk_FreeToHighMark (hunk_tempmark);
+	}
+
+	return hunk_high_used;
+}
+
+void Hunk_FreeToHighMark (int mark)
+{
+	if (hunk_tempactive)
+	{
+		hunk_tempactive = false;
+		Hunk_FreeToHighMark (hunk_tempmark);
+	}
+	if (mark < 0 || mark > hunk_high_used)
+		Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
+	memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
+	hunk_high_used = mark;
+}
+
+
+/*
+===================
+Hunk_HighAllocName
+===================
+*/
+void *Hunk_HighAllocName (int size, char *name)
+{
+	hunk_t	*h;
+
+	if (size < 0)
+		Sys_Error ("Hunk_HighAllocName: bad size: %i", size);
+
+	if (hunk_tempactive)
+	{
+		Hunk_FreeToHighMark (hunk_tempmark);
+		hunk_tempactive = false;
+	}
+
+#ifdef PARANOID
+	Hunk_Check ();
+#endif
+
+	size = sizeof(hunk_t) + ((size+15)&~15);
+
+	if (hunk_size - hunk_low_used - hunk_high_used < size)
+	{
+		Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size);
+		return NULL;
+	}
+
+	hunk_high_used += size;
+	Cache_FreeHigh (hunk_high_used);
+
+	h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
+
+	memset (h, 0, size);
+	h->size = size;
+	h->sentinal = HUNK_SENTINAL;
+	Q_strncpy (h->name, name, 8);
+
+	return (void *)(h+1);
+}
+
+
+/*
+=================
+Hunk_TempAlloc
+
+Return space from the top of the hunk
+=================
+*/
+void *Hunk_TempAlloc (int size)
+{
+	void	*buf;
+
+	size = (size+15)&~15;
+	
+	if (hunk_tempactive)
+	{
+		Hunk_FreeToHighMark (hunk_tempmark);
+		hunk_tempactive = false;
+	}
+	
+	hunk_tempmark = Hunk_HighMark ();
+
+	buf = Hunk_HighAllocName (size, "temp");
+
+	hunk_tempactive = true;
+
+	return buf;
+}
+
+/*
+===============================================================================
+
+CACHE MEMORY
+
+===============================================================================
+*/
+
+typedef struct cache_system_s
+{
+	int						size;		// including this header
+	cache_user_t			*user;
+	char					name[16];
+	struct cache_system_s	*prev, *next;
+	struct cache_system_s	*lru_prev, *lru_next;	// for LRU flushing	
+} cache_system_t;
+
+cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
+
+cache_system_t	cache_head;
+
+/*
+===========
+Cache_Move
+===========
+*/
+void Cache_Move ( cache_system_t *c)
+{
+	cache_system_t		*new;
+
+// we are clearing up space at the bottom, so only allocate it late
+	new = Cache_TryAlloc (c->size, true);
+	if (new)
+	{
+//		Con_Printf ("cache_move ok\n");
+
+		Q_memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) );
+		new->user = c->user;
+		Q_memcpy (new->name, c->name, sizeof(new->name));
+		Cache_Free (c->user);
+		new->user->data = (void *)(new+1);
+	}
+	else
+	{
+//		Con_Printf ("cache_move failed\n");
+
+		Cache_Free (c->user);		// tough luck...
+	}
+}
+
+/*
+============
+Cache_FreeLow
+
+Throw things out until the hunk can be expanded to the given point
+============
+*/
+void Cache_FreeLow (int new_low_hunk)
+{
+	cache_system_t	*c;
+	
+	while (1)
+	{
+		c = cache_head.next;
+		if (c == &cache_head)
+			return;		// nothing in cache at all
+		if ((byte *)c >= hunk_base + new_low_hunk)
+			return;		// there is space to grow the hunk
+		Cache_Move ( c );	// reclaim the space
+	}
+}
+
+/*
+============
+Cache_FreeHigh
+
+Throw things out until the hunk can be expanded to the given point
+============
+*/
+void Cache_FreeHigh (int new_high_hunk)
+{
+	cache_system_t	*c, *prev;
+	
+	prev = NULL;
+	while (1)
+	{
+		c = cache_head.prev;
+		if (c == &cache_head)
+			return;		// nothing in cache at all
+		if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
+			return;		// there is space to grow the hunk
+		if (c == prev)
+			Cache_Free (c->user);	// didn't move out of the way
+		else
+		{
+			Cache_Move (c);	// try to move it
+			prev = c;
+		}
+	}
+}
+
+void Cache_UnlinkLRU (cache_system_t *cs)
+{
+	if (!cs->lru_next || !cs->lru_prev)
+		Sys_Error ("Cache_UnlinkLRU: NULL link");
+
+	cs->lru_next->lru_prev = cs->lru_prev;
+	cs->lru_prev->lru_next = cs->lru_next;
+	
+	cs->lru_prev = cs->lru_next = NULL;
+}
+
+void Cache_MakeLRU (cache_system_t *cs)
+{
+	if (cs->lru_next || cs->lru_prev)
+		Sys_Error ("Cache_MakeLRU: active link");
+
+	cache_head.lru_next->lru_prev = cs;
+	cs->lru_next = cache_head.lru_next;
+	cs->lru_prev = &cache_head;
+	cache_head.lru_next = cs;
+}
+
+/*
+============
+Cache_TryAlloc
+
+Looks for a free block of memory between the high and low hunk marks
+Size should already include the header and padding
+============
+*/
+cache_system_t *Cache_TryAlloc (int size, qboolean nobottom)
+{
+	cache_system_t	*cs, *new;
+	
+// is the cache completely empty?
+
+	if (!nobottom && cache_head.prev == &cache_head)
+	{
+		if (hunk_size - hunk_high_used - hunk_low_used < size)
+			Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
+
+		new = (cache_system_t *) (hunk_base + hunk_low_used);
+		memset (new, 0, sizeof(*new));
+		new->size = size;
+
+		cache_head.prev = cache_head.next = new;
+		new->prev = new->next = &cache_head;
+		
+		Cache_MakeLRU (new);
+		return new;
+	}
+	
+// search from the bottom up for space
+
+	new = (cache_system_t *) (hunk_base + hunk_low_used);
+	cs = cache_head.next;
+	
+	do
+	{
+		if (!nobottom || cs != cache_head.next)
+		{
+			if ( (byte *)cs - (byte *)new >= size)
+			{	// found space
+				memset (new, 0, sizeof(*new));
+				new->size = size;
+				
+				new->next = cs;
+				new->prev = cs->prev;
+				cs->prev->next = new;
+				cs->prev = new;
+				
+				Cache_MakeLRU (new);
+	
+				return new;
+			}
+		}
+
+	// continue looking		
+		new = (cache_system_t *)((byte *)cs + cs->size);
+		cs = cs->next;
+
+	} while (cs != &cache_head);
+	
+// try to allocate one at the very end
+	if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size)
+	{
+		memset (new, 0, sizeof(*new));
+		new->size = size;
+		
+		new->next = &cache_head;
+		new->prev = cache_head.prev;
+		cache_head.prev->next = new;
+		cache_head.prev = new;
+		
+		Cache_MakeLRU (new);
+
+		return new;
+	}
+	
+	return NULL;		// couldn't allocate
+}
+
+/*
+============
+Cache_Flush
+
+Throw everything out, so new data will be demand cached
+============
+*/
+void Cache_Flush (void)
+{
+	while (cache_head.next != &cache_head)
+		Cache_Free ( cache_head.next->user );	// reclaim the space
+}
+
+
+/*
+============
+Cache_Print
+
+============
+*/
+void Cache_Print (void)
+{
+	cache_system_t	*cd;
+
+	for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
+	{
+		Con_Printf ("%8i : %s\n", cd->size, cd->name);
+	}
+}
+
+/*
+============
+Cache_Report
+
+============
+*/
+void Cache_Report (void)
+{
+	Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
+}
+
+/*
+============
+Cache_Compact
+
+============
+*/
+void Cache_Compact (void)
+{
+}
+
+/*
+============
+Cache_Init
+
+============
+*/
+void Cache_Init (void)
+{
+	cache_head.next = cache_head.prev = &cache_head;
+	cache_head.lru_next = cache_head.lru_prev = &cache_head;
+
+	Cmd_AddCommand ("flush", Cache_Flush);
+}
+
+/*
+==============
+Cache_Free
+
+Frees the memory and removes it from the LRU list
+==============
+*/
+void Cache_Free (cache_user_t *c)
+{
+	cache_system_t	*cs;
+
+	if (!c->data)
+		Sys_Error ("Cache_Free: not allocated");
+
+	cs = ((cache_system_t *)c->data) - 1;
+
+	cs->prev->next = cs->next;
+	cs->next->prev = cs->prev;
+	cs->next = cs->prev = NULL;
+
+	c->data = NULL;
+
+	Cache_UnlinkLRU (cs);
+}
+
+
+
+/*
+==============
+Cache_Check
+==============
+*/
+void *Cache_Check (cache_user_t *c)
+{
+	cache_system_t	*cs;
+
+	if (!c->data)
+		return NULL;
+
+	cs = ((cache_system_t *)c->data) - 1;
+
+// move to head of LRU
+	Cache_UnlinkLRU (cs);
+	Cache_MakeLRU (cs);
+	
+	return c->data;
+}
+
+
+/*
+==============
+Cache_Alloc
+==============
+*/
+void *Cache_Alloc (cache_user_t *c, int size, char *name)
+{
+	cache_system_t	*cs;
+
+	if (c->data)
+		Sys_Error ("Cache_Alloc: allready allocated");
+	
+	if (size <= 0)
+		Sys_Error ("Cache_Alloc: size %i", size);
+
+	size = (size + sizeof(cache_system_t) + 15) & ~15;
+
+// find memory for it	
+	while (1)
+	{
+		cs = Cache_TryAlloc (size, false);
+		if (cs)
+		{
+			strncpy (cs->name, name, sizeof(cs->name)-1);
+			c->data = (void *)(cs+1);
+			cs->user = c;
+			break;
+		}
+	
+	// free the least recently used cahedat
+		if (cache_head.lru_prev == &cache_head)
+			Sys_Error ("Cache_Alloc: out of memory");
+													// not enough memory at all
+		Cache_Free ( cache_head.lru_prev->user );
+	} 
+	
+	return Cache_Check (c);
+}
+
+//============================================================================
+
+
+/*
+========================
+Memory_Init
+========================
+*/
+void Memory_Init (void *buf, int size)
+{
+	int p;
+	int zonesize = DYNAMIC_SIZE;
+
+	hunk_base = buf;
+	hunk_size = size;
+	hunk_low_used = 0;
+	hunk_high_used = 0;
+	
+	Cache_Init ();
+	p = COM_CheckParm ("-zone");
+	if (p)
+	{
+		if (p < com_argc-1)
+			zonesize = Q_atoi (com_argv[p+1]) * 1024;
+		else
+			Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
+	}
+	mainzone = Hunk_AllocName (zonesize, "zone" );
+	Z_ClearZone (mainzone, zonesize);
+}
+
diff --git a/apps/plugins/sdl/progs/quake/zone.h b/apps/plugins/sdl/progs/quake/zone.h
new file mode 100644
index 0000000..7655ad9
--- /dev/null
+++ b/apps/plugins/sdl/progs/quake/zone.h
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+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.
+
+*/
+/*
+ memory allocation
+
+
+H_??? The hunk manages the entire memory block given to quake.  It must be
+contiguous.  Memory can be allocated from either the low or high end in a
+stack fashion.  The only way memory is released is by resetting one of the
+pointers.
+
+Hunk allocations should be given a name, so the Hunk_Print () function
+can display usage.
+
+Hunk allocations are guaranteed to be 16 byte aligned.
+
+The video buffers are allocated high to avoid leaving a hole underneath
+server allocations when changing to a higher video mode.
+
+
+Z_??? Zone memory functions used for small, dynamic allocations like text
+strings from command input.  There is only about 48K for it, allocated at
+the very bottom of the hunk.
+
+Cache_??? Cache memory is for objects that can be dynamically loaded and
+can usefully stay persistant between levels.  The size of the cache
+fluctuates from level to level.
+
+To allocate a cachable object
+
+
+Temp_??? Temp memory is used for file loading and surface caching.  The size
+of the cache memory is adjusted so that there is a minimum of 512k remaining
+for temp memory.
+
+
+------ Top of Memory -------
+
+high hunk allocations
+
+<--- high hunk reset point held by vid
+
+video buffer
+
+z buffer
+
+surface cache
+
+<--- high hunk used
+
+cachable memory
+
+<--- low hunk used
+
+client and server low hunk allocations
+
+<-- low hunk reset point held by host
+
+startup hunk allocations
+
+Zone block
+
+----- Bottom of Memory -----
+
+
+
+*/
+
+void Memory_Init (void *buf, int size);
+
+void Z_Free (void *ptr);
+void *Z_Malloc (int size);			// returns 0 filled memory
+void *Z_TagMalloc (int size, int tag);
+
+void Z_DumpHeap (void);
+void Z_CheckHeap (void);
+int Z_FreeMemory (void);
+
+void *Hunk_Alloc (int size);		// returns 0 filled memory
+void *Hunk_AllocName (int size, char *name);
+
+void *Hunk_HighAllocName (int size, char *name);
+
+int	Hunk_LowMark (void);
+void Hunk_FreeToLowMark (int mark);
+
+int	Hunk_HighMark (void);
+void Hunk_FreeToHighMark (int mark);
+
+void *Hunk_TempAlloc (int size);
+
+void Hunk_Check (void);
+
+typedef struct cache_user_s
+{
+	void	*data;
+} cache_user_t;
+
+void Cache_Flush (void);
+
+void *Cache_Check (cache_user_t *c);
+// returns the cached data, and moves to the head of the LRU list
+// if present, otherwise returns NULL
+
+void Cache_Free (cache_user_t *c);
+
+void *Cache_Alloc (cache_user_t *c, int size, char *name);
+// Returns NULL if all purgable data was tossed and there still
+// wasn't enough room.
+
+void Cache_Report (void);
+
+
+
diff --git a/apps/plugins/sdl/sdl.make b/apps/plugins/sdl/sdl.make
index 492284e..a00948d 100644
--- a/apps/plugins/sdl/sdl.make
+++ b/apps/plugins/sdl/sdl.make
@@ -13,34 +13,46 @@
 SDL_SRC := $(call preprocess, $(SDL_SRCDIR)/SOURCES)
 DUKE3D_SRC := $(call preprocess, $(SDL_SRCDIR)/SOURCES.duke)
 WOLF3D_SRC := $(call preprocess, $(SDL_SRCDIR)/SOURCES.wolf)
+QUAKE_SRC := $(call preprocess, $(SDL_SRCDIR)/SOURCES.quake)
 
 SDL_OBJ := $(call c2obj, $(SDL_SRC))
 DUKE3D_OBJ = $(call c2obj, $(DUKE3D_SRC))
 WOLF3D_OBJ = $(call c2obj, $(WOLF3D_SRC))
+QUAKE_OBJ = $(call c2obj, $(QUAKE_SRC))
 
 # add source files to OTHER_SRC to get automatic dependencies
-OTHER_SRC += $(SDL_SRC) $(DUKE3D_SRC) $(WOLF3D_SRC)
+OTHER_SRC += $(SDL_SRC) $(DUKE3D_SRC) $(WOLF3D_SRC) $(QUAKE_SRC)
 OTHER_INC += -I$(SDL_SRCDIR)/include
 
 # include comes first because of possible system SDL headers taking
 # precedence
-SDLFLAGS = -I$(SDL_SRCDIR)/include $(filter-out -O%,$(PLUGINFLAGS))	\
--O3 -Wno-unused-parameter -Xpreprocessor -Wno-undef -Wcast-align -w
+# some of these are for Quake only
+SDLFLAGS = -I$(SDL_SRCDIR)/include $(filter-out -O%,$(PLUGINFLAGS))		\
+-O3 -Wno-unused-parameter -Xpreprocessor -Wno-undef -Wcast-align	\
+-ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations	\
+-D_GNU_SOURCE=1 -D_REENTRANT -DSDL -DELF -w # disable all warnings
 
+# use FPU on ARMv6
 ifeq ($(ARCH_VERSION),6)
     SDLFLAGS += -mfloat-abi=softfp
 endif
 
 ifndef APP_TYPE
     ### no target has a big enough plugin buffer
-    ROCKS += $(SDL_OBJDIR)/duke3d.ovl $(SDL_OBJDIR)/wolf3d.ovl
+    ROCKS += $(SDL_OBJDIR)/duke3d.ovl
+    ROCKS += $(SDL_OBJDIR)/wolf3d.ovl
+    ROCKS += $(SDL_OBJDIR)/quake.ovl
+
     DUKE3D_OUTLDS = $(SDL_OBJDIR)/duke3d.link
     WOLF3D_OUTLDS = $(SDL_OBJDIR)/wolf3d.link
+    QUAKE_OUTLDS = $(SDL_OBJDIR)/quake.link
+
     SDL_OVLFLAGS = -Wl,--gc-sections -Wl,-Map,$(basename $@).map
 else
     ### simulator
     ROCKS += $(SDL_OBJDIR)/duke3d.rock
     ROCKS += $(SDL_OBJDIR)/wolf3d.rock
+    ROCKS += $(SDL_OBJDIR)/quake.rock
 endif
 
 # Duke3D
@@ -77,6 +89,26 @@
 		-lgcc  -T$(WOLF3D_OUTLDS) $(SDL_OVLFLAGS)
 	$(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@)
 
+# Quake
+###
+
+$(SDL_OBJDIR)/quake.rock: $(SDL_OBJ) $(QUAKE_OBJ) $(TLSFLIB)
+
+$(SDL_OBJDIR)/quake.refmap: $(SDL_OBJ) $(QUAKE_OBJ) $(TLSFLIB)
+
+$(QUAKE_OUTLDS): $(PLUGIN_LDS) $(SDL_OBJDIR)/quake.refmap
+	$(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DOVERLAY_OFFSET=$(shell \
+		$(TOOLSDIR)/ovl_offset.pl $(SDL_OBJDIR)/quake.refmap))
+
+$(SDL_OBJDIR)/quake.ovl: $(SDL_OBJ) $(QUAKE_OBJ) $(TLSFLIB) $(QUAKE_OUTLDS)
+	$(SILENT)$(CC) $(PLUGINFLAGS) -o $(basename $@).elf \
+		$(filter %.o, $^) \
+		$(filter %.a, $+) \
+		-lgcc -T$(QUAKE_OUTLDS) $(SDL_OVLFLAGS)
+	$(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@)
+
+###
+
 # common
 
 $(SDL_OBJDIR)/%.o: $(SDL_SRCDIR)/%.c $(SDL_SRCDIR)/sdl.make $(BUILDDIR)/sysfont.h
diff --git a/apps/plugins/sdl/src/audio/SDL_audio.c b/apps/plugins/sdl/src/audio/SDL_audio.c
index 9c0feed..f2a2e59 100644
--- a/apps/plugins/sdl/src/audio/SDL_audio.c
+++ b/apps/plugins/sdl/src/audio/SDL_audio.c
@@ -204,6 +204,7 @@
 
 		/* Convert the audio if necessary */
 		if ( audio->convert.needed ) {
+                    LOGF("RB AUDIO: converting audio. Will be slow!");
 			SDL_ConvertAudio(&audio->convert);
 			stream = audio->GetAudioBuf(audio);
 			if ( stream == NULL ) {
diff --git a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
index 7833430..cb72687 100644
--- a/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
+++ b/apps/plugins/sdl/src/audio/rockbox/SDL_rockboxaudio.c
@@ -225,12 +225,29 @@
     rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
 }
 
+static bool freq_ok(unsigned int freq)
+{
+    for(int i = 0; i < SAMPR_NUM_FREQ; i++)
+    {
+        if(rb->hw_freq_sampr[i] == freq)
+            return true;
+    }
+    return false;
+}
+
 static int ROCKBOXAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
 {
     /* change to our format */
     spec->format = AUDIO_S16SYS;
     spec->channels = 2;
-    spec->freq = RB_SAMPR;
+
+    if(!freq_ok(spec->freq))
+    {
+        rb->splashf(HZ, "Warning: Unsupported audio rate. Defaulting to %d Hz", RB_SAMPR);
+
+        // switch to default
+        spec->freq = RB_SAMPR;
+    }
 
     /* we've changed it */
     SDL_CalculateAudioSpec(spec);
diff --git a/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c b/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
index f9dc877..b9bd873 100644
--- a/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
+++ b/apps/plugins/sdl/src/thread/rockbox/SDL_systhread.c
@@ -51,13 +51,13 @@
     static int threadnum = 0;
     snprintf(names[threadnum], 16, "sdl_%d", threadnum);
 
-    while(global_args) rb->yield(); /* busy wait */
+    while(global_args) rb->yield(); /* busy wait, pray that this works */
 
     global_args = args;
 
     thread->handle = rb->create_thread(rbsdl_runthread, stacks[threadnum], DEFAULT_STACK_SIZE,
                                        0, names[threadnum] /* collisions allowed? */
-                                       IF_PRIO(, PRIORITY_USER_INTERFACE)
+                                       IF_PRIO(, PRIORITY_BUFFERING) // this is used for sound mixing
                                        IF_COP(, CPU));
 
     threadnum++;
diff --git a/apps/plugins/sdl/src/timer/rockbox/SDL_systimer.c b/apps/plugins/sdl/src/timer/rockbox/SDL_systimer.c
index f4e8862..8da8455 100644
--- a/apps/plugins/sdl/src/timer/rockbox/SDL_systimer.c
+++ b/apps/plugins/sdl/src/timer/rockbox/SDL_systimer.c
@@ -28,6 +28,7 @@
 
 #include "plugin.h"
 
+/* color because greylib will use timer otherwise */
 #if defined(HAVE_LCD_COLOR) && !defined(SIMULATOR) && !defined(RB_PROFILE)
 #define USE_TIMER
 #endif
diff --git a/apps/plugins/sdl/src/video/rockbox/SDL_rockboxvideo.c b/apps/plugins/sdl/src/video/rockbox/SDL_rockboxvideo.c
index 58de157..3a12d98 100644
--- a/apps/plugins/sdl/src/video/rockbox/SDL_rockboxvideo.c
+++ b/apps/plugins/sdl/src/video/rockbox/SDL_rockboxvideo.c
@@ -769,8 +769,8 @@
             /* FIXME: this won't work for rotated screen or overlapping rects */
             flip_pixels(rects[i].x, rects[i].y, rects[i].w, rects[i].h);
 #endif
+            rb->lcd_update_rect(rects[i].x, rects[i].y, rects[i].w, rects[i].h);
         } /* for */
-        rb->lcd_update();
     } /* if */
 }
 
diff --git a/apps/plugins/sdl/wrappers.c b/apps/plugins/sdl/wrappers.c
index ee512dd..02e9db1 100644
--- a/apps/plugins/sdl/wrappers.c
+++ b/apps/plugins/sdl/wrappers.c
@@ -165,9 +165,80 @@
   return number;
 }
 
-double atof_wrapper(const char *str)
+// stolen from Quake
+float atof_wrapper (char *str)
 {
-    return rb_strtod(str, NULL);
+        double                  val;
+        int             sign;
+        int             c;
+        int             decimal, total;
+
+        if (*str == '-')
+        {
+                sign = -1;
+                str++;
+        }
+        else
+                sign = 1;
+
+        val = 0;
+
+//
+// check for hex
+//
+        if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
+        {
+                str += 2;
+                while (1)
+                {
+                        c = *str++;
+                        if (c >= '0' && c <= '9')
+                                val = (val*16) + c - '0';
+                        else if (c >= 'a' && c <= 'f')
+                                val = (val*16) + c - 'a' + 10;
+                        else if (c >= 'A' && c <= 'F')
+                                val = (val*16) + c - 'A' + 10;
+                        else
+                                return val*sign;
+                }
+        }
+
+//
+// check for character
+//
+        if (str[0] == '\'')
+        {
+                return sign * str[1];
+        }
+
+//
+// assume decimal
+//
+        decimal = -1;
+        total = 0;
+        while (1)
+        {
+                c = *str++;
+                if (c == '.')
+                {
+                        decimal = total;
+                        continue;
+                }
+                if (c <'0' || c > '9')
+                        break;
+                val = val*10 + c - '0';
+                total++;
+        }
+
+        if (decimal == -1)
+                return val*sign;
+        while (total > decimal)
+        {
+                val /= 10;
+                total--;
+        }
+
+        return val*sign;
 }
 
 double sin_wrapper(double rads)
@@ -217,6 +288,51 @@
     return sin_wrapper(f)/cos_wrapper(f);
 }
 
+// Total hack. Supports only format strings of the form %Cc, where C
+// is a format specifier and c is a delimiter. Surprisingly, most
+// format strings aren't that complicated to need a real fscanf. This
+// is just enough to make Quake run!
+int fscanf_wrapper(FILE *f, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+
+    if(strlen(fmt) != 3)
+        return 0; // not implemented
+
+    if(fmt[0] != '%')
+        return 0; // not implemented
+
+    char format = fmt[1];
+    char delim = fmt[2];
+
+    // extract argument
+    char buf[1024];
+    char *ptr = (format == 's' ? va_arg(ap, char*) : buf);
+    int c;
+    do {
+        c = fgetc(f);
+        *ptr++ = c;
+    } while(c != delim && c != EOF);
+
+    // overwrite delimiter
+    *(ptr-1) = 0;
+
+    //rb->splashf(HZ, "got argument %s, %s\n", fmt, buf);
+
+    switch(format)
+    {
+    case 'i':
+        *va_arg(ap, int*) = atoi(buf);
+        break;
+    case 'f':
+        *va_arg(ap, float*) = atof(buf);
+        break;
+    }
+    return 1;
+}
+
 /* stolen from doom */
 // Here is a hacked up printf command to get the output from the game.
 int printf_wrapper(const char *fmt, ...)
@@ -272,7 +388,7 @@
 
 char *strcpy_wrapper(char *dest, const char *src)
 {
-    rb->strlcpy(dest, src, 999);
+    strlcpy(dest, src, 999);
     return dest;
 }
 
diff --git a/docs/CREDITS b/docs/CREDITS
index 09be170..cef7a2e 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -711,3 +711,4 @@
 The Chocolate Duke Nukem 3D team
 The Wolfenstein 3D team
 The MAME team
+The Quake team
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index 5b13829..c07d106 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -76,6 +76,8 @@
 
 \opt{lcd_bitmap}{\input{plugins/pong.tex}}
 
+\opt{lcd_color}{\nopt{lowmem,iaudiox5,iriverh300}{\input{plugins/quake.tex}}}
+
 \opt{lcd_bitmap}{\input{plugins/reversi.tex}}
 
 \opt{lcd_bitmap}{\input{plugins/robotfindskitten.tex}}
diff --git a/manual/plugins/quake.tex b/manual/plugins/quake.tex
new file mode 100644
index 0000000..e7a9367
--- /dev/null
+++ b/manual/plugins/quake.tex
@@ -0,0 +1,15 @@
+\subsection{Quake}
+
+This is id Software's \emph{Quake}, first released in 1996. The game
+features a Gothic atmosphere and full 3D graphics.
+
+\subsubsection{Installation}
+Copy the game data files into \fname{/.rockbox/quake} on your
+device. There should be an \fname{id1/} directory with a {.pak}
+file. The shareware version is known to work.
+
+\subsubsection{Configuration}
+It is possible to customize the game controls through the
+\emph{Options > Customize Controls} menu. Currently a limitation
+exists which prevents one key from being bound to more than one
+function.