about summary refs log tree commit diff
path: root/src/engine/client
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-08-14 18:37:16 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-08-14 18:37:16 +0000
commit2cde04ddcec3f3c083527c464f93bf8c30b6e790 (patch)
tree2666b20bf713f7d5244af1aec9f2d2f54d193f35 /src/engine/client
parent8809084d253be4e9923307a13c8830c593dfefc0 (diff)
downloadzcatch-2cde04ddcec3f3c083527c464f93bf8c30b6e790.tar.gz
zcatch-2cde04ddcec3f3c083527c464f93bf8c30b6e790.zip
merged over all stuff from 0.2 to trunk
Diffstat (limited to 'src/engine/client')
-rw-r--r--src/engine/client/client.cpp203
-rw-r--r--src/engine/client/gfx.cpp111
-rw-r--r--src/engine/client/snd.cpp76
3 files changed, 254 insertions, 136 deletions
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index f3390c51..3a64db7e 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -29,7 +29,6 @@ static int info_request_end;
 static int snapshot_part;
 static int64 local_start_time;
 static int64 game_start_time;
-static int current_tick;
 static float latency = 0;
 static int extra_polating = 0;
 static int debug_font;
@@ -37,8 +36,12 @@ static float frametime = 0.0001f;
 static net_client net;
 static netaddr4 master_server;
 static netaddr4 server_address;
-static const char *server_spam_address=0;
 static int window_must_refocus = 0;
+static int snaploss = 0;
+
+static int current_tick = 0;
+static float intratick = 0;
+
 
 // --- input wrappers ---
 static int keyboard_state[2][input::last];
@@ -151,14 +154,9 @@ static void client_snapshot_purge_until(int tick)
 
 static snapshot_info *snapshots[NUM_SNAPSHOT_TYPES];
 static int recived_snapshots;
-static int64 snapshot_start_time;
 static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE];
 
 // ---
-float client_localtime()
-{
-	return (time_get()-local_start_time)/(float)(time_freq());
-}
 
 const void *snap_get_item(int snapid, int index, snap_item *item)
 {
@@ -199,7 +197,7 @@ static void snap_init()
 // ------ time functions ------
 float client_intratick()
 {
-	return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED);
+	return intratick;
 }
 
 int client_tick()
@@ -217,6 +215,10 @@ float client_frametime()
 	return frametime;
 }
 
+float client_localtime()
+{
+	return (time_get()-local_start_time)/(float)(time_freq());
+}
 
 int menu_loop(); // TODO: what is this?
 
@@ -312,9 +314,11 @@ void client_serverbrowse_refresh(int lan)
 	
 	if(serverlist_lan)
 	{
-		dbg_msg("client", "broadcasting for servers");
+		if(config.debug)
+			dbg_msg("client", "broadcasting for servers");
 		NETPACKET packet;
 		packet.client_id = -1;
+		mem_zero(&packet, sizeof(packet));
 		packet.address.ip[0] = 0;
 		packet.address.ip[1] = 0;
 		packet.address.ip[2] = 0;
@@ -330,8 +334,10 @@ void client_serverbrowse_refresh(int lan)
 	}
 	else
 	{
-		dbg_msg("client", "requesting server list");
+		if(config.debug)
+			dbg_msg("client", "requesting server list");
 		NETPACKET packet;
+		mem_zero(&packet, sizeof(packet));
 		packet.client_id = -1;
 		packet.address = master_server;
 		packet.flags = PACKETFLAG_CONNLESS;
@@ -347,9 +353,12 @@ void client_serverbrowse_refresh(int lan)
 
 static void client_serverbrowse_request(int id)
 {
-	dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
-		servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2],
-		servers.addresses[id].ip[3], servers.addresses[id].port);
+	if(config.debug)
+	{
+		dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d",
+			servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2],
+			servers.addresses[id].ip[3], servers.addresses[id].port);
+	}
 	NETPACKET packet;
 	packet.client_id = -1;
 	packet.address = servers.addresses[id];
@@ -388,7 +397,8 @@ static int state;
 int client_state() { return state; }
 static void client_set_state(int s)
 {
-	dbg_msg("game", "state change. last=%d current=%d", state, s);
+	if(config.debug)
+		dbg_msg("client", "state change. last=%d current=%d", state, s);
 	int old = state;
 	state = s;
 	if(old != s)
@@ -422,6 +432,7 @@ void client_connect(const char *server_address_str)
 	
 	net.connect(&server_address);
 	client_set_state(CLIENTSTATE_CONNECTING);	
+	current_tick = 0;
 }
 
 void client_disconnect()
