shithub: qk1

Download patch

ref: 29d6a62b7536f0e023f588076cfc7b7175617c71
parent: 373a5aadf3814e5c433787d419a218a9061f5c3d
author: qwx <>
date: Wed Dec 12 01:20:24 EST 2018

qw: align video, sound and input code with qk1

note that sound will cut up by default unless devaudio delay is increased, or
cl_maxfps or rate are adjusted for a maximum framerate of 72 (hardcoded).
the latter is recommended.  no idea why one would want a lower framerate.

video code also essentially assumes that the screen is always updated in its
entirety.  even if this is not always true, it matters only ingame, where it
(currently) is.

--- a/qw/cd.c
+++ /dev/null
@@ -1,393 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <stdio.h>
-#include "quakedef.h"
-
-static qboolean cdValid;
-static qboolean	playing;
-static qboolean	wasPlaying;
-static qboolean	initialized;
-static qboolean	enabled = true;
-static qboolean playLooping;
-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)
-{
-	cdValid = false;
-	return -1;
-
-/*
-	struct cdrom_tochdr tochdr;
-
-	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)
-{
-	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;
-	}
-
-	USED(looping);
-/*
-	struct cdrom_tocentry entry;
-	struct cdrom_ti ti;
-
-	// 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 (cistrcmp(command, "on") == 0)
-	{
-		enabled = true;
-		return;
-	}
-
-	if (cistrcmp(command, "off") == 0)
-	{
-		if (playing)
-			CDAudio_Stop();
-		enabled = false;
-		return;
-	}
-
-	if (cistrcmp(command, "reset") == 0)
-	{
-		enabled = true;
-		if (playing)
-			CDAudio_Stop();
-		for (n = 0; n < 100; n++)
-			remap[n] = n;
-		CDAudio_GetAudioDiskInfo();
-		return;
-	}
-
-	if (cistrcmp(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 (cistrcmp(command, "close") == 0)
-	{
-		CDAudio_CloseDoor();
-		return;
-	}
-
-	if (!cdValid)
-	{
-		CDAudio_GetAudioDiskInfo();
-		if (!cdValid)
-		{
-			Con_Printf("No CD in player.\n");
-			return;
-		}
-	}
-
-	if (cistrcmp(command, "play") == 0)
-	{
-		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false);
-		return;
-	}
-
-	if (cistrcmp(command, "loop") == 0)
-	{
-		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true);
-		return;
-	}
-
-	if (cistrcmp(command, "stop") == 0)
-	{
-		CDAudio_Stop();
-		return;
-	}
-
-	if (cistrcmp(command, "pause") == 0)
-	{
-		CDAudio_Pause();
-		return;
-	}
-
-	if (cistrcmp(command, "resume") == 0)
-	{
-		CDAudio_Resume();
-		return;
-	}
-
-	if (cistrcmp(command, "eject") == 0)
-	{
-		if (playing)
-			CDAudio_Stop();
-		CDAudio_Eject();
-		cdValid = false;
-		return;
-	}
-
-	if (cistrcmp(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)
-{
-	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 ();
-		}
-	}
-
-/*
-	struct cdrom_subchnl subchnl;
-	static time_t lastchk;
-	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 (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, OREAD)) == -1) {
-		fprint(2, "CDAudio_Init:open: %r\n");
-		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;
-}
--- a/qw/cdaudio.h
+++ b/qw/cdaudio.h
@@ -1,7 +1,6 @@
-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);
+int	initcd(void);
+void	startcd(int, int);
+void	pausecd(void);
+void	resumecd(void);
+void	shutcd(void);
+void	stepcd(void);
--- a/qw/cl_main.c
+++ b/qw/cl_main.c
@@ -326,7 +326,7 @@
 {
 	int			i;
 
-	S_StopAllSounds (true);
+	stopallsfx();
 
 	Con_DPrintf ("Clearing memory\n");
 	D_FlushCaches ();
@@ -370,7 +370,7 @@
 	connect_time = -1;
 
 // stop sounds (especially looping!)
-	S_StopAllSounds (true);
+	stopallsfx();
 	
 // if running a local server, shut it down
 	if (cls.demoplayback)
@@ -709,7 +709,7 @@
 	if (cls.download)  // don't change when downloading
 		return;
 
-	S_StopAllSounds (true);
+	stopallsfx();
 	cl.intermission = 0;
 	cls.state = ca_connected;	// not active anymore, but not disconnected
 	Con_Printf ("\nChanging map...\n");
@@ -728,7 +728,7 @@
 	if (cls.download)  // don't change when downloading
 		return;
 
-	S_StopAllSounds (true);
+	stopallsfx();
 
 	if (cls.state == ca_connected) {
 		Con_Printf ("reconnecting...\n");
@@ -1185,38 +1185,8 @@
 	}
 }
 
-
-//============================================================================
-
 /*
 ==================
-Host_SimulationTime
-
-This determines if enough time has passed to run a simulation frame
-==================
-*/
-/*
-qboolean Host_SimulationTime(float time)
-{
-	float fps;
-
-	if (oldrealtime > realtime)
-		oldrealtime = 0;
-
-	if (cl_maxfps.value)
-		fps = Max(30.0, Min(cl_maxfps.value, 72.0));
-	else
-		fps = Max(30.0, Min(rate.value/80.0, 72.0));
-
-	if (!cls.timedemo && (realtime + time) - oldrealtime < 1.0/fps)
-		return false;			// framerate is too high
-	return true;
-}
-*/
-
-
-/*
-==================
 Host_Frame
 
 Runs all active servers
@@ -1294,13 +1264,13 @@
 	// update audio
 	if (cls.state == ca_active)
 	{
-		S_Update (r_origin, vpn, vright, vup);
+		stepsnd (r_origin, vpn, vright, vup);
 		CL_DecayLights ();
 	}
 	else
-		S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
+		stepsnd (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
 	
-	CDAudio_Update();
+	stepcd();
 
 	if (host_speeds.value)
 	{
@@ -1382,12 +1352,12 @@
 		Sys_Error ("Couldn't load gfx/colormap.lmp");
 
 	IN_Init ();
-	CDAudio_Init ();
+	initcd ();
 	VID_Init (host_basepal);
 	Draw_Init ();
 	SCR_Init ();
 	R_Init ();
-	S_Init ();
+	initsnd (parms);
 	cls.state = ca_disconnected;
 	Sbar_Init ();
 	CL_Init ();
@@ -1428,9 +1398,9 @@
 
 	Host_WriteConfiguration (); 
 		
-	CDAudio_Shutdown ();
+	shutcd ();
 	NET_Shutdown ();
-	S_Shutdown();
+	shutsnd();
 	IN_Shutdown ();
 	if (host_basepal)
 		VID_Shutdown();
--- a/qw/cl_parse.c
+++ b/qw/cl_parse.c
@@ -267,7 +267,7 @@
 	{
 		if (!cl.sound_name[i][0])
 			break;
-		cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]);
+		cl.sound_precache[i] = precachesfx(cl.sound_name[i]);
 	}
 
 	// done with sounds, request models now
@@ -762,7 +762,7 @@
 	vol = MSG_ReadByte ();
 	atten = MSG_ReadByte ();
 	
-	S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
+	staticsfx (cl.sound_precache[sound_num], org, vol, atten);
 }
 
 
@@ -812,7 +812,7 @@
 	if (ent > MAX_EDICTS)
 		Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
 	
-    S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
+    startsfx (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
 }       
 
 
@@ -1139,7 +1139,7 @@
 			i = MSG_ReadByte ();
 			if (i == PRINT_CHAT)
 			{
-				S_LocalSound ("misc/talk.wav");
+				localsfx ("misc/talk.wav");
 				con_ormask = 128;
 			}
 			Con_Printf ("%s", MSG_ReadString ());
@@ -1186,7 +1186,7 @@
 			
 		case svc_stopsound:
 			i = MSG_ReadShort();
-			S_StopSound(i>>3, i&7);
+			stopsfx(i>>3, i&7);
 			break;
 		
 		case svc_updatefrags:
@@ -1255,7 +1255,7 @@
 
 		case svc_cdtrack:
 			cl.cdtrack = MSG_ReadByte ();
-			CDAudio_Play ((byte)cl.cdtrack, true);
+			startcd ((byte)cl.cdtrack, true);
 			break;
 
 		case svc_intermission:
@@ -1348,9 +1348,9 @@
 		case svc_setpause:
 			cl.paused = MSG_ReadByte ();
 			if (cl.paused)
-				CDAudio_Pause ();
+				pausecd ();
 			else
-				CDAudio_Resume ();
+				resumecd ();
 			break;
 
 		}
--- a/qw/cl_tent.c
+++ b/qw/cl_tent.c
@@ -43,13 +43,13 @@
 */
 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");
+	cl_sfx_wizhit = precachesfx ("wizard/hit.wav");
+	cl_sfx_knighthit = precachesfx ("hknight/hit.wav");
+	cl_sfx_tink1 = precachesfx ("weapons/tink1.wav");
+	cl_sfx_ric1 = precachesfx ("weapons/ric1.wav");
+	cl_sfx_ric2 = precachesfx ("weapons/ric2.wav");
+	cl_sfx_ric3 = precachesfx ("weapons/ric3.wav");
+	cl_sfx_r_exp3 = precachesfx ("weapons/r_exp3.wav");
 }
 
 /*
@@ -162,7 +162,7 @@
 		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);
+		startsfx (-1, 0, cl_sfx_wizhit, pos, 1, 1);
 		break;
 		
 	case TE_KNIGHTSPIKE:			// spike hitting wall
@@ -170,7 +170,7 @@
 		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);
+		startsfx (-1, 0, cl_sfx_knighthit, pos, 1, 1);
 		break;
 		
 	case TE_SPIKE:			// spike hitting wall
@@ -180,16 +180,16 @@
 		R_RunParticleEffect (pos, vec3_origin, 0, 10);
 
 		if ( rand() % 5 )
-			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+			startsfx (-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);
+				startsfx (-1, 0, cl_sfx_ric1, pos, 1, 1);
 			else if (rnd == 2)
-				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+				startsfx (-1, 0, cl_sfx_ric2, pos, 1, 1);
 			else
-				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+				startsfx (-1, 0, cl_sfx_ric3, pos, 1, 1);
 		}
 		break;
 	case TE_SUPERSPIKE:			// super spike hitting wall
@@ -199,16 +199,16 @@
 		R_RunParticleEffect (pos, vec3_origin, 0, 20);
 
 		if ( rand() % 5 )
-			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
+			startsfx (-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);
+				startsfx (-1, 0, cl_sfx_ric1, pos, 1, 1);
 			else if (rnd == 2)
-				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
+				startsfx (-1, 0, cl_sfx_ric2, pos, 1, 1);
 			else
-				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
+				startsfx (-1, 0, cl_sfx_ric3, pos, 1, 1);
 		}
 		break;
 		
@@ -231,7 +231,7 @@
 		dl->color[3] = 0.7;
 	
 	// sound
-		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+		startsfx (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
 	
 	// sprite
 		ex = CL_AllocExplosion ();
@@ -246,7 +246,7 @@
 		pos[2] = MSG_ReadCoord ();
 		R_BlobExplosion (pos);
 
-		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
+		startsfx (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
 		break;
 
 	case TE_LIGHTNING1:				// lightning bolts
--- a/qw/client.h
+++ b/qw/client.h
@@ -121,7 +121,8 @@
 ca_demostart,		// starting up a demo
 ca_connected,		// netchan_t established, waiting for svc_serverdata
 ca_onserver,		// processing data lists, donwloading, etc
-ca_active			// everything is in, so frames can be rendered
+ca_active,		// everything is in, so frames can be rendered
+ca_dedicated
 } cactive_t;
 
 typedef enum {
@@ -300,7 +301,6 @@
 extern	cvar_t	m_side;
 
 extern cvar_t		m_windowed;
-
 extern	cvar_t	name;
 
 
--- a/qw/d_sprite.c
+++ b/qw/d_sprite.c
@@ -416,5 +416,4 @@
 	D_SpriteScanLeftEdge ();
 	D_SpriteScanRightEdge ();
 	D_SpriteDrawSpans (sprite_spans);
-	print("D_DrawSprite: ok\n");
 }
--- a/qw/draw.c
+++ b/qw/draw.c
@@ -116,7 +116,6 @@
 {
 	byte			*dest;
 	byte			*source;
-	unsigned short	*pusdest;
 	int				drawline;	
 	int				row, col;
 
@@ -142,63 +141,19 @@
 	}
 	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;
-		}
+	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);
-		}
-	}
 }
 
 /*
@@ -234,20 +189,9 @@
 void Draw_Pixel(int x, int y, byte color)
 {
 	byte			*dest;
-	unsigned short	*pusdest;
 
-	if (r_pixbytes == 1)
-	{
-		dest = vid.conbuffer + y*vid.conrowbytes + x;
-		*dest = color;
-	}
-	else
-	{
-	// FIXME: pre-expand to native format?
-		pusdest = (unsigned short *)
-				((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1));
-		*pusdest = d_8to16table[color];
-	}
+	dest = vid.conbuffer + y*vid.conrowbytes + x;
+	*dest = color;
 }
 
 void Draw_Crosshair(void)
@@ -326,8 +270,7 @@
 void Draw_Pic (int x, int y, qpic_t *pic)
 {
 	byte			*dest, *source;
-	unsigned short	*pusdest;
-	int				v, u;
+	int				v;
 
 	if ((x < 0) ||
 		(x + pic->width > vid.width) ||
@@ -338,34 +281,12 @@
 	}
 
 	source = pic->data;
-
-	if (r_pixbytes == 1)
-	{
-		dest = vid.buffer + y * vid.rowbytes + x;
-
-		for (v=0 ; v<pic->height ; v++)
-		{
-			memcpy (dest, source, pic->width);
-			dest += vid.rowbytes;
-			source += pic->width;
-		}
+	dest = vid.buffer + y * vid.rowbytes + x;
+	for(v=0; v<pic->height; v++){
+		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;
-		}
-	}
 }
 
 
@@ -377,8 +298,7 @@
 void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height)
 {
 	byte			*dest, *source;
-	unsigned short	*pusdest;
-	int				v, u;
+	int				v;
 
 	if ((x < 0) ||
 		(x + width > vid.width) ||
@@ -389,34 +309,12 @@
 	}
 
 	source = pic->data + srcy * pic->width + srcx;
-
-	if (r_pixbytes == 1)
-	{
-		dest = vid.buffer + y * vid.rowbytes + x;
-
-		for (v=0 ; v<height ; v++)
-		{
-			memcpy (dest, source, width);
-			dest += vid.rowbytes;
-			source += pic->width;
-		}
+	dest = vid.buffer + y * vid.rowbytes + x;
+	for(v=0; v<height; v++){
+		memcpy(dest, source, 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<height ; v++)
-		{
-			for (u=srcx ; u<(srcx+width) ; u++)
-			{
-				pusdest[u] = d_8to16table[source[u]];
-			}
-
-			pusdest += vid.rowbytes >> 1;
-			source += pic->width;
-		}
-	}
 }
 
 
@@ -428,7 +326,6 @@
 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 ||
@@ -438,69 +335,36 @@
 	}
 		
 	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;
-			}
+	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{	// 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;
 			}
-		}
-	}
-	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;
+			dest += vid.rowbytes;
 			source += pic->width;
 		}
 	}
@@ -515,7 +379,6 @@
 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 ||
@@ -525,69 +388,37 @@
 	}
 		
 	source = pic->data;
+	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];
 
-	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;
-			}
+			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{	// 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];
 			}
-		}
-	}
-	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;
+			dest += vid.rowbytes;
 			source += pic->width;
 		}
 	}
@@ -628,7 +459,6 @@
 {
 	int				x, y, v;
 	byte			*src, *dest;
-	unsigned short	*pusdest;
 	int				f, fstep;
 	qpic_t			*conback;
 	char			ver[100];
@@ -653,56 +483,20 @@
 		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;
+	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)
-			{
-				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;
+			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;
 			}
 		}
 	}
@@ -759,66 +553,7 @@
 	}
 }
 
-
 /*
-==============
-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
 
@@ -869,16 +604,7 @@
 
 			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);
-			}
-
+			R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0);
 			vr.x += vr.width;
 			width -= vr.width;
 			tileoffsetx = 0;	// only the left tile can be left-clipped
@@ -901,8 +627,6 @@
 void Draw_Fill (int x, int y, int w, int h, int c)
 {
 	byte			*dest;
-	unsigned short	*pusdest;
-	unsigned		uc;
 	int				u, v;
 
 	if (x < 0 || x + w > vid.width ||
@@ -912,22 +636,10 @@
 		return;
 	}
 
-	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;
-	}
+	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;
 }
 //=============================================================================
 
@@ -942,10 +654,6 @@
 	int			x,y;
 	byte		*pbuf;
 
-	VID_UnlockBuffer ();
-	S_ExtraUpdate ();
-	VID_LockBuffer ();
-
 	for (y=0 ; y<vid.height ; y++)
 	{
 		int	t;
@@ -959,10 +667,6 @@
 				pbuf[x] = 0;
 		}
 	}
-
-	VID_UnlockBuffer ();
-	S_ExtraUpdate ();
-	VID_LockBuffer ();
 }
 
 //=============================================================================
--- a/qw/in.c
+++ b/qw/in.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <stdio.h>
+#include <bio.h>
 #include <draw.h>
 #include <thread.h>
 #include <mouse.h>
@@ -7,47 +8,60 @@
 #include <keyboard.h>
 #include "quakedef.h"
 
-/* vid_9.c */
+/* vid.c */
 extern int resized;
 extern Point center;