@@ -459,9 +470,10 @@ static void client_debug_render()
 	static float frametime_avg = 0;
 	frametime_avg = frametime_avg*0.9f + frametime*0.1f;
 	char buffer[512];
-	sprintf(buffer, "send: %6d recv: %6d latency: %4.0f %c gfxmem: %6dk fps: %3d",
+	sprintf(buffer, "send: %6d recv: %6d snaploss: %4d latency: %4.0f %c gfxmem: %6dk fps: %3d",
 		(current.send_bytes-prev.send_bytes)*10,
 		(current.recv_bytes-prev.recv_bytes)*10,
+		snaploss,
 		latency*1000.0f, extra_polating?'E':' ',
 		gfx_memory_usage()/1024,
 		(int)(1.0f/frametime_avg));
@@ -488,7 +500,7 @@ static void client_render()
 
 static void client_error(const char *msg)
 {
-	dbg_msg("game", "error: %s", msg);
+	dbg_msg("client", "error: %s", msg);
 	client_send_error(msg);
 	client_set_state(CLIENTSTATE_QUITING);
 }
@@ -549,7 +561,8 @@ static void client_process_packet(NETPACKET *packet)
 						packet->address.ip[0], packet->address.ip[1], packet->address.ip[2],
 						packet->address.ip[3], packet->address.port);
 
-					dbg_msg("client", "got server info");
+					if(config.debug)
+						dbg_msg("client", "got server info");
 					servers.num++;
 					
 				}
@@ -565,7 +578,8 @@ static void client_process_packet(NETPACKET *packet)
 						servers.infos[i].max_players = unpacker.get_int();
 						servers.infos[i].num_players = unpacker.get_int();
 						servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq();
-						dbg_msg("client", "got server info");
+						if(config.debug)
+							dbg_msg("client", "got server info");
 						break;
 					}
 				}
@@ -611,7 +625,7 @@ static void client_process_packet(NETPACKET *packet)
 				if(msg != NETMSG_SNAPEMPTY)
 					part_size = msg_unpack_int();
 				
-				if(snapshot_part == part)
+				if(snapshot_part == part && game_tick > current_tick)
 				{
 					// TODO: clean this up abit
 					const char *d = (const char *)msg_unpack_raw(part_size);
@@ -620,29 +634,16 @@ static void client_process_packet(NETPACKET *packet)
 				
 					if(snapshot_part == num_parts)
 					{
-						current_tick = game_tick;
-
-						// decompress snapshot
-						void *deltadata = snapshot_empty_delta();
-						int deltasize = sizeof(int)*3;
-
-						unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE];
-						unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
-						if(part_size)
-						{
-							int compsize = zerobit_decompress(snapshot_incomming_data, part_size, tmpbuffer);
-							int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
-							deltadata = tmpbuffer2;
-							deltasize = intsize;
-						}
-
+						snapshot_part = 0;
+						
 						// find snapshot that we should use as delta 
 						static snapshot emptysnap;
 						emptysnap.data_size = 0;
 						emptysnap.num_items = 0;
 						
 						snapshot *deltashot = &emptysnap;
-
+						
+						// find delta
 						if(delta_tick >= 0)
 						{
 							//void *delta_data;
@@ -652,11 +653,34 @@ static void client_process_packet(NETPACKET *packet)
 								deltashot = delta_info->snap;
 							else
 							{
-								// TODO: handle this
-								dbg_msg("client", "error, couldn't find the delta snapshot");
+								// couldn't find the delta snapshots that the server used
+								// to compress this snapshot. force the server to resync
+								if(config.debug)
+									dbg_msg("client", "error, couldn't find the delta snapshot");
+								
+								// ack snapshot
+								msg_pack_start_system(NETMSG_SNAPACK, 0);
+								msg_pack_int(-1);
+								msg_pack_end();
+								client_send_msg();
+								return;
 							}
 						}
 
+						// decompress snapshot
+						void *deltadata = snapshot_empty_delta();
+						int deltasize = sizeof(int)*3;
+
+						unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE];
+						unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE];
+						if(part_size)
+						{
+							int compsize = zerobit_decompress(snapshot_incomming_data, part_size, tmpbuffer);
+							int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2);
+							deltadata = tmpbuffer2;
+							deltasize = intsize;
+						}
+
 						//dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick);
 						
 						unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE];
@@ -681,12 +705,17 @@ static void client_process_packet(NETPACKET *packet)
 						// apply snapshot, cycle pointers
 						recived_snapshots++;
 						
+
+						if(current_tick > 0)
+							snaploss += game_tick-current_tick-1;
+						
+						current_tick = game_tick;
+						
 						// we got two snapshots until we see us self as connected
 						if(recived_snapshots <= 2)
 						{
 							snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
 							snapshots[SNAP_CURRENT] = snap;
-							snapshot_start_time = time_get();
 						}
 						
 						if(recived_snapshots == 2)
@@ -699,7 +728,8 @@ static void client_process_packet(NETPACKET *packet)
 						int64 t = now - game_tick*time_freq()/50;
 						if(game_start_time == -1 || t < game_start_time)
 						{
-							dbg_msg("client", "adjusted time");
+							if(config.debug)
+								dbg_msg("client", "adjusted time");
 							game_start_time = t;
 						}
 						
@@ -707,11 +737,6 @@ static void client_process_packet(NETPACKET *packet)
 						float current_latency = (now-wanted)/(float)time_freq();
 						latency = latency*0.95f+current_latency*0.05f;
 						
-						//if(recived_snapshots > 2)
-						//	modc_newsnapshot();
-						
-						snapshot_part = 0;
-						
 						// ack snapshot
 						msg_pack_start_system(NETMSG_SNAPACK, 0);
 						msg_pack_int(game_tick);
@@ -781,14 +806,14 @@ static void client_run(const char *direct_connect_server)
 	if(!client_load_data())
 		return;
 
+	// init menu
+	modmenu_init(); // TODO: remove
+	
 	// init snapshotting
 	snap_init();
 	
 	// init the mod
 	modc_init();
-
-	// init menu
-	modmenu_init(); // TODO: remove
 	
 	// open socket
 	NETADDR4 bindaddr;
@@ -837,7 +862,8 @@ static void client_run(const char *direct_connect_server)
 					{
 						snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
 						snapshots[SNAP_CURRENT] = next;
-						snapshot_start_time = t;
+						if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
+							modc_newsnapshot();
 
 						if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
 							modc_newsnapshot();
@@ -854,12 +880,25 @@ static void client_run(const char *direct_connect_server)
 					break;
 				}
 			}
+			
+			if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
+			{
+				int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50;
+				if(latency > 0)
+					curtick_start += (int64)(time_freq()*(latency*1.1f));
+					
+				int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50;
+				if(latency > 0)
+					prevtick_start += (int64)(time_freq()*(latency*1.1f));
+					
+				intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start);
+			}
 		}
 
 		// send input
 		if(client_state() == CLIENTSTATE_ONLINE)
 		{
-			if(server_spam_address)
+			if(config.stress&1 && client_localtime() > 10.0f)
 				client_disconnect();
 			
 			if(input_is_changed || time_get() > last_input+time_freq())
@@ -870,8 +909,8 @@ static void client_run(const char *direct_connect_server)
 			}
 		}
 		
-		if(client_state() == CLIENTSTATE_OFFLINE && server_spam_address)
-			client_connect(server_spam_address);
+		if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0)
+			client_connect(config.cl_stress_server);
 		
 		// update input
 		inp_update();
@@ -933,10 +972,19 @@ static void client_run(const char *direct_connect_server)
 		client_serverbrowse_update();
 		
 		// render
-		client_render();
-		
-		// swap the buffers
-		gfx_swap();
+		if(config.stress)
+		{
+			if((frames%10) == 0)
+			{
+				client_render();
+				gfx_swap();
+			}
+		}
+		else
+		{
+			client_render();
+			gfx_swap();
+		}
 		
 		// check conditions
 		if(client_state() == CLIENTSTATE_QUITING)
@@ -948,8 +996,11 @@ static void client_run(const char *direct_connect_server)
 		
 		if(reporttime < time_get())
 		{
-			dbg_msg("client/report", "fps=%.02f netstate=%d",
-				frames/(float)(reportinterval/time_freq()), net.state());
+			if(config.debug)
+			{
+				dbg_msg("client/report", "fps=%.02f netstate=%d",
+					frames/(float)(reportinterval/time_freq()), net.state());
+			}
 			frames = 0;
 			reporttime += reportinterval;
 		}