+extern Rectangle grabr;
 
-cvar_t m_windowed = {"m_windowed", "0", true};
-
-static cvar_t m_filter = {"m_filter", "0", true};
-
-static int mouseon, oldmwin;
-static float mx, my, oldmx, oldmy;
-static int mb, oldmb;
-
 typedef struct Kev Kev;
+
+enum{
+	Nbuf = 20
+};
 struct Kev{
 	int key;
 	int down;
 };
-enum{
-	Nbuf	= 64
-};
-static Channel *kchan;
+static Channel *inchan;
+static QLock mlck;
 
+cvar_t m_windowed = {"m_windowed", "0", true};
+static cvar_t m_filter = {"m_filter", "0", true};
+static int mouseon, fixms;
+static int oldmwin;
+static float olddx, olddy;
+static int mΔx, mΔy, mb, oldmb;
 
 void
-IN_Grabm(int on)
+conscmd(void)
 {
-	static char nocurs[2*4+2*2*16];
-	static int fd = -1;
+	char *p;
 
-	if(mouseon == on)
+	if(cls.state != ca_dedicated)
 		return;
-	if(mouseon = on && m_windowed.value){
-		if((fd = open("/dev/cursor", ORDWR|OCEXEC)) < 0){
-			fprint(2, "IN_Grabm:open: %r\n");
-			return;
+	while(p = nbrecvp(inchan), p != nil){
+		Cbuf_AddText(p);
+		free(p);
+	}
+}
+
+static void
+cproc(void *)
+{
+	char *s;
+	Biobuf *bf;
+
+	if(bf = Bfdopen(0, OREAD), bf == nil)
+		sysfatal("Bfdopen: %r");
+	for(;;){
+		if(s = Brdstr(bf, '\n', 1), s == nil)
+			break;
+		if(sendp(inchan, s) < 0){
+			free(s);
+			break;
 		}
-		write(fd, nocurs, sizeof nocurs);
-	}else if(fd >= 0){
-		close(fd);
-		fd = -1;
 	}
+	Bterm(bf);
 }
 
 void
@@ -56,62 +70,69 @@
 	Kev ev;
 	int r;
 
-	if(kchan == nil)
+	if(cls.state == ca_dedicated)
 		return;
-
 	if(oldmwin != (int)m_windowed.value){
 		oldmwin = (int)m_windowed.value;
 		IN_Grabm(oldmwin);
 	}
 
-	while((r = nbrecv(kchan, &ev)) > 0)
+	while(r = nbrecv(inchan, &ev), r > 0)
 		Key_Event(ev.key, ev.down);
 	if(r < 0)
-		fprint(2, "Sys_SendKeyEvents:nbrecv: %r\n");
+		fprint(2, "Sys_SendKeyEvents: %r\n");
 }
 
 void
 IN_Commands(void)
 {
-	int i;
+	int b, i, k, r;
 
-	if(!mouseon)
+	if(!mouseon || cls.state == ca_dedicated)
 		return;
-
-	/* FIXMEGASHIT */
-	for(i = 0; i < 3; i++){
-		if(mb & 1<<i && ~oldmb & 1<<i)
-			Key_Event(K_MOUSE1+i, true);
-		if (~mb & 1<<i && oldmb & 1<<i)
-			Key_Event(K_MOUSE1+i, false);
+	qlock(&mlck);
+	b = mb;
+	qunlock(&mlck);
+	b = b & 0x19 | (b & 2) << 1 | (b & 4) >> 1;
+	for(i=0, k=K_MOUSE1; i<5; i++, k++){
+		if(i == 3)
+			k = K_MWHEELUP;
+		r = b & 1<<i;
+		if(r ^ oldmb & 1<<i)
+			Key_Event(k, r);
 	}
-	oldmb = mb;
+	oldmb = b & 7;
 }
 
 void
 IN_Move(usercmd_t *cmd)
 {
+	float dx, dy;
+
 	if(!mouseon)
 		return;
-   
+	qlock(&mlck);
+	dx = mΔx;
+	dy = mΔy;
+	mΔx = 0;
+	mΔy = 0;
+	qunlock(&mlck);
 	if(m_filter.value){
-		mx = (mx + oldmx) * 0.5;
-		my = (my + oldmy) * 0.5;
+		dx = (dx + olddx) * 0.5;
+		dy = (dy + olddy) * 0.5;
 	}
-	oldmx = mx;
-	oldmy = my;
-	mx *= sensitivity.value;
-	my *= sensitivity.value;
-   
+	olddx = dx;
+	olddy = dy;
+	dx *= sensitivity.value;
+	dy *= sensitivity.value;
 	if(in_strafe.state & 1 || lookstrafe.value && in_mlook.state & 1)
-		cmd->sidemove += m_side.value * mx;
+		cmd->sidemove += m_side.value * dx;
 	else
-		cl.viewangles[YAW] -= m_yaw.value * mx;
+		cl.viewangles[YAW] -= m_yaw.value * dx;
 	if(in_mlook.state & 1)
 		V_StopPitchDrift();
-   
 	if(in_mlook.state & 1 && ~in_strafe.state & 1){
-		cl.viewangles[PITCH] += m_pitch.value * my;
+		cl.viewangles[PITCH] += m_pitch.value * dy;
 		if(cl.viewangles[PITCH] > 80)
 			cl.viewangles[PITCH] = 80;
 		if(cl.viewangles[PITCH] < -70)
@@ -118,11 +139,10 @@
 			cl.viewangles[PITCH] = -70;
 	}else{
 		if(in_strafe.state & 1 && noclip_anglehack)
-			cmd->upmove -= m_forward.value * my;
+			cmd->upmove -= m_forward.value * dy;
 		else
-			cmd->forwardmove -= m_forward.value * my;
+			cmd->forwardmove -= m_forward.value * dy;
 	}
-	mx = my = 0.0;
 }
 
 static int
@@ -173,15 +193,17 @@
 kproc(void *)
 {
 	int n, k, fd;
-	char buf[128], kdown[128], *s;
+	char buf[256], kdown[128], *s, *p;
 	Rune r;
-	Kev ev;
+	Kev ev, evc;
 
-	threadsetgrp(THin);
-
-	if((fd = open("/dev/kbd", OREAD)) < 0)
+	fd = open("/dev/kbd", OREAD);
+	if(fd < 0)
 		sysfatal("open /dev/kbd: %r");
-	kdown[0] = kdown[1] = buf[0] = 0;
+	memset(buf, 0, sizeof buf);
+	memset(kdown, 0, sizeof kdown);
+	evc.key = K_ENTER;
+	evc.down = true;
 	for(;;){
 		if(buf[0] != 0){
 			n = strlen(buf)+1;
@@ -188,48 +210,43 @@
 			memmove(buf, buf+n, sizeof(buf)-n);
 		}
 		if(buf[0] == 0){
-			n = read(fd, buf, sizeof(buf)-1);
-			if(n <= 0)
+			if(n = read(fd, buf, sizeof(buf)-1), n <= 0)
 				break;
 			buf[n-1] = 0;
 			buf[n] = 0;
 		}
-		switch(*buf){
+		switch(buf[0]){
 		case 'c':
+			if(send(inchan, &evc) < 0)
+				threadexits(nil);
 		default:
 			continue;
 		case 'k':
+			ev.down = true;
 			s = buf+1;
-			while(*s){
-				s += chartorune(&r, s);
-				if(utfrune(kdown+1, r) == nil){
-					if(k = runetokey(r)){
-						ev.key = k;
-						ev.down = true;
-						if(nbsend(kchan, &ev) < 0)
-							fprint(2, "kproc:nbsend: %r\n");
-					}
-				}
-			}
+			p = kdown+1;
 			break;
 		case 'K':
+			ev.down = false;
 			s = kdown+1;
-			while(*s){
-				s += chartorune(&r, s);
-				if(utfrune(buf+1, r) == nil){
-					if(k = runetokey(r)){
-						ev.key = k;
-						ev.down = false;
-						if(nbsend(kchan, &ev) < 0)
-							fprint(2, "kproc:nbsend: %r\n");
-					}
-				}
-			}
+			p = buf+1;
 			break;
 		}
+		while(*s != 0){
+			s += chartorune(&r, s);
+			if(utfrune(p, r) == nil){
+				k = runetokey(r);
+				if(k == 0)
+					continue;
+				ev.key = k;
+				if(send(inchan, &ev) < 0)
+					threadexits(nil);
+				if(ev.down)
+					evc.key = k;
+			}
+		}
 		strcpy(kdown, buf);
 	}
-	close(fd);
 }
 
 static void
@@ -237,19 +254,18 @@
 {
 	int b, n, nerr, fd;
 	char buf[1+5*12];
-	float x, y;
+	Point p, o;
 
-	threadsetgrp(THin);
-
-	if((fd = open("/dev/mouse", ORDWR)) < 0)
+	fd = open("/dev/mouse", ORDWR);
+	if(fd < 0)
 		sysfatal("open /dev/mouse: %r");
-
 	nerr = 0;
+	o = center;
 	for(;;){
-		if((n = read(fd, buf, sizeof buf)) != 1+4*12){
-			fprint(2, "mproc:read: bad count %d not 49: %r\n", n);
+		if(n = read(fd, buf, sizeof buf), n != 1+4*12){
 			if(n < 0 || ++nerr > 10)
 				break;
+			fprint(2, "mproc: bad count %d not 49: %r\n", n);
 			continue;
 		}
 		nerr = 0;
@@ -260,47 +276,75 @@
 		case 'm':
 			if(!mouseon)
 				break;
-
-			x = atoi(buf+1+0*12) - center.x;
-			y = atoi(buf+1+1*12) - center.y;
+			if(fixms){
+				fixms = 0;
+				goto res;
+			}
+			p.x = atoi(buf+1+0*12);
+			p.y = atoi(buf+1+1*12);
 			b = atoi(buf+1+2*12);
-
-			mx += x;
-			my += y;
-			if(x != 0.0 ||  y != 0.0)
+			qlock(&mlck);
+			mΔx += p.x - o.x;
+			mΔy += p.y - o.y;
+			mb = b;
+			qunlock(&mlck);
+			if(!ptinrect(p, grabr)){
+		res:
 				fprint(fd, "m%d %d", center.x, center.y);
-
-			mb = b&1 | (b&2)<<1 | (b&4)>>1;
+				p = center;
+			}
+			o = p;
 			break;
 		}
 	}
-	close(fd);
 }
 
 void
+IN_Grabm(int on)
+{
+	static char nocurs[2*4+2*2*16];
+	static int fd = -1;
+
+	if(mouseon == on)
+		return;
+	if(mouseon = on && m_windowed.value){
+		fd = open("/dev/cursor", ORDWR|OCEXEC);
+		if(fd < 0){
+			fprint(2, "IN_Grabm:open: %r\n");
+			return;
+		}
+		write(fd, nocurs, sizeof nocurs);
+		fixms++;
+	}else if(fd >= 0){
+		close(fd);
+		fd = -1;
+	}
+}
+
+void
 IN_Shutdown(void)
 {
-	threadkillgrp(THin);
+	if(inchan != nil)
+		chanfree(inchan);
 	IN_Grabm(0);
-	if(kchan != nil){
-		chanfree(kchan);
-		kchan = nil;
-	}
 }
 
 void
 IN_Init(void)
 {
+	if(cls.state == ca_dedicated){
+		if(inchan = chancreate(sizeof(void *), 2), inchan == nil)
+			sysfatal("chancreate: %r");
+		if(proccreate(cproc, nil, 8192) < 0)
+			sysfatal("proccreate iproc: %r");
+		return;
+	}
 	Cvar_RegisterVariable(&m_windowed);
 	Cvar_RegisterVariable(&m_filter);
-	if((kchan = chancreate(sizeof(Kev), Nbuf)) == nil)
-		sysfatal("chancreate kchan: %r");
+	if(inchan = chancreate(sizeof(Kev), Nbuf), inchan == nil)
+		sysfatal("chancreate: %r");
 	if(proccreate(kproc, nil, 8192) < 0)
 		sysfatal("proccreate kproc: %r");
-	if(COM_CheckParm("-nomouse"))
-		return;
 	if(proccreate(mproc, nil, 8192) < 0)
 		sysfatal("proccreate mproc: %r");
-	mx = my = 0.0;
-	IN_Grabm(0);
 }
--- a/qw/keys.c
+++ b/qw/keys.c
@@ -639,14 +639,14 @@
 // update auto-repeat status
 	if (down)
 	{
-		key_repeats[key]++;
-		if (key != K_BACKSPACE 
-			&& key != K_PAUSE 
-			&& key != K_PGUP 
-			&& key != K_PGDN
-			&& key_repeats[key] > 1)
-			return;	// ignore most autorepeats
-			
+		if(key != K_MWHEELUP && key != K_MWHEELDOWN)
+			key_repeats[key]++;
+		/* ignore cons event immediately following kbd down event */
+		if(key_repeats[key] == 2)
+			return;
+		if(key_repeats[key] > 2 && (key == K_ESCAPE || key == K_ENTER
+		|| key == '`' || key_dest == key_game))
+			return;
 		if (key >= 200 && !keybindings[key])
 			Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
 	}
--- a/qw/menu.c
+++ b/qw/menu.c
@@ -299,13 +299,13 @@
 		break;
 		
 	case K_DOWNARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		if (++m_main_cursor >= MAIN_ITEMS)
 			m_main_cursor = 0;
 		break;
 
 	case K_UPARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		if (--m_main_cursor < 0)
 			m_main_cursor = MAIN_ITEMS - 1;
 		break;
@@ -358,7 +358,7 @@
 
 void M_AdjustSliders (int dir)
 {
-	S_LocalSound ("misc/menu3.wav");
+	localsfx ("misc/menu3.wav");
 
 	switch (options_cursor)
 	{
@@ -561,7 +561,7 @@
 		return;
 	
 	case K_UPARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		options_cursor--;
 		if (options_cursor < 0)
 			options_cursor = OPTIONS_ITEMS-1;
@@ -568,7 +568,7 @@
 		break;
 
 	case K_DOWNARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		options_cursor++;
 		if (options_cursor >= OPTIONS_ITEMS)
 			options_cursor = 0;
@@ -726,7 +726,7 @@
 	
 	if (bind_grab)
 	{	// defining a key
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		if (k != '`')
 		{
 			sprintf (cmd, "bind %s \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);			
@@ -745,7 +745,7 @@
 
 	case K_LEFTARROW:
 	case K_UPARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		keys_cursor--;
 		if (keys_cursor < 0)
 			keys_cursor = NUMCOMMANDS-1;
@@ -753,7 +753,7 @@
 
 	case K_DOWNARROW:
 	case K_RIGHTARROW:
-		S_LocalSound ("misc/menu1.wav");
+		localsfx ("misc/menu1.wav");
 		keys_cursor++;
 		if (keys_cursor >= NUMCOMMANDS)
 			keys_cursor = 0;
@@ -761,7 +761,7 @@
 
 	case K_ENTER:		// go into bind mode
 		M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
-		S_LocalSound ("misc/menu2.wav");
+		localsfx ("misc/menu2.wav");
 		if (keys[1] != -1)
 			M_UnbindCommand (bindnames[keys_cursor][0]);
 		bind_grab = true;
@@ -769,7 +769,7 @@
 
 	case K_BACKSPACE:		// delete bindings
 	case K_DEL:				// delete bindings
-		S_LocalSound ("misc/menu2.wav");
+		localsfx ("misc/menu2.wav");
 		M_UnbindCommand (bindnames[keys_cursor][0]);
 		break;
 	}
@@ -1055,12 +1055,7 @@
 		scr_copyeverything = 1;
 
 		if (scr_con_current)
-		{
 			Draw_ConsoleBackground (vid.height);
-			VID_UnlockBuffer ();
-			S_ExtraUpdate ();
-			VID_LockBuffer ();
-		}
 		else
 			Draw_FadeScreen ();
 
@@ -1147,13 +1142,9 @@
 
 	if (m_entersound)
 	{
-		S_LocalSound ("misc/menu2.wav");
+		localsfx ("misc/menu2.wav");
 		m_entersound = false;
 	}
-
-	VID_UnlockBuffer ();
-	S_ExtraUpdate ();
-	VID_LockBuffer ();
 }
 
 
--- a/qw/mkfile
+++ b/qw/mkfile
@@ -6,7 +6,6 @@
 	qwsv\
 
 CLOBJ=\
-	cd.$O\
 	cl_cam.$O\
 	cl_demo.$O\
 	cl_ents.$O\
@@ -63,9 +62,6 @@
 	screen.$O\
 	skin.$O\
 	snd.$O\
-	snd_dma.$O\
-	snd_mem.$O\
-	snd_mix.$O\
 	sys.$O\
 	vid.$O\
 	view.$O\
--- a/qw/model.c
+++ b/qw/model.c
@@ -1325,33 +1325,13 @@
 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)
-	{
-		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);
-	}
-
+	memcpy (pskin, pinskin, skinsize);
 	pinskin += skinsize;
-
 	return ((void *)pinskin);
 }
 
@@ -1673,9 +1653,7 @@
 {
 	dspriteframe_t		*pinframe;
 	mspriteframe_t		*pspriteframe;
-	int					i, width, height, size, origin[2];
-	unsigned short		*ppixout;
-	byte				*ppixin;
+	int					width, height, size, origin[2];
 
 	pinframe = (dspriteframe_t *)pin;
 
@@ -1698,25 +1676,7 @@
 	pspriteframe->down = origin[1] - height;
 	pspriteframe->left = origin[0];
 	pspriteframe->right = width + origin[0];
-
-	if (r_pixbytes == 1)
-	{
-		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);
-	}
-
+	memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size);
 	return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size);
 }
 
--- a/qw/qwcl.c
+++ b/qw/qwcl.c
@@ -19,11 +19,8 @@
 static void
 croak(void *, char *note)
 {
-	if(!strncmp(note, "sys:", 4)){
-		IN_Shutdown();
-		SNDDMA_Shutdown();
-		NET_Shutdown();
-	}
+	if(!strncmp(note, "sys:", 4))
+		IN_Grabm(0);
 	noted(NDFLT);
 }
 
--- a/qw/r_edge.c
+++ b/qw/r_edge.c
@@ -688,10 +688,6 @@
 	// 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
--- a/qw/r_main.c
+++ b/qw/r_main.c
@@ -900,13 +900,6 @@
 		db_time2 = Sys_DoubleTime ();
 		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 ();
@@ -945,22 +938,8 @@
 
 	if (!r_worldentity.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 ();
-
-	if (!r_dspeeds.value)
-	{
-		VID_UnlockBuffer ();
-		S_ExtraUpdate ();	// don't let sound get messed up if going slow
-		VID_LockBuffer ();
-	}
 	
 	if (r_dspeeds.value)
 	{
--- a/qw/r_sky.c
+++ b/qw/r_sky.c
@@ -180,48 +180,6 @@
 	}
 }
 
-
-/*
-=================
-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
--- a/qw/r_surf.c
+++ b/qw/r_surf.c
@@ -575,35 +575,8 @@
 	}
 }
 
-
 /*
 ================
-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
 ================
 */