@@ -977,12 +1028,24 @@ int main(int argc, char **argv)
 	dbg_msg("client", "starting...");
 	
 	config_reset();
+
 #ifdef CONF_PLATFORM_MACOSX
-		config_load("~/.teewars");
+	const char *config_filename = "~/.teewars";
 #else
-		config_load("default.cfg");
+	const char *config_filename = "default.cfg";
 #endif
 
+	for(int i = 1; i < argc; i++)
+	{
+		if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
+		{
+			config_filename = argv[i+1];
+			i++;
+		}
+	}
+
+	config_load(config_filename);
+
 	const char *direct_connect_server = 0x0;
 	snd_set_master_volume(config.volume / 255.0f);
 	bool editor = false;
@@ -999,28 +1062,12 @@ int main(int argc, char **argv)
 			i++;
 			direct_connect_server = argv[i];
 		}
-		else if(argv[i][0] == '-' && argv[i][1] == 's' && argv[i][2] == 0 && argc - i > 1)
-		{
-			// -s SERVER:PORT
-			i++;
-			server_spam_address = argv[i];
-		}
-		else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1)
-		{
-			// -n NAME
-			i++;
-			config_set_player_name(&config, argv[i]);
-		}
-		else if(argv[i][0] == '-' && argv[i][1] == 'w' && argv[i][2] == 0)
-		{
-			// -w
-			config.gfx_fullscreen = 0;
-		}
-		
 		else if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0)
 		{
 			editor = true;
 		}
+		else
+			config_set(argv[i]);
 	}
 	
 	if(editor)
diff --git a/src/engine/client/gfx.cpp b/src/engine/client/gfx.cpp
index dafdfa06..6a096139 100644
--- a/src/engine/client/gfx.cpp
+++ b/src/engine/client/gfx.cpp
@@ -122,6 +122,12 @@ bool gfx_init()
 	screen_width = config.gfx_screen_width;
 	screen_height = config.gfx_screen_height;
 
+	if(config.stress)
+	{
+		screen_width = 320;
+		screen_height = 240;
+	}
+	
 	if(config.gfx_fullscreen)
 	{
 		if(!context.create(screen_width, screen_height, 24, 0, 0, 0, opengl::context::FLAG_FULLSCREEN))
@@ -138,7 +144,12 @@ bool gfx_init()
 			return false;
 		}
 	}
-
+	
+	context.set_title("Teewars");
+	
+	// We don't want to see the window when we run the stress testing
+	if(config.stress)
+		context.iconify();
 	
 	// Init vertices
 	if (vertices)
@@ -146,7 +157,6 @@ bool gfx_init()
 	vertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertex_buffer_size, 1);
 	num_vertices = 0;
 