@@ -611,27 +584,12 @@
 {
 	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);
-		}
+		R_GenTurbTile((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);
-		}
+		R_GenSkyTile(pdest);
 	}
 	else
 	{
--- a/qw/screen.c
+++ b/qw/screen.c
@@ -930,8 +930,6 @@
 	scr_drawdialog = true;
 	SCR_UpdateScreen ();
 	scr_drawdialog = false;
-	
-	S_ClearBuffer ();		// so dma doesn't loop current sound
 
 	do
 	{
@@ -1128,7 +1126,7 @@
 		vrect.pnext = 0;
 	
 		VID_Update (&vrect);
-	}	
+	}
 }
 
 /*
--- a/qw/snd.c
+++ b/qw/snd.c
@@ -1,113 +1,987 @@
 #include <u.h>
 #include <libc.h>
 #include <stdio.h>
-#include <thread.h>
 #include "quakedef.h"
 
-static int afd, sndon;
-static uint wpos;
+cvar_t bgmvolume = {"bgmvolume", "1", 1};
+cvar_t volume = {"volume", "0.7", 1};
+
+typedef struct Chan Chan;
+
 enum{
-	Nbuf	= 16
+	Te9 = 1000000000,
+	Fpsmin = 10,
+	Fpsmax = 72,
+	Srate = 44100,
+	Ssize = 2,
+	Sch = 2,
+	Sblk = Ssize * Sch,
+	Ssamp = Srate / Fpsmin,
+	Snbuf = Ssamp * Sblk,
+	Nchan = 128,
+	Ndyn = 8,
+	Sstat = Ndyn + NUM_AMBIENTS
 };
-static Channel *schan;
-static QLock sndlock;
+static float Clipdist = 1000.0;
 
+struct Chan{
+	sfx_t *sfx;
+	int chvol;
+	int lvol;
+	int rvol;
+	vec_t attf;
+	vec3_t zp;
+	int entn;
+	int entch;
+	int p;
+	int n;
+};
+static Chan chans[Nchan], *che;
 
+static int afd = -1;
+static uchar mixbuf[Snbuf];
+static vlong sndt;
+static int nsamp;
+static int sampbuf[Ssamp*Sch*sizeof(int)];
+static int scalt[32][256];
+
+static sfx_t *ambsfx[NUM_AMBIENTS];
+
+static char cdfile[13];
+static int ntrk;
+static int cdfd = -1;
+static int cdread, cdloop, cdvol;
+
+typedef struct
+{
+	int 	length;
+	int 	speed;
+	int 	width;
+	int 	stereo;
+	int loop;
+	byte	data[1];		// variable sized
+} sfxcache_t;
+
+typedef struct
+{
+	int		rate;
+	int		width;
+	int		channels;
+	int		loopofs;
+	int		samples;
+	int		dataofs;
+} wavinfo_t;
+
+static vec3_t		listener_origin;
+static vec3_t		listener_forward;
+static vec3_t		listener_right;
+static vec3_t		listener_up;
+#define	MAX_SFX		512
+static sfx_t		*known_sfx;		// hunk allocated [MAX_SFX]
+static int			num_sfx;
+
+static cvar_t precache = {"precache", "1"};
+static cvar_t loadas8bit = {"loadas8bit", "0"};
+static cvar_t ambient_level = {"ambient_level", "0.3"};
+static cvar_t ambient_fade = {"ambient_fade", "100"};
+
+static byte	*data_p;
+static byte 	*iff_end;
+static byte 	*last_chunk;
+static byte 	*iff_data;
+
+// QuakeWorld hack...
+#define	viewentity	playernum+1
+
+/* TODO: refuctor wav loading */
 static void
-sproc(void *)
+resample(sfxcache_t *sc, byte *data, float stepscale)
 {
-	int n;
+	int inwidth;
+	int		outcount;
+	int		srcsample;
+	int		i;
+	int		sample, samplefrac, fracstep;
 
-	threadsetgrp(THsnd);
+	inwidth = sc->width;
+	outcount = sc->length / stepscale;
+	sc->length = outcount;
+	sc->speed = Srate;
+	if (loadas8bit.value)
+		sc->width = 1;
+	else
+		sc->width = inwidth;
+	sc->stereo = 0;
 
-	for(;;){
-		if(recv(schan, nil) < 0)
-			break;
-		if((n = write(afd, shm->buffer, shm->samplebits/8 * shm->samples)) < 0)
-			break;
-		qlock(&sndlock);
-		wpos += n;
-		qunlock(&sndlock);
+	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);
 	}
-	fprint(2, "sproc %d: %r\n", threadpid(threadid()));
+	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;
+		}
+	}
 }
 
-qboolean
-SNDDMA_Init(void)
+static short
+GetLittleShort(void)
 {
-	int i;
+	short val;
 
-	sndon = 0;
+	val = *data_p;
+	val = val + (*(data_p+1)<<8);
+	data_p += 2;
+	return val;
+}
 
-	if((afd = open("/dev/audio", OWRITE)) < 0){
-		fprint(2, "open: %r\n");
-		return 0;
+static int
+GetLittleLong(void)
+{
+	int val;
+
+	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;
+}
+
+static void
+FindNextChunk(char *name)
+{
+	int iff_chunk_len;
+
+	while (1)
+	{
+		data_p=last_chunk;
+
+		if (data_p >= iff_end)
+		{	// didn't find the chunk
+			data_p = nil;
+			return;
+		}
+		
+		data_p += 4;
+		iff_chunk_len = GetLittleLong();
+		if (iff_chunk_len < 0)
+		{
+			data_p = nil;
+			return;
+		}
+//		if (iff_chunk_len > 1024*1024)
+//			Sys_Error ("FindNextChunk: %d 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(strncmp((char *)data_p, name, 4) == 0)
+			return;
 	}
+}
 
-	shm = &sn;
-	shm->splitbuffer = 0;
+static void
+FindChunk(char *name)
+{
+	last_chunk = iff_data;
+	FindNextChunk (name);
+}
 
-	if((i = COM_CheckParm("-sndbits")) != 0)
-		shm->samplebits = atoi(com_argv[i+1]);
-	if(shm->samplebits != 16 && shm->samplebits != 8)
-		shm->samplebits = 16;
+static wavinfo_t
+GetWavinfo(char *name, byte *wav, vlong wavlength)
+{
+	wavinfo_t	info;
+	int     i;
+	int     format;
+	int		samples;
 
-	if((i = COM_CheckParm("-sndspeed")) != 0)
-		shm->speed = atoi(com_argv[i+1]);
+	memset(&info, 0, sizeof info);
+
+	if (!wav)
+		return info;
+		
+	iff_data = wav;
+	iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+	FindChunk("RIFF");
+	if(!(data_p && strncmp((char *)data_p+8, "WAVE", 4) == 0))
+	{
+		Con_Printf("Missing RIFF/WAVE chunks\n");
+		return info;
+	}
+
+// get "fmt " chunk
+	iff_data = data_p + 12;
+
+	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.loopofs = GetLittleLong();
+
+	// if the next chunk is a LIST chunk, look for a cue length marker
+		FindNextChunk ("LIST");
+		if (data_p)
+		{
+			if(strncmp((char *)data_p+28, "mark", 4) == 0)
+			{	// this is not a proper parse, but it works with cooledit...
+				data_p += 24;
+				i = GetLittleLong ();	// samples in loop
+				info.samples = info.loopofs + i;
+//				Con_Printf("looped length: %d\n", i);
+			}
+		}
+	}
 	else
-		shm->speed = 44100;
+		info.loopofs = -1;
 
-	shm->channels = 2;
-	if(COM_CheckParm("-sndmono") != 0)
-		shm->channels = 1;
+// find data chunk
+	FindChunk("data");
+	if (!data_p)
+	{
+		Con_Printf("Missing data chunk\n");
+		return info;
+	}
 
-	shm->samples = 4096;
-	shm->submission_chunk = 1;
+	data_p += 4;
+	samples = GetLittleLong () / info.width;
 
-	if((shm->buffer = mallocz(shm->samplebits/8 * shm->samples, 1)) == nil)
-		sysfatal("SNDDMA_Init:mallocz: %r\n");
-	shm->samplepos = 0;
-	sndon = 1;
-	wpos = 0;
-	if((schan = chancreate(sizeof(int), Nbuf)) == nil)
-		sysfatal("SNDDMA_Init:chancreate: %r");
-	if(proccreate(sproc, nil, 8192) < 0)
-		sysfatal("SNDDMA_Init:proccreate: %r");
-	return 1;
+	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;
 }
 
-int
-SNDDMA_GetDMAPos(void)
+static sfxcache_t *
+loadsfx(sfx_t *sfx)
 {
-	if(!sndon)
-		return 0;
-	qlock(&sndlock);
-	shm->samplepos = wpos / (shm->samplebits/8);
-	qunlock(&sndlock);
-	return shm->samplepos;
+	wavinfo_t info;
+	int len;
+	float stepscale;
+	sfxcache_t *sc;
+	uchar *u, buf[1024];	/* avoid dirtying the cache heap */
+
+	if(sc = Cache_Check(&sfx->cache), sc != nil)
+		return sc;
+	u = COM_LoadStackFile(va("sound/%s", sfx->name), buf, sizeof buf);
+	if(u == nil){
+		fprint(2, "loadsfx: %r\n");
+		return nil;
+	}
+	info = GetWavinfo(sfx->name, u, com_filesize);
+	if(info.channels != 1){
+		fprint(2, "loadsfx: non mono wave %s\n", sfx->name);
+		return nil;
+	}
+	stepscale = (float)info.rate / Srate;	
+	len = info.samples / stepscale;
+	len *= info.width * info.channels;
+	if(sc = Cache_Alloc(&sfx->cache, len + sizeof *sc, sfx->name), sc == nil)
+		return nil;
+	sc->length = info.samples;
+	sc->loop = info.loopofs;
+	if(info.loopofs >= 0)
+		sc->loop /= stepscale;
+	sc->speed = info.rate;
+	sc->width = info.width;
+	sc->stereo = info.channels;
+	resample(sc, u + info.dataofs, stepscale);
+	return sc;
 }
 
 void
-SNDDMA_Shutdown(void)
+stepcd(void)
 {
-	if(!sndon)
+	cdvol = bgmvolume.value * 256;
+	cdread = cdfd >= 0 && cdvol > 0;
+}
+
+static void
+sndout(void)
+{
+	int v, vol, *pb, *pe;
+	uchar *p;
+
+	vol = volume.value * 256;
+	p = mixbuf;
+	pb = sampbuf;
+	pe = sampbuf + nsamp * 2;
+	while(pb < pe){
+		v = (short)(p[1] << 8 | p[0]) * cdvol >> 8;
+		v += *pb++ * vol >> 8;
+		if(v > 0x7fff)
+			v = 0x7fff;
+		else if(v < -0x8000)
+			v = -0x8000;
+		p[0] = v;
+		p[1] = v >> 8;
+		p += 2;
+	}
+}
+
+static void
+sample8(Chan *c, void *d, int n)
+{
+	int v, *pb, *pe, *ls, *rs;
+	uchar *p;
+
+	if(c->lvol > 255)
+		c->lvol = 255;
+	if(c->rvol > 255)
+		c->rvol = 255;
+	ls = scalt[c->lvol >> 3];
+	rs = scalt[c->rvol >> 3];
+	p = (uchar *)d + c->p;
+	pb = sampbuf;
+	pe = sampbuf + n * 2;
+	while(pb < pe){
+		v = *p++;
+		*pb++ += ls[v];
+		*pb++ += rs[v];
+	}
+}
+
+static void
+sample16(Chan *c, void *d, int n)
+{
+	int v, *pb, *pe, lv, rv;
+	short *p;
+
+	lv = c->lvol;
+	rv = c->rvol;
+	p = (short *)d + c->p;
+	pb = sampbuf;
+	pe = sampbuf + n * 2;
+	while(pb < pe){
+		v = *p++;
+		*pb++ += v * lv >> 8;
+		*pb++ += v * rv >> 8;
+	}
+}
+
+static void
+samplesfx(void)
+{
+	int n, m;
+	Chan *c;
+	sfxcache_t *sc;
+	void (*sf)(Chan *, void *, int);
+
+	memset(sampbuf, 0, sizeof sampbuf);
+	for(c=chans; c<che; c++){
+		if(c->sfx == nil)
+			continue;
+		if(c->lvol == 0 && c->rvol == 0)
+			continue;
+		if(sc = loadsfx(c->sfx), sc == nil)
+			continue;
+		sf = sc->width == 1 ? sample8 : sample16;
+		n = nsamp;
+		while(n > 0){
+			m = n < c->n ? n : c->n;
+			if(m > 0)
+				sf(c, sc->data, m);
+			c->p += m;
+			c->n -= m;
+			n -= m;
+			if(c->n <= 0){
+				if(sc->loop >= 0){
+					c->p = sc->loop;
+					c->n = sc->length - c->p;
+				}else{
+					c->sfx = nil;
+					break;
+				}
+			}
+		}
+	}
+	sndout();
+}
+
+void
+stopcd(void)
+{
+	if(cdfd >= 0)
+		close(cdfd);
+	cdread = 0;
+	cdloop = 0;
+}
+
+void
+pausecd(void)
+{
+	cdread = 0;
+}
+
+void
+resumecd(void)
+{
+	cdread = 1;
+}
+
+static void
+readcd(int ns)
+{
+	int n;
+
+	if(cdfd < 0 || !cdread)
 		return;
+	if(n = readn(cdfd, mixbuf, ns), n != ns){
+		if(n < 0 || !cdloop)
+			stopcd();
+		else{
+			seek(cdfd, 0, 0);
+			ns -= n;
+			if(n = readn(cdfd, mixbuf+n, ns), n != ns)
+				stopcd();
+		}
+	}
+}
 