-	context.set_title("---");
 
 	/*
 	dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(),
@@ -250,6 +260,7 @@ int gfx_get_video_modes(video_mode *list, int maxcount)
 		mem_copy(list, fakemodes, sizeof(fakemodes));
 		return min((int)(sizeof(fakemodes)/sizeof(video_mode)), maxcount);
 	}
+	
 	return context.getvideomodes((opengl::videomode *)list, maxcount);
 }
 
@@ -321,7 +332,8 @@ int gfx_load_texture_raw(int w, int h, int format, const void *data)
 		}
 	}
 	
-	dbg_msg("gfx", "%d = %dx%d", tex, w, h);
+	if(config.debug)
+		dbg_msg("gfx", "%d = %dx%d", tex, w, h);
 	
 	// set data and return
 	if(config.gfx_texture_compression && GLEW_ARB_texture_compression)
@@ -790,64 +802,93 @@ double extra_kerning[256*256] = {0};
 
 pretty_font *current_font = &default_font;
 
-void gfx_pretty_text(float x, float y, float size, const char *text, int max_width)
+static int word_length(const char *text)
 {
+	int s = 1;
+	while(1)
+	{
+		if(*text == 0)
+			return s-1;
+		if(*text == '\n' || *text == '\t' || *text == ' ')
+			return s;
+		text++;
+		s++;
+	}
+}
+
+float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length)
+{
+	const unsigned char *text = (unsigned char *)text_;
 	const float spacing = 0.05f;
 	gfx_texture_set(current_font->font_texture);
 	gfx_quads_begin();
 	
-	float startx = x;
+	if(length < 0)
+		length = strlen(text_);
 	
-	while (*text)
+	while(length)
 	{
 		const int c = *text;
+		text++;
+	
+		const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c];
+
+		x -= size * current_font->m_CharStartTable[c];
 		
-		if(c == '\n')
-		{
-			x = startx;
-			y += size;
-		}
-		else
-		{
-			const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c];
+		gfx_quads_setsubset(
+			(c%16)/16.0f, // startx
+			(c/16)/16.0f, // starty
+			(c%16)/16.0f+1.0f/16.0f, // endx
+			(c/16)/16.0f+1.0f/16.0f); // endy
 
-			x -= size * current_font->m_CharStartTable[c];
-			
-			gfx_quads_setsubset(
-				(c%16)/16.0f, // startx
-				(c/16)/16.0f, // starty
-				(c%16)/16.0f+1.0f/16.0f, // endx
-				(c/16)/16.0f+1.0f/16.0f); // endy
+		gfx_quads_drawTL(x, y, size, size);
 
-			gfx_quads_drawTL(x, y, size, size);
+		double x_nudge = 0;
+		if(length > 1 && text[1])
+			x_nudge = extra_kerning[text[0] + text[1] * 256];
 
-			double x_nudge = 0;
-			if (text[1])
-				x_nudge = extra_kerning[text[0] + text[1] * 256];
+		x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size;
+		length--;
+	}
 
-			x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size;
+	gfx_quads_end();
+	
+	return x;
+}
 
-			if (max_width != -1 && x - startx > max_width)
+void gfx_pretty_text(float x, float y, float size, const char *text, int max_width)
+{
+	if(max_width == -1)
+		gfx_pretty_text_raw(x, y, size, text, -1);
+	else
+	{
+		float startx = x;
+		while(*text)
+		{
+			int wlen = word_length(text);
+			float w = gfx_pretty_text_width(size, text, wlen);
+			if(x+w-startx > max_width)
 			{
+				y += size-2;
 				x = startx;
-				y += size - 2;
 			}
+			
+			x = gfx_pretty_text_raw(x, y, size, text, wlen);
+			
+			text += wlen;
 		}
-
-		text++;
 	}
-
-	gfx_quads_end();
 }
 
-float gfx_pretty_text_width(float size, const char *text, int length)
+float gfx_pretty_text_width(float size, const char *text_, int length)
 {
 	const float spacing = 0.05f;
 	float w = 0.0f;
+	const unsigned char *text = (unsigned char *)text_;
 
-	const char *stop;
+	const unsigned char *stop;
 	if (length == -1)
-		stop = text + strlen(text);
+		stop = text + strlen((char*)text);
 	else
 		stop = text + length;
 
diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp
index c28efe05..7b9efaa2 100644
--- a/src/engine/client/snd.cpp
+++ b/src/engine/client/snd.cpp
@@ -3,6 +3,7 @@
 #include <baselib/stream/file.h>
 
 #include <engine/interface.h>
+#include <engine/config.h>
 
 extern "C" {
 #include "../../wavpack/wavpack.h"
@@ -24,6 +25,16 @@ static const float GLOBAL_SOUND_DELAY = 0.05f;
 class sound_data
 {
 public:
+	sound_data() :
+		data(0x0),
+		num_samples(0),
+		rate(0),
+		channels(0),
+		sustain_start(-1),
+		sustain_end(-1),
+		last_played(0)
+	{ }
+
 	short *data;
 	int num_samples;
 	int rate;
@@ -33,13 +44,14 @@ public:
 	int64 last_played;
 };
 
-inline short clamp(int i)
+template<typename T>
+inline const T clamp(const T val, const T lower, const T upper)
 {
-	if(i > 0x7fff)
-		return 0x7fff;
-	if(i < -0x7fff)
-		return -0x7fff;
-	return i;
+	if(val > upper)
+		return upper;
+	if(val < lower)
+		return lower;
+	return val;
 }
 
 static class mixer : public audio_stream
@@ -65,34 +77,40 @@ public:
 	enum
 	{
 		MAX_CHANNELS=32,
+		MAX_FILL_FRAMES=256,
 	};
 
 	channel channels[MAX_CHANNELS];
+	int buffer[MAX_FILL_FRAMES*2];
 
-	void fill_mono(short *out, unsigned long frames, channel *c, float dv = 0.0f)
+	void fill_mono(int *out, unsigned long frames, channel *c, float dv = 0.0f)
 	{
+		float pl = clamp(1.0f - c->pan, 0.0f, 1.0f);
+		float pr = clamp(1.0f + c->pan, 0.0f, 1.0f);
+
 		for(unsigned long i = 0; i < frames; i++)
 		{
-			float p = (1.0f-(c->pan+1.0f)*0.5f);
-			int val = (int)(p*c->vol * master_volume * c->data->data[c->tick]);
-			out[i<<1] += (short)val;
-			out[(i<<1)+1] += (short)val;
+			float val = c->vol * master_volume * c->data->data[c->tick];
+
+			out[i<<1] += (int)(pl*val);
+			out[(i<<1)+1] += (int)(pr*val);
 			c->tick++;
 			c->vol += dv;
 			if(c->vol < 0.0f) c->vol = 0.0f;
 		}
 	}
 
-	void fill_stereo(short *out, unsigned long frames, channel *c, float dv = 0.0f)
+	void fill_stereo(int *out, unsigned long frames, channel *c, float dv = 0.0f)
 	{
+		float pl = clamp(1.0f - c->pan, 0.0f, 1.0f);
+		float pr = clamp(1.0f + c->pan, 0.0f, 1.0f);
+
 		for(unsigned long i = 0; i < frames; i++)
 		{
-			float pl = c->pan<0.0f?-c->pan:1.0f;
-			float pr = c->pan>0.0f?1.0f-c->pan:1.0f;
 			int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]);
 			int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]);
-			out[i<<1] += (short)vl;
-			out[(i<<1)+1] += (short)vr;
+			out[i<<1] += vl;
+			out[(i<<1)+1] += vr;
 			c->tick += 2;
 			c->vol += dv;
 			if(c->vol < 0.0f) c->vol = 0.0f;
@@ -103,10 +121,12 @@ public:
 	{
 		short *out = (short*)output;
 
+		dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer");
+
 		for(unsigned long i = 0; i < frames; i++)
 		{
-			out[i<<1] = 0;
-			out[(i<<1)+1] = 0;
+			buffer[i<<1] = 0;
+			buffer[(i<<1)+1] = 0;
 		}
 
 		for(int c = 0; c < MAX_CHANNELS; c++)
@@ -133,9 +153,9 @@ public:
 				}
 
 				if(channels[c].data->channels == 1)
-					fill_mono(out, to_fill, &channels[c], dv);
+					fill_mono(buffer, to_fill, &channels[c], dv);
 				else
-					fill_stereo(out, to_fill, &channels[c], dv);
+					fill_stereo(buffer, to_fill, &channels[c], dv);
 
 				if(channels[c].loop >= 0 &&
 						channels[c].data->sustain_start >= 0 &&
@@ -156,6 +176,12 @@ public:
 				filled += to_fill;
 			}
 		}
+
+		for(unsigned long i = 0; i < frames; i++)
+		{
+			out[i<<1] = (short)clamp(buffer[i<<1], -0x7fff, 0x7fff);
+			out[(i<<1)+1] = (short)clamp(buffer[(i<<1)+1], -0x7fff, 0x7fff);
+		}
 	}
 	
 	int play(sound_data *sound, unsigned loop, float vol, float pan)
@@ -262,7 +288,7 @@ int snd_load_wv(const char *filename)
 
 	char error[100];
 
-	file = fopen(filename, "r");
+	file = fopen(filename, "rb");
 
 	WavpackContext *context = WavpackOpenFileInput(read_data, error);
 	if (context)
@@ -453,7 +479,8 @@ int snd_load_wav(const char *filename)
 			{
 				unsigned char smpl[36];
 				unsigned char loop[24];
-				dbg_msg("sound/wav", "got sustain");
+				if(config.debug)
+					dbg_msg("sound/wav", "got sustain");
 
 				file.read(smpl, sizeof(smpl));
 				unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24));
@@ -482,7 +509,10 @@ int snd_load_wav(const char *filename)
 	}
 
 	if(id >= 0)
-		dbg_msg("sound/wav", "loaded %s", filename);
+	{
+		if(config.debug)
+			dbg_msg("sound/wav", "loaded %s", filename);
+	}
 	else
 		dbg_msg("sound/wav", "failed to load %s", filename);