-	threadkillgrp(THsnd);
-	close(afd);
-	free(shm->buffer);
-	if(schan != nil){
-		chanfree(schan);
-		schan = nil;
+static void
+spatialize(Chan *c)
+{
+	vec_t Δr, m;
+	vec3_t src;
+
+	if(c->entn == cl.viewentity){
+		c->lvol = c->chvol;
+		c->rvol = c->chvol;
+		return;
 	}
-	sndon = 0;
+	VectorSubtract(c->zp, listener_origin, src);
+	Δr = 1.0 - VectorNormalize(src) * c->attf;
+	m = DotProduct(listener_right, src);
+	c->rvol = Δr * (1.0 + m) * c->chvol;
+	if(c->rvol < 0)
+		c->rvol = 0;
+	c->lvol = Δr * (1.0 - m) * c->chvol;
+	if(c->lvol < 0)
+		c->lvol = 0;
 }
 
+static void
+ambs(void)
+{
+	uchar *av;
+	float vol;
+	Chan *c, *e;
+	mleaf_t *l;
+	sfx_t **sfx;
+
+	if(cl.worldmodel == nil)
+		return;
+	c = chans;
+	e = chans + NUM_AMBIENTS;
+	l = Mod_PointInLeaf(listener_origin, cl.worldmodel);
+	if(l == nil || !ambient_level.value){
+		while(c < e)
+			c++->sfx = nil;
+		return;
+	}
+	sfx = ambsfx;
+	av = l->ambient_sound_level;
+	while(c < e){
+		c->sfx = *sfx++;
+		vol = ambient_level.value * *av++;
+		if(vol < 8)
+			vol = 0;
+		if(c->chvol < vol){
+			c->chvol += host_frametime * ambient_fade.value;
+			if(c->chvol > vol)
+				c->chvol = vol;
+		}else if(c->chvol > vol){
+			c->chvol -= host_frametime * ambient_fade.value;
+			if(c->chvol < vol)
+				c->chvol = vol;
+		}
+		c->lvol = c->chvol;
+		c->rvol = c->chvol;
+		c++;
+	}
+}
+
 void
-SNDDMA_Submit(void)
+stepsnd(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
 {
-	if(nbsend(schan, nil) < 0){
-		fprint(2, "SNDDMA_Submit:nbsend: %r\n");
-		SNDDMA_Shutdown();
+	int ns;
+	Chan *c, *sum;
+
+	if(afd < 0)
+		return;
+	VectorCopy(origin, listener_origin);
+	VectorCopy(forward, listener_forward);
+	VectorCopy(right, listener_right);
+	VectorCopy(up, listener_up);
+	ambs();
+	sum = nil;
+	for(c=chans+NUM_AMBIENTS; c<che; c++){
+		if(c->sfx == nil)
+			continue;
+		spatialize(c);
+		if(c->lvol == 0 && c->rvol == 0)
+			continue;
+		/* sum static sounds to avoid useless remixing */
+		if(c >= chans + Sstat){
+			if(sum != nil && sum->sfx == c->sfx){
+				sum->lvol += c->lvol;
+				sum->rvol += c->rvol;
+				c->lvol = c->rvol = 0;
+				continue;
+			}
+			for(sum=chans + Sstat; sum<c; sum++)
+				if(sum->sfx == c->sfx)
+					break;
+			if(sum == che)
+				sum = nil;
+			else if(sum != c){
+				sum->lvol += c->lvol;
+				sum->rvol += c->rvol;
+				c->lvol = c->rvol = 0;
+			}
+		}
 	}
+	if(sndt == 0)
+		sndt = nsec() - Te9 / Fpsmax;
+	nsamp = (nsec() - sndt) / (Te9 / Srate);
+	if(!cls.timedemo)
+		nsamp = nsamp + 15 & ~15;
+	if(nsamp > Ssamp)
+		nsamp = Ssamp;
+	ns = nsamp * Sblk;
+	memset(mixbuf, 0, ns);
+	readcd(ns);
+	samplesfx();
+	if(write(afd, mixbuf, ns) != ns){
+		fprint(2, "sndwrite: %r\n");
+		shutsnd();
+	}
+	sndt = nsec();
+}
+
+void
+startcd(int nt, int loop)
+{
+	if(ntrk < 1)
+		return;
+	nt -= 1;	/* d001 assumed part of track list */
+	if(nt < 1 || nt > ntrk){
+		fprint(2, "startcd: invalid track number %d\n", nt);
+		return;
+	}
+	if(cdfd = open(va("%s%03d", cdfile, nt), OREAD), cdfd < 0){
+		fprint(2, "startcd: open: %r\n");
+		return;
+	}
+	cdloop = loop;
+	if(cdvol > 0)
+		cdread = 1;
+}
+
+void
+stopallsfx(void)
+{
+	if(afd < 0)
+		return;
+	memset(chans, 0, sizeof chans);
+	che = chans + Sstat;
+}
+
+void
+stopsfx(int n, int ch)
+{
+	Chan *c, *e;
+
+	if(afd < 0)
+		return;
+	c = chans;
+	e = chans + Ndyn;
+	while(c < e){
+		if(c->entn == n && c->entch == ch){
+			c->sfx = nil;
+			return;
+		}
+		c++;
+	}
+}
+
+static Chan *
+pickchan(int entn, int entch)
+{
+	int Δt;
+	Chan *c, *p, *e;
+
+	p = nil;
+	Δt = 0x7fffffff;
+	for(c=chans+NUM_AMBIENTS, e=c+Ndyn; c<e; c++){
+		if(entch != 0 && c->entn == entn
+		&& (c->entch == entch || entch == -1)){
+			p = c;
+			break;
+		}
+		if(c->entn == cl.viewentity && entn != cl.viewentity && c->sfx != nil)
+			continue;
+		if(c->n < Δt){
+			Δt = c->n;
+			p = c;
+		}
+	}
+	if(p != nil)
+		p->sfx = nil;
+	return p;
+}       
+
+void
+startsfx(int entn, int entch, sfx_t *sfx, vec3_t zp, float vol, float att)
+{
+	int skip;
+	Chan *c, *c2, *e;
+	sfxcache_t *sc;
+
+	if(afd < 0 || sfx == nil)
+		return;
+	if(c = pickchan(entn, entch), c == nil)
+		return;
+	memset(c, 0, sizeof *c);
+	VectorCopy(zp, c->zp);
+	c->attf = att / Clipdist;
+	c->chvol = vol * 255;
+	c->entn = entn;
+	c->entch = entch;
+	spatialize(c);
+	if(c->lvol == 0 && c->rvol == 0)
+		return;
+	if(sc = loadsfx(sfx), sc == nil)
+		return;
+	c->sfx = sfx;
+	c->p = 0;
+	c->n = sc->length;
+	/* don't sum identical sfx started on the same frame */
+	for(c2=chans+NUM_AMBIENTS, e=chans+Sstat; c2<e; c2++){
+		if(c2 == c || c2->sfx != sfx || c2->p != 0)
+			continue;
+		skip = nrand(Srate / Fpsmin);
+		if(skip >= c->n)
+			skip = c->n - 1;
+		c->p += skip;
+		c->n -= skip;
+		break;
+	}
+}
+
+void
+localsfx(char *s)
+{
+	sfx_t *sfx;
+
+	if(afd < 0)
+		return;
+	sfx = precachesfx(s);
+	startsfx(cl.viewentity, -1, sfx, vec3_origin, 1, 1);
+}
+
+void
+staticsfx(sfx_t *sfx, vec3_t zp, float vol, float att)
+{
+	Chan *c;
+	sfxcache_t *sc;
+
+	if(sfx == nil)
+		return;
+	if(che >= chans + nelem(chans)){
+		fprint(2, "staticsfx: channel overflow\n");
+		return;
+	}
+	c = che++;
+	if(sc = loadsfx(sfx), sc == nil)
+		return;
+	if(sc->loop < 0){
+		fprint(2, "staticsfx %s: nonlooped static sound\n", sfx->name);
+		return;
+	}
+	c->sfx = sfx;
+	VectorCopy(zp, c->zp);
+	c->chvol = vol;
+	c->attf = (att / 64) / Clipdist;
+	c->n = sc->length;
+	spatialize(c);
+}
+
+static sfx_t *
+findsfx(char *s)
+{
+	sfx_t *sfx, *e;
+
+	if(s == nil)
+		Sys_Error("findsfx: nil pointer\n");
+	if(strlen(s) >= MAX_QPATH)
+		Sys_Error("findsfx: path too long %s", s);
+	sfx = known_sfx;
+	e = known_sfx + num_sfx;
+	while(sfx < e){
+		if(strcmp(sfx->name, s) == 0)
+			return sfx;
+		sfx++;
+	}
+	if(num_sfx == MAX_SFX)
+		Sys_Error("findsfx: sfx list overflow");
+	strcpy(sfx->name, s);
+	num_sfx++;
+	return sfx;
+}
+
+void
+touchsfx(char *s)
+{
+	sfx_t *sfx;
+
+	if(afd < 0)
+		return;
+	sfx = findsfx(s);
+	Cache_Check(&sfx->cache);
+}
+
+sfx_t *
+precachesfx(char *s)
+{
+	sfx_t *sfx;
+
+	if(afd < 0)
+		return nil;
+	sfx = findsfx(s);
+	if(precache.value)
+		loadsfx(sfx);
+	return sfx;
+}
+
+static void
+cdcmd(void)
+{
+	char *c;
+
+	if(Cmd_Argc() < 2){
+usage:
+		Con_Printf("cd (play|loop|stop|pause|resume|info) [track]\n");
+		return;
+	}
+	c = Cmd_Argv(1);
+	if(cistrcmp(c, "play") == 0){
+		if(Cmd_Argc() < 2)
+			goto usage;
+		startcd(atoi(Cmd_Argv(2)), 0);
+	}else if(cistrcmp(c, "loop") == 0){
+		if(Cmd_Argc() < 2)
+			goto usage;
+		startcd(atoi(Cmd_Argv(2)), 1);
+	}else if(cistrcmp(c, "stop") == 0)
+		stopcd();
+	else if(cistrcmp(c, "pause") == 0)
+		pausecd();
+	else if(cistrcmp(c, "resume") == 0)
+		resumecd();
+}
+
+static void
+playsfx(void)
+{
+	static int hash = 345;
+	int i;
+	char *s;
+	sfx_t *sfx;
+
+	if(Cmd_Argc() < 2){
+		Con_Printf("play wav [wav..]: play a wav lump\n");
+		return;
+	}
+	i = 1;
+	while(i < Cmd_Argc()){
+		if(strrchr(Cmd_Argv(i), '.') == nil)
+			s = va("%s.wav", Cmd_Argv(i));
+		else
+			s = Cmd_Argv(i);
+		sfx = precachesfx(s);
+		startsfx(hash++, 0, sfx, listener_origin, 1.0, 1.0);
+		i++;
+	}
+}
+
+static void
+playvolsfx(void)
+{
+	static int hash = 543;
+	int i;
+	float vol;
+	char *s;
+	sfx_t *sfx;
+
+	if(Cmd_Argc() < 3){
+		Con_Printf("play wav vol [wav vol]..: play an amplified wav lump\n");
+		return;
+	}
+	i = 1;
+	while(i < Cmd_Argc()){
+		if(strrchr(Cmd_Argv(i), '.') == nil)
+			s = va("%s.wav", Cmd_Argv(i));
+		else
+			s = Cmd_Argv(i);
+		sfx = precachesfx(s);
+		vol = atof(Cmd_Argv(++i));
+		startsfx(hash++, 0, sfx, listener_origin, vol, 1.0);
+		i++;
+	}
+}
+
+static void
+sfxlist(void)
+{
+	char c;
+	int sz, sum;
+	sfx_t *sfx, *e;
+	sfxcache_t *sc;
+
+	sum = 0;
+	for(sfx=known_sfx, e=known_sfx+num_sfx; sfx<e; sfx++){
+		if(sc = Cache_Check(&sfx->cache), sc == nil)
+			continue;
+		sz = sc->length * sc->width * (sc->stereo + 1);
+		sum += sz;
+		c = sc->loop >= 0 ? 'L' : ' ';
+		Con_Printf("%c(%2db) %6d : %s\n", c, sc->width * 8, sz, sfx->name);
+	}
+	Con_Printf("Total resident: %d\n", sum);
+}
+
+void
+shutcd(void)
+{
+	stopcd();
+}
+
+void
+shutsnd(void)
+{
+	if(afd < 0)
+		return;
+	close(afd);
+}
+
+static int
+cdinfo(void)
+{
+	int fd, i, n, nt;
+	char *t, types[] = {'a', 'u', 0};
+	Dir *d;
+
+	ntrk = 0;
+	if(fd = open("/mnt/cd", OREAD), fd < 0)
+		return -1;
+	if(n = dirreadall(fd, &d), n < 0){
+		close(fd);
+		return -1;
+	}
+	close(fd);
+	t = types;
+	for(;;){
+		for(nt=0, i=0; i<n; i++)
+			if(strcmp(d[i].name, va("%c%03d", *t, ntrk+1)) == 0){
+				ntrk++;
+				nt = 1;
+			}
+		if(ntrk < 1){
+			if(*++t == 0){
+				werrstr("cdinfo: no tracks found");
+				break;
+			}
+		}else if(nt == 0){
+			snprint(cdfile, sizeof cdfile, "/mnt/cd/%c", *t);
+			break;
+		}
+	}
+	free(d);
+	return ntrk < 1 ? -1 : 0;
+}
+
+int
+initcd(void)
+{
+	if(cdinfo() < 0)
+		return -1;
+	Cmd_AddCommand("cd", cdcmd);
+	return 0;
+}
+
+int
+initsnd(quakeparms_t *q)
+{
+	int i, j, *p;
+
+	if(afd = open("/dev/audio", OWRITE), afd < 0)
+		return -1;
+	for(p=scalt[1], i=8; i<8*nelem(scalt); i+=8)
+		for(j=0; j<256; j++)
+			*p++ = (char)j * i;
+	Cmd_AddCommand("play", playsfx);
+	Cmd_AddCommand("playvol", playvolsfx);
+	Cmd_AddCommand("stopsound", stopallsfx);
+	Cmd_AddCommand("soundlist", sfxlist);
+	Cvar_RegisterVariable(&volume);
+	Cvar_RegisterVariable(&precache);
+	Cvar_RegisterVariable(&loadas8bit);
+	Cvar_RegisterVariable(&bgmvolume);
+	Cvar_RegisterVariable(&ambient_level);
+	Cvar_RegisterVariable(&ambient_fade);
+	if(q->memsize < 0x800000){
+		Cvar_Set("loadas8bit", "1");
+		fprint(2, "initsnd: forcing 8bit width\n");
+	}
+	known_sfx = Hunk_AllocName(MAX_SFX * sizeof *known_sfx, "sfx_t");
+	num_sfx = 0;
+	ambsfx[AMBIENT_WATER] = precachesfx("ambience/water1.wav");
+	ambsfx[AMBIENT_SKY] = precachesfx("ambience/wind2.wav");
+	stopallsfx();
+	return 0;
 }
--- a/qw/snd_dma.c
+++ /dev/null
@@ -1,924 +1,0 @@
-// snd_dma.c -- main control for any streaming sound output device
-
-#include <u.h>
-#include <libc.h>
-#include <stdio.h>
-#include "quakedef.h"
-
-void S_Play(void);
-void S_PlayVol(void);
-void S_SoundList(void);
-void S_Update_(void);
-void S_StopAllSounds(qboolean clear);
-void S_StopAllSoundsC(void);
-
-// QuakeWorld hack...
-#define	viewentity	playernum+1
-
-// =======================================================================
-// 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];
-
-int 		desired_speed = 11025;
-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)
-		{
-			Con_Printf("S_Startup: SNDDMA_Init failed.\n");
-			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 ();
-
-	SND_InitScaletable ();
-
-	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");
-	}
-
-//	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 != nil)
-		shm->gamealive = 0;
-
-	if(!fakedma)
-		SNDDMA_Shutdown();
-	shm = 0;
-	sound_started = 0;
-}
-
-
-// =======================================================================
-// Load a sound
-// =======================================================================
-
-/*
-==================
-S_FindName
-
-==================
-*/
-sfx_t *S_FindName (char *name)
-{
-	int		i;
-	sfx_t	*sfx;
-
-	if(name == nil)
-		Sys_Error ("S_FindName: NULL\n");
-
-	if(strlen(name) >= MAX_QPATH)
-		Sys_Error ("Sound name too long: %s", name);
-
-// see if already loaded
-	for (i=0 ; i < num_sfx ; i++)
-		if(!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 dist;
-    vec_t lscale, rscale, scale;
-    vec3_t source_vec;
-
-// 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
-
-	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;
-
-	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;
-		
-	if (!sound_started || !shm || !shm->buffer)
-		return;
-
-	if (shm->samplebits == 8)
-		clear = 0x80;
-	else
-		clear = 0;
-
-	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.
-	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;
-}
-
-void S_ExtraUpdate (void)
-{
-	if (snd_noextraupdate.value)
-		return;		// don't pollute timings
-	S_Update_();
-}
-
-
-
-void S_Update_(void)
-{
-	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;
-
-	S_PaintChannels (endtime);
-
-	SNDDMA_Submit ();
-}
-
-/*
-===============================================================================
-
-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(strrchr(Cmd_Argv(i), '.') == nil)
-		{
-			strcpy(name, Cmd_Argv(i));
-			strcat(name, ".wav");
-		}
-		else
-			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(strrchr(Cmd_Argv(i), '.') == nil)
-		{
-			strcpy(name, Cmd_Argv(i));
-			strcat(name, ".wav");
-		}
-		else
-			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)
-{
-}
-
--- a/qw/snd_mem.c
+++ /dev/null
@@ -1,327 +1,0 @@
-// snd_mem.c: sound caching
-
-#include <u.h>
-#include <libc.h>
-#include <stdio.h>
-#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
-	strcpy(namebuffer, "sound/");
-	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;
-	val = *data_p;
-	val = val + (*(data_p+1)<<8);
-	data_p += 2;
-	return val;
-}
-
-int GetLittleLong(void)
-{
-	int val;
-	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 (!strncmp((char *)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 != nil && !strncmp((char *)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((char *)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;
-}
-
--- a/qw/snd_mix.c
+++ /dev/null
@@ -1,279 +1,0 @@
-// snd_mix.c -- portable code to mix sounds for snd_dma.c
-
-#include <u.h>
-#include <libc.h>
-#include <stdio.h>
-#include "quakedef.h"
-
-#define DWORD	unsigned long
-
-#define	PAINTBUFFER_SIZE	512
-portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
-int		snd_scaletable[32][256];
-int 	*snd_p, snd_linear_count, snd_vol;
-short	*snd_out;
-
-void Snd_WriteLinearBlastStereo16 (void);
-
-void Snd_WriteLinearBlastStereo16 (void)
-{
-	int		i;
-	int		val;
-
-	for (i=0 ; i<snd_linear_count ; i+=2)
-	{
-		val = (snd_p[i]*snd_vol)>>8;
-		if (val > 0x7fff)
-			snd_out[i] = 0x7fff;
-		else if (val < (short)0x8000)
-			snd_out[i] = (short)0x8000;
-		else
-			snd_out[i] = val;
-
-		val = (snd_p[i+1]*snd_vol)>>8;
-		if (val > 0x7fff)
-			snd_out[i+1] = 0x7fff;
-		else if (val < (short)0x8000)
-			snd_out[i+1] = (short)0x8000;
-		else
-			snd_out[i+1] = val;
-	}
-}
-
-void S_TransferStereo16 (int endtime)
-{
-	int		lpos;
-	int		lpaintedtime;
-	DWORD	*pbuf;
-	
-	snd_vol = volume.value*256;
-
-	snd_p = (int *) paintbuffer;
-	lpaintedtime = paintedtime;
-
-	pbuf = (DWORD *)shm->buffer;
-
-	while (lpaintedtime < endtime)
-	{
-	// handle recirculating buffer issues
-		lpos = lpaintedtime & ((shm->samples>>1)-1);
-
-		snd_out = (short *) pbuf + (lpos<<1);
-
-		snd_linear_count = (shm->samples>>1) - lpos;
-		if (lpaintedtime + snd_linear_count > endtime)
-			snd_linear_count = endtime - lpaintedtime;
-
-		snd_linear_count <<= 1;
-
-	// write a linear blast of samples
-		Snd_WriteLinearBlastStereo16 ();
-
-		snd_p += snd_linear_count;
-		lpaintedtime += (snd_linear_count>>1);
-	}
-}
-
-void S_TransferPaintBuffer(int endtime)
-{
-	int 	out_idx;
-	int 	count;
-	int 	out_mask;
-	int 	*p;
-	int 	step;
-	int		val;
-	int		snd_vol;
-	DWORD	*pbuf;
-
-	if (shm->samplebits == 16 && shm->channels == 2)
-	{
-		S_TransferStereo16 (endtime);
-		return;
-	}
-	
-	p = (int *) paintbuffer;
-	count = (endtime - paintedtime) * shm->channels;
-	out_mask = shm->samples - 1; 
-	out_idx = paintedtime * shm->channels & out_mask;
-	step = 3 - shm->channels;
-	snd_vol = volume.value*256;
-
-	pbuf = (DWORD *)shm->buffer;
-
-	if (shm->samplebits == 16)
-	{
-		short *out = (short *) pbuf;
-		while (count--)
-		{
-			val = (*p * snd_vol) >> 8;
-			p+= step;
-			if (val > 0x7fff)
-				val = 0x7fff;
-			else if (val < (short)0x8000)
-				val = (short)0x8000;
-			out[out_idx] = val;
-			out_idx = (out_idx + 1) & out_mask;
-		}
-	}
-	else if (shm->samplebits == 8)
-	{
-		unsigned char *out = (unsigned char *) pbuf;
-		while (count--)
-		{
-			val = (*p * snd_vol) >> 8;
-			p+= step;
-			if (val > 0x7fff)
-				val = 0x7fff;
-			else if (val < (short)0x8000)
-				val = (short)0x8000;
-			out[out_idx] = (val>>8) + 128;
-			out_idx = (out_idx + 1) & out_mask;
-		}
-	}
-}
-
-
-/*
-===============================================================================
-
-CHANNEL MIXING
-
-===============================================================================
-*/
-
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime);
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime);
-
-void S_PaintChannels(int endtime)
-{
-	int 	i;
-	int 	end;
-	channel_t *ch;
-	sfxcache_t	*sc;
-	int		ltime, count;
-
-	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(portable_samplepair_t));
-
-	// 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);
-			if (!sc)
-				continue;
-
-			ltime = paintedtime;
-
-			while (ltime < end)
-			{	// paint up to end
-				if (ch->end < end)
-					count = ch->end - ltime;
-				else
-					count = end - ltime;
-
-				if (count > 0)
-				{	
-					if (sc->width == 1)
-						SND_PaintChannelFrom8(ch, sc, count);
-					else
-						SND_PaintChannelFrom16(ch, sc, 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;
-	}
-}
-
-void SND_InitScaletable (void)
-{
-	int		i, j;
-	
-	for (i=0 ; i<32 ; i++)
-		for (j=0 ; j<256 ; j++)
-			snd_scaletable[i][j] = ((signed char)j) * i * 8;
-}
-
-
-void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count)
-{
-	int 	data;
-	int		*lscale, *rscale;
-	unsigned char *sfx;
-	int		i;
-
-	if (ch->leftvol > 255)
-		ch->leftvol = 255;
-	if (ch->rightvol > 255)
-		ch->rightvol = 255;
-		
-	lscale = snd_scaletable[ch->leftvol >> 3];
-	rscale = snd_scaletable[ch->rightvol >> 3];
-	sfx = (uchar *)sc->data + ch->pos;
-
-	for (i=0 ; i<count ; i++)
-	{
-		data = sfx[i];
-		paintbuffer[i].left += lscale[data];
-		paintbuffer[i].right += rscale[data];
-	}
-	
-	ch->pos += count;
-}
-
-
-void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count)
-{
-	int data;
-	int left, right;
-	int leftvol, rightvol;
-	signed short *sfx;
-	int	i;
-
-	leftvol = ch->leftvol;
-	rightvol = ch->rightvol;
-	sfx = (signed short *)sc->data + ch->pos;
-
-	for (i=0 ; i<count ; i++)
-	{
-		data = sfx[i];
-		left = (data * leftvol) >> 8;
-		right = (data * rightvol) >> 8;
-		paintbuffer[i].left += left;
-		paintbuffer[i].right += right;
-	}
-
-	ch->pos += count;
-}
--- a/qw/sound.h
+++ b/qw/sound.h
@@ -64,24 +64,22 @@
 	int		dataofs;		// chunk starts this many bytes from file start
 } wavinfo_t;
 
-void S_Init (void);
+int	initsnd(quakeparms_t*);
 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);
+void shutsnd (void);
+void startsfx (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol,  float attenuation);
+void staticsfx (sfx_t *sfx, vec3_t origin, float vol, float attenuation);
+void stopsfx (int entnum, int entchannel);
+void stopallsfx(void);
+void stepsnd (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
 
-sfx_t *S_PrecacheSound (char *sample);
+sfx_t*	precachesfx(char*);
 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);
+void initsndPaintChannels (void);
 
 // picks a channel based on priorities, empty slots, number of channels
 channel_t *SND_PickChannel(int entnum, int entchannel);
@@ -116,21 +114,16 @@
 //
 // 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.
+// number of times stepsnd() 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;
 
@@ -138,10 +131,8 @@
 
 extern int		snd_blocked;
 
-void S_LocalSound (char *s);
+void localsfx (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);
--- a/qw/sys.c
+++ b/qw/sys.c
@@ -6,6 +6,16 @@
 
 int svonly;
 
+void *
+emalloc(ulong n)
+{
+	void *p;
+
+	if(p = mallocz(n, 1), p == nil)
+		sysfatal("emalloc %r");
+	setmalloctag(p, getcallerpc(&n));
+	return p;
+}
 
 void
 Sys_Printf(char *fmt, ...)
--- a/qw/sys.h
+++ b/qw/sys.h
@@ -27,3 +27,4 @@
 void initparm(quakeparms_t *);
 
 void killiop(void);
+void*	emalloc(ulong);
--- a/qw/vid.c
+++ b/qw/vid.c
@@ -4,95 +4,80 @@
 #include <stdio.h>
 #include "quakedef.h"
 
-viddef_t vid;
-ushort d_8to16table[256];
+viddef_t vid;		/* global video state */
 int resized;
-Point center;
+int dumpwin, scaleon;
+Point center;		/* of window */
+Rectangle grabr;
 
-void (*vid_menudrawfn)(void);
-void (*vid_menukeyfn)(int);
+static int scale = 1;
+static s32int fbpal[256];
+static uchar *fb, *fbs;
+static Image *fbi;
+static Rectangle fbr;
 
-typedef ulong PIXEL;
-
-enum{
-	Rmask	= 0xff0000,
-	Gmask	= 0xff00,
-	Bmask	= 0xff
-};
-static int shifton, rshift, gshift, bshift;
-static uchar *framebuf;	/* draw buffer */
-static Image *fbim;	/* framebuf image */
-
-static PIXEL st2d_8to16table[256];
-
-static void	mkmasks(void);
-static PIXEL	rgb24(int, int, int);
-static void	st2_fixup(uchar *, int, int, int, int);
-static void	resetfb(void);
-
-
 static void
-mkmasks(void)
+scalefb(void)
 {
-	uint x;
+	int *p, c, *s, dy;
 
-	for(rshift = -8, x = 1; x < Rmask; x <<= 1)
-		rshift++;
-	for(gshift = -8, x = 1; x < Gmask; x <<= 1)
-		gshift++;
-	for(bshift = -8, x = 1; x < Bmask; x <<= 1)
-		bshift++;
-	shifton = 1;
+	if(scale < 2)
+		return;
+	p = (s32int *)fbs;
+	s = (s32int *)fb;
+	dy = vid.height * vid.width;
+	while(dy-- > 0){
+		c = *s++;
+		switch(scale){
+		case 16: p[15] = c;
+		case 15: p[14] = c;
+		case 14: p[13] = c;
+		case 13: p[12] = c;
+		case 12: p[11] = c;
+		case 11: p[10] = c;
+		case 10: p[9] = c;
+		case 9: p[8] = c;
+		case 8: p[7] = c;
+		case 7: p[6] = c;
+		case 6: p[5] = c;
+		case 5: p[4] = c;
+		case 4: p[3] = c;
+		case 3: p[2] = c;
+		case 2: p[1] = c; p[0] = c;
+		}
+		p += scale;
+	}
 }
 
-static PIXEL
-rgb24(int r, int g, int b)
-{
-	PIXEL p = 0;
-
-	if(!shifton)
-		mkmasks();
-
-	if(rshift > 0)
-		p = r<<rshift & Rmask;
-	else if(rshift < 0)
-		p = r>>-rshift & Rmask;
-	else
-		p |= r & Rmask;
-	if(gshift > 0)
-		p |= g<<gshift & Gmask;
-	else if(gshift < 0)
-		p |= g>>-gshift & Gmask;
-	else
-		p |= g & Gmask;
-	if(bshift > 0)
-		p |= b<<bshift & Bmask;
-	else if(bshift < 0)
-		p |= b>>-bshift & Bmask;
-	else
-		p |= b & Bmask;
-	return p;
-}
-
 static void
-st2_fixup(uchar *data, int x, int y, int width, int height)
+drawfb(void)
 {
-	int xi, yi;
-	uchar *src;
-	PIXEL *dest;
+	uchar *s;
+	int n, dy, we, w8, wr, *d;
 
-	if(x < 0 || y < 0)
-		return;
-
-	for (yi = y; yi < y+height; yi++){
-		src = &data[yi*vid.rowbytes];
-		dest = (PIXEL *)src;
-		for(xi = x+width-1; xi >= x; xi--)
-			dest[xi] = st2d_8to16table[src[xi]];
+	we = vid.width - 1;
+	w8 = vid.width + 7 >> 3;
+	wr = vid.width % 8;
+	dy = vid.height * vid.rowbytes;
+	while((dy -= vid.rowbytes) >= 0){
+		s = fb + dy;
+		d = ((int *)s) + we;
+		s += we;
+		n = w8;
+		switch(wr){
+		case 0:	do{	*d-- = fbpal[*s--];
+		case 7:		*d-- = fbpal[*s--];
+		case 6:		*d-- = fbpal[*s--];
+		case 5:		*d-- = fbpal[*s--];
+		case 4:		*d-- = fbpal[*s--];
+		case 3:		*d-- = fbpal[*s--];
+		case 2:		*d-- = fbpal[*s--];
+		case 1:		*d-- = fbpal[*s--];
+			}while(--n > 0);
+		}
 	}
 }
 
-/* vid.height and vid.width must be set correctly before this call */
 static void
 resetfb(void)
 {
@@ -100,11 +85,19 @@
 	void *surfcache;
 	int hunkvbuf, scachesz;
 
-	if(framebuf != nil){
-		free(framebuf);
-		framebuf = nil;
+	Point p;
+
+	if(scaleon){
+		scale = Dx(screen->r) / vid.width;
+		if(scale <= 0)
+			scale = 1;
+		else if(scale > 16)
+			scale = 16;
+	}else{
+		vid.width = Dx(screen->r);
+		vid.height = Dy(screen->r);
 	}
-	if(d_pzbuffer){
+	if(d_pzbuffer != nil){
 		D_FlushCaches();
 		Hunk_FreeToHighMark(highhunk);
 		d_pzbuffer = nil;
@@ -112,48 +105,64 @@
 
 	highhunk = Hunk_HighMark();
 	// alloc an extra line in case we want to wrap, and allocate the z-buffer
-	hunkvbuf = vid.width * vid.height * sizeof(*d_pzbuffer);
+	hunkvbuf = vid.width * vid.height * sizeof *d_pzbuffer;
 	scachesz = D_SurfaceCacheForRes(vid.width, vid.height);
 	hunkvbuf += scachesz;
 	if((d_pzbuffer = Hunk_HighAllocName(hunkvbuf, "video")) == nil)
 		sysfatal("Not enough memory for video mode\n");
-	surfcache = (byte *)d_pzbuffer + vid.width * vid.height * sizeof(*d_pzbuffer);
+	surfcache = (byte *)d_pzbuffer + vid.width * vid.height * sizeof *d_pzbuffer;
 	D_InitCaches(surfcache, scachesz);
 
-	framebuf = malloc(sizeof *framebuf * Dx(screen->r) * Dy(screen->r) * screen->depth/8);
-	vid.buffer = framebuf;
-	vid.rowbytes = Dx(screen->r) * screen->depth/8;
-	vid.aspect = (float)vid.height / (float)vid.width * (320.0/240.0);	/* FIXME */
-	vid.conbuffer = vid.buffer;
+	vid.rowbytes = vid.width * sizeof *fbpal;
+	vid.aspect = (float)vid.height / (float)vid.width * (320.0/240.0);
 	vid.conrowbytes = vid.rowbytes;
 	vid.conwidth = vid.width;
 	vid.conheight = vid.height;
-	center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
-	if(fbim != nil)
-		freeimage(fbim);
-	fbim = allocimage(display, Rect(0, 0, vid.width, vid.height), screen->chan, 1, DNofill);
+
+	center = divpt(addpt(screen->r.min, screen->r.max), 2);
+	p = Pt(scale * vid.width/2, scale * vid.height/2);
+	fbr = Rpt(subpt(center, p), addpt(center, p));
+	p = Pt(vid.width/4, vid.height/4);
+	grabr = Rpt(subpt(center, p), addpt(center, p));
+	freeimage(fbi);
+	free(fb);
+	fbi = allocimage(display,
+		Rect(0, 0, vid.width * scale, scale > 1 ? 1 : vid.height),
+		XRGB32, scale > 1, 0);
+	if(fbi == nil)
+		sysfatal("resetfb: %r (%d %d)", vid.width, vid.height);
+	fb = emalloc(vid.rowbytes * vid.height);
+	if(scaleon){
+		free(fbs);
+		fbs = emalloc(vid.rowbytes * scale * vid.height);
+	}
+	vid.buffer = fb;
+	vid.conbuffer = fb;
+	draw(screen, screen->r, display->black, nil, ZP);
 }
 
-// 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_Init(uchar */*palette*/)
+VID_Init(uchar *)
 {
+	int n;
+
+	n = COM_CheckParm("-scale");
+	if(n && n < com_argc-2){
+		scaleon = 1;
+		vid.width = strtol(com_argv[n+1], nil, 0);
+		vid.height = strtol(com_argv[n+2], nil, 0);
+		if(vid.width < 320 || vid.height < 200)
+			sysfatal("invalid scale resolution");
+	}
 	vid.maxwarpwidth = WARP_WIDTH;
 	vid.maxwarpheight = WARP_HEIGHT;
 	vid.numpages = 2;
 	vid.colormap = host_colormap;
 	vid.fullbright = 256 - LittleLong(*((int *)vid.colormap + 2048));
-
 	srand(getpid());
-
 	if(initdraw(nil, nil, "quake") < 0)
-		sysfatal("VID_Init:initdraw: %r\n");
-	vid.width = Dx(screen->r);
-	vid.height = Dy(screen->r);
+		sysfatal("initdraw: %r\n");
 	resetfb();
-	vid.direct = 0;
 }
 
 void
@@ -163,46 +172,53 @@
 }
 
 void
-VID_SetPalette(uchar *palette)
+VID_SetPalette(uchar *p)
 {
-	int i;
+	int *fp;
 
-	for(i = 0; i < 256; i++)
-		st2d_8to16table[i] = rgb24(palette[i*3], palette[i*3+1], palette[i*3+2]);
+	for(fp=fbpal; fp<fbpal+nelem(fbpal); p+=3)
+		*fp++ = p[0] << 16 | p[1] << 8 | p[2];
+	scr_fullupdate = 0;
 }
 
 void
 VID_Shutdown(void)
 {
-	Con_Printf("VID_Shutdown\n");
-	free(framebuf);
-	freeimage(fbim);
+	free(fb);
+	free(fbs);
+	freeimage(fbi);
 }
 
 void
-VID_Update(vrect_t *rects)
+VID_Update(vrect_t *)
 {
 	if(resized){		/* skip this frame if window resize */
 		resized = 0;
 		if(getwindow(display, Refnone) < 0)
 			sysfatal("VID_Update:getwindow: %r\n");
-		vid.width = Dx(screen->r);
-		vid.height = Dy(screen->r);
 		resetfb();
 		vid.recalc_refdef = 1;	/* force a surface cache flush */
 		Con_CheckResize();
 		Con_Clear_f();
-		return;
 	}
+	drawfb();
+	scalefb();
+	if(scale == 1){
+		loadimage(fbi, Rect(0,0,vid.width,vid.height), fb, vid.height * vid.rowbytes);
+		draw(screen, fbr, fbi, nil, ZP);
+	}else{
+		Rectangle r;
+		uchar *p;
 
-	scr_fullupdate = 0;	/* force full update if not 8bit (cf. screen.h) */
-
-	while(rects){
-		st2_fixup(framebuf, rects->x, rects->y, rects->width, rects->height);
-		rects = rects->pnext;
+		p = fbs;
+		r = fbr;
+		while(r.min.y < fbr.max.y){
+			r.max.y = r.min.y + scale;
+			p += loadimage(fbi, fbi->r, p, vid.rowbytes * scale);
+			draw(screen, r, fbi, nil, ZP);
+			r.min.y = r.max.y;
+		}
 	}
-	loadimage(fbim, fbim->r, framebuf, vid.height * vid.rowbytes);
-	draw(screen, screen->r, fbim, nil, ZP);
 	flushimage(display, 1);
 }
 
--- a/qw/vid.h
+++ b/qw/vid.h
@@ -35,8 +35,6 @@
 } viddef_t;
 
 extern	viddef_t	vid;				// global video state
-extern	unsigned short	d_8to16table[256];
-extern	unsigned	d_8to24table[256];
 
 void	VID_SetPalette (unsigned char *palette);
 // called at startup and after any gamma correction