diff --git a/Makefile b/Makefile index fb9c1a7..54d17ce 100644 --- a/Makefile +++ b/Makefile @@ -259,22 +259,8 @@ ifneq ($(BUILD_CLIENT),0) endif # Add svn version info -USE_SVN= -ifeq ($(wildcard .svn),.svn) - SVN_REV=$(shell LANG=C svnversion .) - ifneq ($(SVN_REV),) - VERSION:=$(VERSION)_SVN$(SVN_REV) - USE_SVN=1 - endif -else -ifeq ($(wildcard .git/svn/.metadata),.git/svn/.metadata) - SVN_REV=$(shell LANG=C git svn info | awk '$$1 == "Revision:" {print $$2; exit 0}') - ifneq ($(SVN_REV),) - VERSION:=$(VERSION)_SVN$(SVN_REV) - endif -endif -endif - +SVN_REV=2214 +VERSION:=$(VERSION)_SVN$(SVN_REV) ############################################################################# # SETUP AND BUILD -- LINUX @@ -1378,6 +1364,7 @@ Q3OBJ = \ \ $(B)/client/snd_adpcm.o \ $(B)/client/snd_dma.o \ + $(B)/client/snd_dmahd.o \ $(B)/client/snd_mem.o \ $(B)/client/snd_mix.o \ $(B)/client/snd_wavelet.o \ diff --git a/Makefile.local b/Makefile.local new file mode 100644 index 0000000..0da7290 --- /dev/null +++ b/Makefile.local @@ -0,0 +1,16 @@ +# +# Build options for SWiSH +# + +CLIENTBIN=ioUrT_SWiSH + +BUILD_STANDALONE=1 +BUILD_CLIENT=1 +BUILD_CLIENT_SMP=1 +BUILD_SERVER=0 +BUILD_GAME_SO=0 +BUILD_GAME_QVM=0 +BUILD_BASEGAME=0 +BUILD_MISSIONPACK=0 +#USE_RENDERER_DLOPEN=0 +#USE_CURL_DLOPEN=0 diff --git a/code/client/cl_input.c b/code/client/cl_input.c index 4153b66..dfcbd6d 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -691,15 +691,15 @@ qboolean CL_ReadyToSendPacket( void ) { return qtrue; } - // check for exceeding cl_maxpackets - if ( cl_maxpackets->integer < 15 ) { - Cvar_Set( "cl_maxpackets", "15" ); - } else if ( cl_maxpackets->integer > 125 ) { - Cvar_Set( "cl_maxpackets", "125" ); + // check for exceeding sacc_maxpackets + if ( sacc_maxpackets->integer < 30 ) { + Cvar_Set( "sacc_maxpackets", "30" ); + } else if ( sacc_maxpackets->integer > 125 ) { + Cvar_Set( "sacc_maxpackets", "125" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK; delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime; - if ( delta < 1000 / cl_maxpackets->integer ) { + if ( delta < 1000 / sacc_maxpackets->integer ) { // the accumulated commands will go out in the next packet return qfalse; } @@ -771,12 +771,12 @@ void CL_WritePacket( void ) { // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server - if ( cl_packetdup->integer < 0 ) { - Cvar_Set( "cl_packetdup", "0" ); - } else if ( cl_packetdup->integer > 5 ) { - Cvar_Set( "cl_packetdup", "5" ); + if ( sacc_packetdup->integer < 0 ) { + Cvar_Set( "sacc_packetdup", "0" ); + } else if ( sacc_packetdup->integer > 5 ) { + Cvar_Set( "sacc_packetdup", "5" ); } - oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; + oldPacketNum = (clc.netchan.outgoingSequence - 1 - sacc_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 2606f6d..36ee313 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -63,6 +63,8 @@ cvar_t *rcon_client_password; cvar_t *rconAddress; cvar_t *cl_timeout; +cvar_t *sacc_maxpackets; +cvar_t *sacc_packetdup; cvar_t *cl_maxpackets; cvar_t *cl_packetdup; cvar_t *cl_timeNudge; @@ -3472,8 +3474,8 @@ void CL_Init( void ) { cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0); - cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE ); - cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); + sacc_maxpackets = Cvar_Get ("sacc_maxpackets", "125", CVAR_USERINFO | CVAR_ARCHIVE ); + sacc_packetdup = Cvar_Get ("sacc_packetdup", "1", CVAR_USERINFO | CVAR_ARCHIVE ); cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); @@ -3542,7 +3544,8 @@ void CL_Init( void ) { // userinfo Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); - Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("sacc_rate", "30000", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get ("rate", "30000", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); diff --git a/code/client/client.h b/code/client/client.h index 3204dc8..83bab8c 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -372,6 +372,8 @@ extern cvar_t *cl_nodelta; extern cvar_t *cl_debugMove; extern cvar_t *cl_noprint; extern cvar_t *cl_timegraph; +extern cvar_t *sacc_maxpackets; +extern cvar_t *sacc_packetdup; extern cvar_t *cl_maxpackets; extern cvar_t *cl_packetdup; extern cvar_t *cl_shownet; diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c index 1e8c559..0dca9c5 100644 --- a/code/client/snd_dma.c +++ b/code/client/snd_dma.c @@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "snd_local.h" #include "snd_codec.h" +#include "snd_dmahd.h" #include "client.h" void S_Update_( void ); @@ -55,14 +56,14 @@ channel_t s_channels[MAX_CHANNELS]; channel_t loop_channels[MAX_CHANNELS]; int numLoopChannels; -static int s_soundStarted; -static qboolean s_soundMuted; +int s_soundStarted; +qboolean s_soundMuted; dma_t dma; -static int listener_number; -static vec3_t listener_origin; -static vec3_t listener_axis[3]; +int listener_number; +vec3_t listener_origin; +vec3_t listener_axis[3]; int s_soundtime; // sample PAIRS int s_paintedtime; // sample PAIRS @@ -81,7 +82,7 @@ cvar_t *s_show; cvar_t *s_mixahead; cvar_t *s_mixPreStep; -static loopSound_t loopSounds[MAX_GENTITIES]; +loopSound_t loopSounds[MAX_GENTITIES]; static channel_t *freelist = NULL; int s_rawend[MAX_RAW_STREAMS]; @@ -1379,7 +1380,7 @@ void S_UpdateBackgroundTrack( void ) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend[0] - s_soundtime); // decide how much data needs to be read from the file - fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed; + fileSamples = (bufferSamples * dma.speed) / s_backgroundStream->info.rate; if (!fileSamples) return; @@ -1544,5 +1545,11 @@ qboolean S_Base_Init( soundInterface_t *si ) { si->MasterGain = S_Base_MasterGain; #endif +#ifndef NO_DMAHD + if (dmaHD_Enabled()) { + return dmaHD_Init(si); + } +#endif + return qtrue; } diff --git a/code/client/snd_dmahd.c b/code/client/snd_dmahd.c new file mode 100644 index 0000000..70de0c7 --- /dev/null +++ b/code/client/snd_dmahd.c @@ -0,0 +1,1531 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +/* +=========================================================================== +Changes to ID's source code: dmaHD HRTF Sound system +by p5yc0runn3r founder of Armed, Pissed & Dangerous clan. +=========================================================================== +*/ + +#ifndef NO_DMAHD + +/* + IMPLEMENTATION DETAILS: + + 1. Remove from static any unresolved extern globals: + From snd_dma.c remove static keyword for the following: + 'static' int s_soundStarted; + 'static' qboolean s_soundMuted; + 'static' int listener_number; + 'static' vec3_t listener_origin; + 'static' vec3_t listener_axis[3]; + 'static' loopSound_t loopSounds[MAX_GENTITIES]; + + 2. In "snd_local.h" add the following changes (channel_t structure changed + ch_side_t structure added): + typedef struct + { + int vol; // Must be first member due to union (see channel_t) + int offset; + int bassvol; + int bassoffset; + int reverbvol; + int reverboffset; + } ch_side_t; + + typedef struct + { + int allocTime; + int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix + int entnum; // to allow overriding a specific sound + int entchannel; // to allow overriding a specific sound + int master_vol; // 0-255 volume before spatialization + float dopplerScale; + float oldDopplerScale; + vec3_t origin; // only use if fixed_origin is set + qboolean fixed_origin; // use origin instead of fetching entnum's origin + sfx_t *thesfx; // sfx structure + qboolean doppler; + union + { + int leftvol; // 0-255 volume after spatialization + ch_side_t l; + }; + union + { + int rightvol; // 0-255 volume after spatialization + ch_side_t r; + }; + vec3_t sodrot; + } channel_t; + + 3. In "snd_local.h" add the following to sfx_t structure: + qboolean weaponsound; + + 4. #include "snd_dmahd.h" in snd_mem.c and snd_dma.c + + 5. Add the following at the top of S_LoadSound() in "snd_mem.c" after variables: + #ifndef NO_DMAHD + if (dmaHD_Enabled()) return dmaHD_LoadSound(sfx); + #endif + + 6. At the bottom of S_Base_Init() in "snd_dma.c" replace "return qtrue" with: + #ifndef NO_DMAHD + if (dmaHD_Enabled()) return dmaHD_Init(si); + #endif + + 7. Fix S_UpdateBackgroundTrack in "snd_dma.c" + Replace: + fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed; + With: + fileSamples = (bufferSamples * dma.speed) / s_backgroundStream->info.rate; + + 8. (Skip if source file does not exist) + Add the following to win_snd.c as a global extern: + extern cvar_t *s_khz; + + 9. (Skip if source file does not exist) + Fix SNDDMA_InitDS() in win_snd.c + Replace: + // if (s_khz->integer == 44) + // dma.speed = 44100; + // else if (s_khz->integer == 22) + // dma.speed = 22050; + // else + // dma.speed = 11025; + + dma.speed = 22050; + With: + if (s_khz->integer >= 44) dma.speed = 44100; + else if (s_khz->integer >= 32) dma.speed = 32000; + else if (s_khz->integer >= 24) dma.speed = 24000; + else if (s_khz->integer >= 22) dma.speed = 22050; + else dma.speed = 11025; + + 10. Compile, Run and Enjoy!! :) +*/ + +#include "client.h" +#include "snd_local.h" +#include "snd_codec.h" +#include "snd_dmahd.h" + +void dmaHD_Update_Mix( void ); +void S_UpdateBackgroundTrack(void); +void S_GetSoundtime(void); +qboolean S_ScanChannelStarts(void); + +// used in dmaEX mixer. +#define SOUND_FULLVOLUME 80 +#define SOUND_ATTENUATE 0.0007f + +extern channel_t s_channels[]; +extern channel_t loop_channels[]; +extern int numLoopChannels; + +extern int s_soundStarted; +extern qboolean s_soundMuted; + +extern int listener_number; +vec3_t g_listener_origin; +vec3_t g_listener_axis[3]; + +extern int s_soundtime; +extern int s_paintedtime; +static int dmaHD_inwater; + +// MAX_SFX may be larger than MAX_SOUNDS because of custom player sounds +#define MAX_SFX 4096 // This must be the same as the snd_dma.c +#define MAX_SOUNDBYTES (256*1024*1024) // 256MiB MAXIMUM... +extern sfx_t s_knownSfx[]; +extern int s_numSfx; + +extern cvar_t *s_mixahead; +extern cvar_t *s_mixPreStep; +cvar_t *dmaHD_Enable = NULL; +cvar_t *dmaHD_Interpolation; +cvar_t *dmaHD_Mixer; +cvar_t *dmaEX_StereoSeparation; + +extern loopSound_t loopSounds[]; + +#ifdef MAX_RAW_STREAMS +extern int s_rawend[MAX_RAW_STREAMS]; +extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES]; +#else +extern int s_rawend; +extern portable_samplepair_t s_rawsamples[]; +#endif + +#define DMAHD_PAINTBUFFER_SIZE 65536 +static portable_samplepair_t dmaHD_paintbuffer[DMAHD_PAINTBUFFER_SIZE]; +static int dmaHD_snd_vol; + +qboolean g_tablesinit = qfalse; +float g_voltable[256]; + +#define SMPCLAMP(a) (((a) < -32768) ? -32768 : ((a) > 32767) ? 32767 : (a)) +#define VOLCLAMP(a) (((a) < 0) ? 0 : ((a) > 255) ? 255 : (a)) + +void dmaHD_InitTables(void) +{ + if (!g_tablesinit) + { + int i; + float x, y; + + // Volume table. + for (i = 0; i < 256; i++) + { + x = (i * (9.0 / 256.0)) + 1.0; + y = 1.0 - log10f(x); + g_voltable[i] = y; + } + + g_tablesinit = qtrue; + } +} + +/* +=============================================================================== +PART#01: dmaHD: dma sound EXtension : MEMORY +=============================================================================== +*/ + +int g_dmaHD_allocatedsoundmemory = 0; + +/* +====================== +dmaHD_FreeOldestSound +====================== +*/ + +void dmaHD_FreeOldestSound( void ) +{ + int i, oldest, used; + sfx_t *sfx; + short* buffer; + + oldest = Com_Milliseconds(); + used = 0; + + for (i = 1 ; i < s_numSfx ; i++) + { + sfx = &s_knownSfx[i]; + if (sfx->inMemory && sfx->lastTimeUsed < oldest) + { + used = i; + oldest = sfx->lastTimeUsed; + } + } + + sfx = &s_knownSfx[used]; + + Com_DPrintf("dmaHD_FreeOldestSound: freeing sound %s\n", sfx->soundName); + + i = (sfx->soundLength + (sfx->soundLength >> 1)) * sizeof(short); + g_dmaHD_allocatedsoundmemory -= i; + if (g_dmaHD_allocatedsoundmemory < 0) g_dmaHD_allocatedsoundmemory = 0; + if ((buffer = (short*)sfx->soundData) != NULL) free(buffer); + sfx->inMemory = qfalse; + sfx->soundData = NULL; +} + +/* +====================== +dmaHD_AllocateSoundBuffer +====================== +*/ + +short* dmaHD_AllocateSoundBuffer(int samples) +{ + int bytes = samples * sizeof(short); + short* buffer; + + while (g_dmaHD_allocatedsoundmemory > 0 && + (g_dmaHD_allocatedsoundmemory + bytes) > MAX_SOUNDBYTES) dmaHD_FreeOldestSound(); + + if (s_numSfx >= (MAX_SFX - 8)) dmaHD_FreeOldestSound(); + + do + { + if ((buffer = (short*)malloc(bytes)) != NULL) break; + dmaHD_FreeOldestSound(); + } while(g_dmaHD_allocatedsoundmemory > 0); + + if (buffer == NULL) Com_Error (ERR_FATAL, "Out of Memory"); + + g_dmaHD_allocatedsoundmemory += bytes; + + return buffer; +} + +// ======================================================= +// DMAHD - Interpolation functions / No need to optimize a lot here since the sounds are interpolated +// once on load and not on playback. This also means that at least twice more memory is used. +// ======================================================= +// x0-----x1--t--x2-----x3 / x0/2/3/4 are know samples / t = 0.0 - 1.0 between x1 and x2 / returns y value at point t +static float dmaHD_InterpolateCubic(float x0, float x1, float x2, float x3, float t) { + float a0,a1,a2,a3;a0=x3-x2-x0+x1;a1=x0-x1-a0;a2=x2-x0;a3=x1; + return (a0*(t*t*t))+(a1*(t*t))+(a2*t)+(a3); +} +static float dmaHD_InterpolateHermite4pt3oX(float x0, float x1, float x2, float x3, float t) { + float c0,c1,c2,c3;c0=x1;c1=0.5f*(x2-x0);c2=x0-(2.5f*x1)+(2*x2)-(0.5f*x3);c3=(0.5f*(x3-x0))+(1.5f*(x1-x2)); + return (((((c3*t)+c2)*t)+c1)*t)+c0; +} +static float dmaHD_NormalizeSamplePosition(float t, int samples) { + while (t<0.0) t+=(float)samples; while (t>=(float)samples) t-=(float)samples; return t; +} +static int dmaHD_GetSampleRaw_8bit(int index, int samples, byte* data) { + return (index < 0 || index >= samples) ? 0 : (int)(((byte)(data[index])-128)<<8); +} +static int dmaHD_GetSampleRaw_16bit(int index, int samples, byte* data) { + return (index < 0 || index >= samples) ? 0 : (int)LittleShort(((short*)data)[index]); +} + +// Get only decimal part (a - floor(a)) +#define FLOAT_DECIMAL_PART(a) (a-(float)((int)a)) + +// t must be a float between 0 and samples +static int dmaHD_GetInterpolatedSampleHermite4pt3oX(float t, int samples, byte *data, + int (*dmaHD_GetSampleRaw)(int, int, byte*)) +{ + int x, val; + + t = dmaHD_NormalizeSamplePosition(t, samples); + // Get points + x = (int)t; + // Interpolate + val = (int)dmaHD_InterpolateHermite4pt3oX( + (float)dmaHD_GetSampleRaw(x - 1, samples, data), + (float)dmaHD_GetSampleRaw(x, samples, data), + (float)dmaHD_GetSampleRaw(x + 1, samples, data), + (float)dmaHD_GetSampleRaw(x + 2, samples, data), FLOAT_DECIMAL_PART(t)); + // Clamp + return SMPCLAMP(val); +} + +// t must be a float between 0 and samples +static int dmaHD_GetInterpolatedSampleCubic(float t, int samples, byte *data, + int (*dmaHD_GetSampleRaw)(int, int, byte*)) +{ + int x, val; + + t = dmaHD_NormalizeSamplePosition(t, samples); + // Get points + x = (int)t; + // Interpolate + val = (int)dmaHD_InterpolateCubic( + (float)dmaHD_GetSampleRaw(x - 1, samples, data), + (float)dmaHD_GetSampleRaw(x, samples, data), + (float)dmaHD_GetSampleRaw(x + 1, samples, data), + (float)dmaHD_GetSampleRaw(x + 2, samples, data), FLOAT_DECIMAL_PART(t)); + // Clamp + return SMPCLAMP(val); +} + +// t must be a float between 0 and samples +static int dmaHD_GetInterpolatedSampleLinear(float t, int samples, byte *data, + int (*dmaHD_GetSampleRaw)(int, int, byte*)) +{ + int x, val; + float c0, c1; + + t = dmaHD_NormalizeSamplePosition(t, samples); + + // Get points + x = (int)t; + + c0 = (float)dmaHD_GetSampleRaw(x, samples, data); + c1 = (float)dmaHD_GetSampleRaw(x+1, samples, data); + + val = (int)(((c1 - c0) * FLOAT_DECIMAL_PART(t)) + c0); + // No need to clamp for linear + return val; +} + +// t must be a float between 0 and samples +static int dmaHD_GetNoInterpolationSample(float t, int samples, byte *data, + int (*dmaHD_GetSampleRaw)(int, int, byte*)) +{ + int x; + + t = dmaHD_NormalizeSamplePosition(t, samples); + + // Get points + x = (int)t; + + if (FLOAT_DECIMAL_PART(t) > 0.5) x++; + + return dmaHD_GetSampleRaw(x, samples, data); +} + +int (*dmaHD_GetInterpolatedSample)(float t, int samples, byte *data, + int (*dmaHD_GetSampleRaw)(int, int, byte*)) = + dmaHD_GetInterpolatedSampleHermite4pt3oX; + +// ======================================================= +// ======================================================= + +/* +================ +dmaHD_ResampleSfx + +resample / decimate to the current source rate +================ +*/ +static void dmaHD_ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed) +{ + short* buffer; + int (*dmaHD_GetSampleRaw)(int, int, byte*) = + (inwidth == 2) ? dmaHD_GetSampleRaw_16bit : dmaHD_GetSampleRaw_8bit; + float stepscale, idx_smp, sample; + float lp_inva, lp_a, hp_a, lp_data, lp_last, hp_data, hp_last, hp_lastsample; + int outcount, idx_hp, bassoutcount, idx_lp; + + stepscale = (float)inrate/(float)dma.speed; + outcount = (int)((float)sfx->soundLength / stepscale); + // Always use even numbered length. + if ((outcount % 2) != 0) outcount += 1; + // Bass buffer output samples count + bassoutcount = (outcount / 2); + + // Create secondary buffer for bass sound while performing lowpass filter; + buffer = dmaHD_AllocateSoundBuffer(outcount + bassoutcount); + + // Check if this is a weapon sound. + sfx->weaponsound = (memcmp(sfx->soundName, "sound/weapons/", 14) == 0) ? qtrue : qfalse; + + // Get first sample from sound effect. + idx_smp = 0.0; + sample = dmaHD_GetInterpolatedSample(idx_smp, sfx->soundLength, data, dmaHD_GetSampleRaw); + idx_smp += stepscale; + + // Set up high pass filter. + idx_hp = 0; + hp_last = sample; + hp_lastsample = sample; + buffer[idx_hp++] = sample; + hp_a = 0.95f; + + // Set up Low pass filter. + idx_lp = outcount; + lp_last = sample; + lp_a = 0.03f; + lp_inva = (1 - lp_a); + + // Now do actual high/low pass on actual data. + for (;idx_hp < outcount; idx_hp++) + { + sample = dmaHD_GetInterpolatedSample(idx_smp, sfx->soundLength, data, dmaHD_GetSampleRaw); + idx_smp += stepscale; + + // High pass. + hp_data = hp_a * (hp_last + sample - hp_lastsample); + buffer[idx_hp] = SMPCLAMP(hp_data); + hp_last = hp_data; + hp_lastsample = sample; + + // Low pass. + lp_data = lp_a * (float)sample + lp_inva * lp_last; + if ((idx_hp % 2) != 0) buffer[idx_lp++] = SMPCLAMP((lp_last + lp_data) / 2.0f); + lp_last = lp_data; + } + + sfx->soundData = (sndBuffer*)buffer; + sfx->soundLength = outcount; +} + +//============================================================================= + +qboolean dmaHD_LoadSound(sfx_t *sfx) +{ + byte *data; + snd_info_t info; + + // Player specific sounds are never directly loaded. + if (sfx->soundName[0] == '*') return qfalse; + + // Load it in. + if (!(data = S_CodecLoad(sfx->soundName, &info))) return qfalse; + + // Information + Com_DPrintf("^3Loading sound: ^7%s", sfx->soundName); + /* wtf is this drunken shit? + if (info.width == 1) + Com_DPrintf(" [^28^3bit ^7-> ^216^3bit]", sfx->soundName); + */ + if (info.rate != dma.speed) + Com_DPrintf(" [^2%d^3Hz ^7-> ^2%d^3Hz]", info.rate, dma.speed); + Com_DPrintf("\n"); + + sfx->lastTimeUsed = Com_Milliseconds() + 1; + + // Do not compress. + sfx->soundCompressionMethod = 0; + sfx->soundLength = info.samples; + sfx->soundData = NULL; + dmaHD_ResampleSfx(sfx, info.rate, info.width, data + info.dataofs, qfalse); + + // Free data allocated by Codec + // This used to be Z_Free() but snd_mem.c now uses temp hunk memory + // for codec load. + Hunk_FreeTempMemory(data); + + return qtrue; +} + +/* +=============================================================================== +PART#02: dmaHD: dma sound EXtension : Mixing +=============================================================================== +*/ + +static void dmaHD_PaintChannelFrom16_HHRTF(channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset, int chan) +{ + int data, vol, i, so, c, len; + portable_samplepair_t *samp = &dmaHD_paintbuffer[bufferOffset]; + int* rawsamps; + short *samples; + ch_side_t* chs = (chan == 0) ? &ch->l : &ch->r; + + if (dmaHD_snd_vol <= 0) return; + + if (chs->bassvol > 0) + { + // Process low frequency + samples = &((short*)sc->soundData)[sc->soundLength]; // Select bass frequency offset (just after high frequency) + len = sc->soundLength >> 1; // Divide length by 2 + so = (sampleOffset - chs->bassoffset) >> 1; + c = count >> 1; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + vol = chs->bassvol * dmaHD_snd_vol; + + rawsamps = (int*)samp; rawsamps += chan; + for (i = 0; i < c; i++) + { + data = (samples[so++] * vol) >> 8; + *rawsamps += data; rawsamps += 2; + *rawsamps += data; rawsamps += 2; + } + } + } + + if (chs->vol > 0) + { + // Process high frequency. + samples = (short*)sc->soundData; // Select high frequency offset. + len = sc->soundLength; + so = sampleOffset - chs->offset; + c = count; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + vol = chs->vol * dmaHD_snd_vol; + + rawsamps = (int*)samp; rawsamps += chan; + for (i = 0; i < c; i++) { *rawsamps += (samples[so++] * vol) >> 8; rawsamps += 2; } + } + } +} + +static void dmaHD_PaintChannelFrom16_dmaEX2(channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset) +{ + int data, rvol, lvol, i, j, so, c, len; + portable_samplepair_t *samp = &dmaHD_paintbuffer[bufferOffset]; + short *samples; + + if (dmaHD_snd_vol <= 0) return; + + if (ch->l.bassvol > 0) + { + // Process low frequency. + samples = &((short*)sc->soundData)[sc->soundLength]; // Select bass frequency offset (just after high frequency) + len = sc->soundLength >> 1; // Divide length by 2 + so = (sampleOffset - ch->l.offset) >> 1; + c = count >> 1; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + lvol = ch->l.bassvol * dmaHD_snd_vol; + + //#pragma omp parallel for private(data, j) + for (i = 0; i < c; i++) + { + data = (samples[so + i] * lvol) >> 8; + j = (i << 1); + samp[j].left += data; + samp[j].right += data; + samp[j + 1].left += data; + samp[j + 1].right += data; + } + } + } + + if (ch->l.vol > 0 || ch->r.vol > 0) + { + // Process high frequency. + samples = (short*)sc->soundData; // Select high frequency offset. + len = sc->soundLength; + so = sampleOffset - ch->l.offset; + c = count; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + lvol = ch->l.vol * dmaHD_snd_vol; + rvol = ch->r.vol * dmaHD_snd_vol; + + // Behind viewer? + if (ch->fixed_origin && ch->sodrot[0] < 0) + { + if (ch->r.vol > ch->l.vol) lvol = -lvol; + else rvol = -rvol; + } + + //#pragma omp parallel for + for (i = 0; i < c; i++) + { + samp[i].left += (samples[so + i] * lvol) >> 8; + samp[i].right += (samples[so + i] * rvol) >> 8; + } + } + } + + // Process high frequency reverb. + if (ch->l.reverbvol > 0 || ch->r.reverbvol > 0) + { + samples = (short*)sc->soundData; // Select high frequency offset. + len = sc->soundLength; + so = sampleOffset - ch->l.reverboffset; + c = count; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes for reverb. + lvol = ch->l.reverbvol * dmaHD_snd_vol; + rvol = ch->r.reverbvol * dmaHD_snd_vol; + + //#pragma omp parallel for + for (i = 0; i < c; i++) + { + samp[i].left += (samples[so + i] * lvol) >> 8; + samp[i].right += (samples[so + i] * rvol) >> 8; + } + } + } +} + +static void dmaHD_PaintChannelFrom16_dmaEX(channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset) +{ + int ldata, rdata, rvol, lvol, i, j, so, c, len; + portable_samplepair_t *samp = &dmaHD_paintbuffer[bufferOffset]; + short *samples; + + if (dmaHD_snd_vol <= 0) return; + + if (ch->l.vol > 0 || ch->r.vol > 0) + { + // Process low frequency. + samples = &((short*)sc->soundData)[sc->soundLength]; // Select bass frequency offset (just after high frequency) + len = sc->soundLength >> 1; // Divide length by 2 + so = (sampleOffset - ch->l.offset) >> 1; + c = count >> 1; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + lvol = ch->l.vol * dmaHD_snd_vol; + rvol = ch->r.vol * dmaHD_snd_vol; + + // Behind viewer? + if (ch->fixed_origin && ch->sodrot[0] < 0) + { + if (lvol < rvol) lvol = -lvol; else rvol = -rvol; + } + + //#pragma omp parallel for private(ldata, rdata, j) + for (i = 0; i < c; i++) + { + ldata = (samples[so + i] * lvol) >> 8; + rdata = (samples[so + i] * rvol) >> 8; + j = (i << 1); + samp[j].left += ldata; + samp[j].right += rdata; + samp[j + 1].left += ldata; + samp[j + 1].right += rdata; + } + } + + // Process high frequency. + samples = (short*)sc->soundData; // Select high frequency offset. + len = sc->soundLength; + so = sampleOffset - ch->l.offset; + c = count; + if (so < 0) { c += so; so = 0; } // [c -= (-so)] == [c += so] + if ((so + c) > len) c = len - so; + if (c > 0) + { + // Calculate volumes. + lvol = ch->l.vol * dmaHD_snd_vol; + rvol = ch->r.vol * dmaHD_snd_vol; + + // Behind viewer? + if (ch->fixed_origin && ch->sodrot[0] < 0) + { + if (lvol < rvol) lvol = -lvol; else rvol = -rvol; + } + + //#pragma omp parallel for + for (i = 0; i < c; i++) + { + samp[i].left += (samples[so + i] * lvol) >> 8; + samp[i].right += (samples[so + i] * rvol) >> 8; + } + } + } +} + +static void dmaHD_PaintChannelFrom16(channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset) +{ + switch (dmaHD_Mixer->integer) + { + // HHRTF + case 10: + case 11: + //#pragma omp parallel sections + { + //#pragma omp section + { + dmaHD_PaintChannelFrom16_HHRTF(ch, sc, count, sampleOffset, bufferOffset, 0); // LEFT + } + //#pragma omp section + { + dmaHD_PaintChannelFrom16_HHRTF(ch, sc, count, sampleOffset, bufferOffset, 1); // RIGHT + } + } + break; + // dmaEX2 + case 20: + dmaHD_PaintChannelFrom16_dmaEX2(ch, sc, count, sampleOffset, bufferOffset); + break; + case 21: + // No reverb. + ch->l.reverbvol = ch->r.reverbvol = 0; + dmaHD_PaintChannelFrom16_dmaEX2(ch, sc, count, sampleOffset, bufferOffset); + break; + // dmaEX + case 30: + dmaHD_PaintChannelFrom16_dmaEX(ch, sc, count, sampleOffset, bufferOffset); + break; + } +} + +void dmaHD_TransferPaintBuffer(int endtime) +{ + int lpos; + int ls_paintedtime; + int i; + int val; + int* snd_p; + int snd_linear_count; + short* snd_out; + short* snd_outtmp; + unsigned long *pbuf = (unsigned long *)dma.buffer; + + snd_p = (int*)dmaHD_paintbuffer; + ls_paintedtime = s_paintedtime; + + while (ls_paintedtime < endtime) + { + // handle recirculating buffer issues + lpos = ls_paintedtime & ((dma.samples >> 1) - 1); + + snd_out = (short *)pbuf + (lpos << 1); + + snd_linear_count = (dma.samples >> 1) - lpos; + if (ls_paintedtime + snd_linear_count > endtime) + snd_linear_count = endtime - ls_paintedtime; + + snd_linear_count <<= 1; + + // write a linear blast of samples + for (snd_outtmp = snd_out, i = 0; i < snd_linear_count; ++i) + { + val = *snd_p++ >> 8; + *snd_outtmp++ = SMPCLAMP(val); + } + + ls_paintedtime += (snd_linear_count>>1); + + if( CL_VideoRecording( ) ) + CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 ); + } +} + +void dmaHD_PaintChannels( int endtime ) +{ + int i; + int end; + channel_t *ch; + sfx_t *sc; + int ltime, count; + int sampleOffset; +#ifdef MAX_RAW_STREAMS + int stream; +#endif + + dmaHD_snd_vol = +#ifdef MAX_RAW_STREAMS // For using Mitsu's build... + (s_muted->integer) ? 0 : +#endif + s_volume->value*256; + + while ( s_paintedtime < endtime ) + { + // if paintbuffer is smaller than DMA buffer + // we may need to fill it multiple times + end = endtime; + if ( endtime - s_paintedtime > DMAHD_PAINTBUFFER_SIZE ) + { + end = s_paintedtime + DMAHD_PAINTBUFFER_SIZE; + } + +#ifdef MAX_RAW_STREAMS + // clear the paint buffer and mix any raw samples... + Com_Memset(dmaHD_paintbuffer, 0, sizeof (dmaHD_paintbuffer)); + for (stream = 0; stream < MAX_RAW_STREAMS; stream++) + { + if (s_rawend[stream] >= s_paintedtime) + { + // copy from the streaming sound source + const portable_samplepair_t *rawsamples = s_rawsamples[stream]; + const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream]; + for ( i = s_paintedtime ; i < stop ; i++ ) + { + const int s = i&(MAX_RAW_SAMPLES-1); + dmaHD_paintbuffer[i-s_paintedtime].left += rawsamples[s].left; + dmaHD_paintbuffer[i-s_paintedtime].right += rawsamples[s].right; + } + } + } +#else + // clear the paint buffer to either music or zeros + if ( s_rawend < s_paintedtime ) + { + Com_Memset(dmaHD_paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t)); + } + else + { + // copy from the streaming sound source + int s; + int stop; + + stop = (end < s_rawend) ? end : s_rawend; + + for ( i = s_paintedtime ; i < stop ; i++ ) + { + s = i&(MAX_RAW_SAMPLES-1); + dmaHD_paintbuffer[i-s_paintedtime].left = s_rawsamples[s].left; + dmaHD_paintbuffer[i-s_paintedtime].right = s_rawsamples[s].right; + } + for ( ; i < end ; i++ ) + { + dmaHD_paintbuffer[i-s_paintedtime].left = 0; + dmaHD_paintbuffer[i-s_paintedtime].right = 0; + } + } +#endif + + // paint in the channels. + ch = s_channels; + for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) + { + if (!ch->thesfx) continue; + + ltime = s_paintedtime; + sc = ch->thesfx; + sampleOffset = ltime - ch->startSample; + count = end - ltime; + if (count > 0) dmaHD_PaintChannelFrom16(ch, sc, count, sampleOffset, 0); + } + + // paint in the looped channels. + ch = loop_channels; + for (i = 0; i < numLoopChannels ; i++, ch++) + { + if (!ch->thesfx) continue; + + ltime = s_paintedtime; + sc = ch->thesfx; + + if (sc->soundData == NULL || sc->soundLength == 0) continue; + // we might have to make two passes if it is a looping sound effect and the end of the sample is hit + do + { + sampleOffset = (ltime % sc->soundLength); + count = end - ltime; + if (count > 0) + { + dmaHD_PaintChannelFrom16(ch, sc, count, sampleOffset, ltime - s_paintedtime); + ltime += count; + } + } while (ltime < end); + } + + // transfer out according to DMA format + dmaHD_TransferPaintBuffer(end); + s_paintedtime = end; + } +} + +/* +=============================================================================== +PART#03: dmaHD: dma sound EXtension : main +=============================================================================== +*/ + + +/* +================= +dmaHD_SpatializeReset + +Reset/Prepares channel before calling dmaHD_SpatializeOrigin +================= +*/ +void dmaHD_SpatializeReset (channel_t* ch) +{ + VectorClear(ch->sodrot); + memset(&ch->l, 0, sizeof(ch_side_t)); + memset(&ch->r, 0, sizeof(ch_side_t)); +} + +/* +================= +dmaHD_SpatializeOrigin + +Used for spatializing s_channels +================= +*/ + +#define CALCVOL(dist) (((tmp = (int)((float)ch->master_vol * g_voltable[ \ + (((idx = (dist / iattenuation)) > 255) ? 255 : idx)])) < 0) ? 0 : tmp) +#define CALCSMPOFF(dist) (dist * dma.speed) >> ismpshift + +void dmaHD_SpatializeOrigin_HHRTF(vec3_t so, channel_t* ch, qboolean b3d) +{ + // so = sound origin/[d]irection/[n]ormalized/[rot]ated/[d]irection [l]eft/[d]irection [r]ight + vec3_t sod, sodl, sodr; + // lo = listener origin/[l]eft/[r]ight + vec3_t lol, lor; + // distance to ears/[l]eft/[r]ight + int distl, distr; // using int since calculations are integer based. + // temp, index + int tmp, idx; + float t; + + int iattenuation = (dmaHD_inwater) ? 2 : 6; + int ismpshift = (dmaHD_inwater) ? 19 : 17; + + // Increase attenuation for weapon sounds since they would be very loud! + if (ch->thesfx && ch->thesfx->weaponsound) iattenuation *= 2; + + // Calculate sound direction. + VectorSubtract(so, g_listener_origin, sod); + // Rotate sound origin to listener axis + VectorRotate(sod, g_listener_axis, ch->sodrot); + + // Origin for ears (~20cm apart) + lol[0] = 0.0; lol[1] = 40; lol[2] = 0.0; // left + lor[0] = 0.0; lor[1] = -40; lor[2] = 0.0; // right + + // Calculate sound direction. + VectorSubtract(ch->sodrot, lol, sodl); // left + VectorSubtract(ch->sodrot, lor, sodr); // right + + VectorNormalize(ch->sodrot); + // Calculate length of sound origin direction vector. + distl = (int)VectorNormalize(sodl); // left + distr = (int)VectorNormalize(sodr); // right + + // Close enough to be at full volume? + if (distl < 0) distl = 0; // left + if (distr < 0) distr = 0; // right + + // Distance 384units = 1m + // 340.29m/s (speed of sound at sea level) + // Do surround effect with doppler. + // 384.0 * 340.29 = 130671.36 + // Most similar is 2 ^ 17 = 131072; so shift right by 17 to divide by 131072 + + // 1484m/s in water + // 384.0 * 1484 = 569856 + // Most similar is 2 ^ 19 = 524288; so shift right by 19 to divide by 524288 + + ch->l.bassoffset = ch->l.offset = CALCSMPOFF(distl); // left + ch->r.bassoffset = ch->r.offset = CALCSMPOFF(distr); // right + // Calculate volume at ears + ch->l.bassvol = ch->l.vol = CALCVOL(distl); // left + ch->r.bassvol = ch->r.vol = CALCVOL(distr); // right + + // Sound originating from inside head of left ear (i.e. from right) + if (ch->sodrot[1] < 0) ch->l.vol *= (1.0 + (ch->sodrot[1] * 0.7f)); + // Sound originating from inside head of right ear (i.e. from left) + if (ch->sodrot[1] > 0) ch->r.vol *= (1.0 - (ch->sodrot[1] * 0.7f)); + + // Calculate HRTF function (lowpass filter) parameters + //if (ch->fixed_origin) + { + // Sound originating from behind viewer + if (ch->sodrot[0] < 0) + { + ch->l.vol *= (1.0 + (ch->sodrot[0] * 0.05f)); + ch->r.vol *= (1.0 + (ch->sodrot[0] * 0.05f)); + // 2ms max + //t = -ch->sodrot[0] * 0.04f; if (t > 0.005f) t = 0.005f; + t = (dma.speed * 0.001f); + ch->l.offset -= t; + ch->r.offset += t; + ch->l.bassoffset -= t; + ch->r.bassoffset += t; + } + } + + if (b3d) + { + // Sound originating from above viewer (decrease bass) + // Sound originating from below viewer (increase bass) + ch->l.bassvol *= ((1 - ch->sodrot[2]) * 0.5); + ch->r.bassvol *= ((1 - ch->sodrot[2]) * 0.5); + } + // Normalize volume + ch->l.vol *= 0.5; + ch->r.vol *= 0.5; + + if (dmaHD_inwater) + { + // Keep bass in water. + ch->l.vol *= 0.2; + ch->r.vol *= 0.2; + } +} + +void dmaHD_SpatializeOrigin_dmaEX2(vec3_t so, channel_t* ch) +{ + // so = sound origin/[d]irection/[n]ormalized/[rot]ated + vec3_t sod; + // distance to head + int dist; // using int since calculations are integer based. + // temp, index + int tmp, idx, vol; + vec_t dot; + + int iattenuation = (dmaHD_inwater) ? 2 : 6; + int ismpshift = (dmaHD_inwater) ? 19 : 17; + + // Increase attenuation for weapon sounds since they would be very loud! + if (ch->thesfx && ch->thesfx->weaponsound) iattenuation *= 2; + + // Calculate sound direction. + VectorSubtract(so, g_listener_origin, sod); + // Rotate sound origin to listener axis + VectorRotate(sod, g_listener_axis, ch->sodrot); + + VectorNormalize(ch->sodrot); + // Calculate length of sound origin direction vector. + dist = (int)VectorNormalize(sod); // left + + // Close enough to be at full volume? + if (dist < 0) dist = 0; // left + + // Distance 384units = 1m + // 340.29m/s (speed of sound at sea level) + // Do surround effect with doppler. + // 384.0 * 340.29 = 130671.36 + // Most similar is 2 ^ 17 = 131072; so shift right by 17 to divide by 131072 + + // 1484m/s in water + // 384.0 * 1484 = 569856 + // Most similar is 2 ^ 19 = 524288; so shift right by 19 to divide by 524288 + + ch->l.offset = CALCSMPOFF(dist); + // Calculate volume at ears + vol = CALCVOL(dist); + ch->l.vol = vol; + ch->r.vol = vol; + ch->l.bassvol = vol; + + dot = -ch->sodrot[1]; + ch->l.vol *= 0.5 * (1.0 - dot); + ch->r.vol *= 0.5 * (1.0 + dot); + + // Calculate HRTF function (lowpass filter) parameters + if (ch->fixed_origin) + { + // Reverberation + dist += 768; + ch->l.reverboffset = CALCSMPOFF(dist); + vol = CALCVOL(dist); + ch->l.reverbvol = vol; + ch->r.reverbvol = vol; + ch->l.reverbvol *= 0.5 * (1.0 + dot); + ch->r.reverbvol *= 0.5 * (1.0 - dot); + + // Sound originating from behind viewer: decrease treble + reverb + if (ch->sodrot[0] < 0) + { + ch->l.vol *= (1.0 + (ch->sodrot[0] * 0.5)); + ch->r.vol *= (1.0 + (ch->sodrot[0] * 0.5)); + } + else // from front... + { + // adjust reverb for each ear. + if (ch->sodrot[1] < 0) ch->r.reverbvol = 0; + else if (ch->sodrot[1] > 0) ch->l.reverbvol = 0; + } + + // Sound originating from above viewer (decrease bass) + // Sound originating from below viewer (increase bass) + ch->l.bassvol *= ((1 - ch->sodrot[2]) * 0.5); + } + else + { + // Reduce base volume by half to keep overall valume. + ch->l.bassvol *= 0.5; + } + + if (dmaHD_inwater) + { + // Keep bass in water. + ch->l.vol *= 0.2; + ch->r.vol *= 0.2; + } +} + +void dmaHD_SpatializeOrigin_dmaEX(vec3_t origin, channel_t* ch) +{ + vec_t dot; + vec_t dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + int tmp; + + const float dist_mult = SOUND_ATTENUATE; + + // calculate stereo seperation and distance attenuation + VectorSubtract(origin, g_listener_origin, source_vec); + + // VectorNormalize returns original length of vector and normalizes vector. + dist = VectorNormalize(source_vec); + dist -= SOUND_FULLVOLUME; + if (dist < 0) dist = 0; // close enough to be at full volume + dist *= dist_mult; // different attenuation levels + + VectorRotate(source_vec, g_listener_axis, ch->sodrot); + + dot = -ch->sodrot[1]; + // DMAEX - Multiply by the stereo separation CVAR. + dot *= dmaEX_StereoSeparation->value; + + rscale = 0.5 * (1.0 + dot); + lscale = 0.5 * (1.0 - dot); + if ( rscale < 0 ) rscale = 0; + if ( lscale < 0 ) lscale = 0; + + // add in distance effect + scale = (1.0 - dist) * rscale; + tmp = (ch->master_vol * scale); + if (tmp < 0) tmp = 0; + ch->r.vol = tmp; + + scale = (1.0 - dist) * lscale; + tmp = (ch->master_vol * scale); + if (tmp < 0) tmp = 0; + ch->l.vol = tmp; +} + +void dmaHD_SpatializeOrigin(vec3_t so, channel_t* ch) +{ + switch(dmaHD_Mixer->integer) + { + // HHRTF + case 10: dmaHD_SpatializeOrigin_HHRTF(so, ch, qtrue); break; + case 11: dmaHD_SpatializeOrigin_HHRTF(so, ch, qfalse); break; + // dmaEX2 + case 20: + case 21: dmaHD_SpatializeOrigin_dmaEX2(so, ch); break; + // dmaEX + case 30: dmaHD_SpatializeOrigin_dmaEX(so, ch); break; + } +} + +/* +============================================================== +continuous looping sounds are added each frame +============================================================== +*/ + +/* +================== +dmaHD_AddLoopSounds + +Spatialize all of the looping sounds. +All sounds are on the same cycle, so any duplicates can just +sum up the channel multipliers. +================== +*/ +void dmaHD_AddLoopSounds (void) +{ + int i, time; + channel_t *ch; + loopSound_t *loop; + static int loopFrame; + + numLoopChannels = 0; + + time = Com_Milliseconds(); + + loopFrame++; + //#pragma omp parallel for private(loop, ch) + for (i = 0 ; i < MAX_GENTITIES; i++) + { + if (numLoopChannels >= MAX_CHANNELS) continue; + + loop = &loopSounds[i]; + // already merged into an earlier sound + if (!loop->active || loop->mergeFrame == loopFrame) continue; + + // allocate a channel + ch = &loop_channels[numLoopChannels]; + + dmaHD_SpatializeReset(ch); + ch->fixed_origin = qtrue; + ch->master_vol = (loop->kill) ? 127 : 90; // 3D / Sphere + dmaHD_SpatializeOrigin(loop->origin, ch); + + loop->sfx->lastTimeUsed = time; + + ch->master_vol = 127; + // Clip volumes. + ch->l.vol = VOLCLAMP(ch->l.vol); + ch->r.vol = VOLCLAMP(ch->r.vol); + ch->l.bassvol = VOLCLAMP(ch->l.bassvol); + ch->r.bassvol = VOLCLAMP(ch->r.bassvol); + ch->l.reverbvol = VOLCLAMP(ch->l.reverbvol); + ch->r.reverbvol = VOLCLAMP(ch->r.reverbvol); + ch->thesfx = loop->sfx; + ch->doppler = qfalse; + + //#pragma omp critical + { + numLoopChannels++; + } + } +} + +//============================================================================= + +/* +============ +dmaHD_Respatialize + +Change the volumes of all the playing sounds for changes in their positions +============ +*/ +void dmaHD_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) +{ + int i; + channel_t *ch; + vec3_t origin; + + if (!s_soundStarted || s_soundMuted) return; + + dmaHD_inwater = inwater; + + listener_number = entityNum; + VectorCopy(head, g_listener_origin); + VectorCopy(axis[0], g_listener_axis[0]); + VectorCopy(axis[1], g_listener_axis[1]); + VectorCopy(axis[2], g_listener_axis[2]); + + // update spatialization for dynamic sounds + //#pragma omp parallel for private(ch) + for (i = 0 ; i < MAX_CHANNELS; i++) + { + ch = &s_channels[i]; + if (!ch->thesfx) continue; + + dmaHD_SpatializeReset(ch); + // Anything coming from the view entity will always be full volume + if (ch->entnum == listener_number) + { + ch->l.vol = ch->master_vol; + ch->r.vol = ch->master_vol; + ch->l.bassvol = ch->master_vol; + ch->r.bassvol = ch->master_vol; + switch(dmaHD_Mixer->integer) + { + case 10: case 11: case 20: case 21: + if (dmaHD_inwater) + { + ch->l.vol *= 0.2; + ch->r.vol *= 0.2; + } + break; + } + } + else + { + if (ch->fixed_origin) { VectorCopy( ch->origin, origin ); } + else { VectorCopy( loopSounds[ ch->entnum ].origin, origin ); } + + dmaHD_SpatializeOrigin(origin, ch); + } + } + + // add loopsounds + dmaHD_AddLoopSounds(); +} + +/* +============ +dmaHD_Update + +Called once each time through the main loop +============ +*/ +void dmaHD_Update( void ) +{ + if (!s_soundStarted || s_soundMuted) return; + // add raw data from streamed samples + S_UpdateBackgroundTrack(); + // mix some sound + dmaHD_Update_Mix(); +} + +void dmaHD_Update_Mix(void) +{ + unsigned endtime; + int samps; + static int lastTime = 0.0f; + int mixahead, op, thisTime, sane; + static int lastsoundtime = -1; + + if (!s_soundStarted || s_soundMuted) return; + + thisTime = Com_Milliseconds(); + + // Updates s_soundtime + S_GetSoundtime(); + + if (s_soundtime <= lastsoundtime) return; + lastsoundtime = s_soundtime; + + // clear any sound effects that end before the current time, + // and start any new sounds + S_ScanChannelStarts(); + + if ((sane = thisTime - lastTime) < 1) sane = 1; + mixahead = (int)((float)dma.speed * s_mixahead->value); + op = (int)((float)(dma.speed * sane) * 0.01); + if (op < mixahead) mixahead = op; + + // mix ahead of current position + endtime = s_soundtime + mixahead; + + // never mix more than the complete buffer + samps = dma.samples >> (dma.channels-1); + if ((endtime - s_soundtime) > samps) endtime = (s_soundtime + samps); + + SNDDMA_BeginPainting (); + + dmaHD_PaintChannels (endtime); + + SNDDMA_Submit (); + + lastTime = thisTime; +} + +/* +================ +dmaHD_Enabled +================ +*/ +qboolean dmaHD_Enabled(void) +{ + if (dmaHD_Enable == NULL) + dmaHD_Enable = Cvar_Get("dmaHD_enable", "1", CVAR_ARCHIVE); + + return (dmaHD_Enable->integer); +} + +// ==================================================================== +// User-setable variables +// ==================================================================== + +void dmaHD_SoundInfo(void) +{ + int i; + + Com_Printf("^7-^5-^4"); + for (i = 0; i < 38; i++) Com_Printf("-"); + + Com_Printf("\n^7dmaHD HRTF Sound System by p5yc0runn3r\n"); + + if (!s_soundStarted) + { + Com_Printf (" ^1Sound system not started...\n"); + } + else + { + Com_Printf(" ^4dma^5HD ^4Mixer type^7: ^5%d", dmaHD_Mixer->integer); + switch (dmaHD_Mixer->integer / 10) + { + case 1: Com_Printf(" ^7- ^5Hybrid^7-^5HRTF"); + switch (dmaHD_Mixer->integer % 10) + { + case 0: Com_Printf(" ^7[^53D^7]"); break; + case 1: Com_Printf(" ^7[^52D^7]"); break; + } + break; + case 2: Com_Printf(" ^7- ^4dma^5EX2"); + switch (dmaHD_Mixer->integer % 10) + { + case 1: Com_Printf(" ^7[^5No reverb^7]"); break; + } + break; + case 3: Com_Printf(" ^7- ^4dma^5EX"); + break; + } + Com_Printf("\n"); + Com_Printf(" ^5%d^4ch ^7- ^5%d^4Hz ^7- ^5%d^4bps\n", dma.channels, dma.speed, dma.samplebits); + } + Com_Printf("^7-^5-^4"); + for (i = 0; i < 38; i++) Com_Printf("-"); + + Com_Printf("^7\n" ); +} + +void dmaHD_SoundList(void) +{ + int i; + sfx_t *sfx; + + for (i = 0; i < 19; i++) Com_Printf("^7-^6-" ); + Com_Printf("\n" ); + Com_Printf("^2p5yc0runn3r's ^3dma^1HD ^7sound list\n"); + + if (s_numSfx > 0 || g_dmaHD_allocatedsoundmemory > 0) + { + for (sfx = s_knownSfx, i = 0; i < s_numSfx; i++, sfx++) + { + Com_Printf(" %s%s ^2%.2f^3KiB\n", + (sfx->inMemory ? S_COLOR_GREEN : S_COLOR_RED), sfx->soundName, + (float)sfx->soundLength / 1024.0f); + } + Com_Printf(" ^2%d^3sounds ^7in ^2%.2f^3MiB\n", s_numSfx, (float)g_dmaHD_allocatedsoundmemory / 1048576.0f); + } + else + { + Com_Printf(" ^1No sounds loaded yet\n"); + } + for (i = 0; i < 19; i++) Com_Printf("^7-^6-" ); + Com_Printf("\n" ); +} + + +/* +================ +dmaHD_Init +================ +*/ +qboolean dmaHD_Init(soundInterface_t *si) +{ + if (!si) return qfalse; + + // Return if not enabled. + if (!dmaHD_Enabled()) return qtrue; + + dmaHD_Mixer = Cvar_Get("dmaHD_mixer", "10", CVAR_ARCHIVE); + if (dmaHD_Mixer->integer != 10 && dmaHD_Mixer->integer != 11 && + dmaHD_Mixer->integer != 20 && dmaHD_Mixer->integer != 21 && + dmaHD_Mixer->integer != 30) + { + Cvar_Set("dmaHD_Mixer", "10"); + dmaHD_Mixer = Cvar_Get("dmaHD_mixer", "10", CVAR_ARCHIVE); + } + + dmaEX_StereoSeparation = Cvar_Get("dmaEX_StereoSeparation", "0.9", CVAR_ARCHIVE); + + if (dmaEX_StereoSeparation->value < 0.1) + { + Cvar_Set("dmaEX_StereoSeparation", "0.1"); + dmaEX_StereoSeparation = Cvar_Get("dmaEX_StereoSeparation", "0.9", CVAR_ARCHIVE); + } + else if (dmaEX_StereoSeparation->value > 2.0) + { + Cvar_Set("dmaEX_StereoSeparation", "2.0"); + dmaEX_StereoSeparation = Cvar_Get("dmaEX_StereoSeparation", "0.9", CVAR_ARCHIVE); + } + + + dmaHD_Interpolation = Cvar_Get("dmaHD_interpolation", "3", CVAR_ARCHIVE); + if (dmaHD_Interpolation->integer == 0) + { + dmaHD_GetInterpolatedSample = dmaHD_GetNoInterpolationSample; + } + else if (dmaHD_Interpolation->integer == 1) + { + dmaHD_GetInterpolatedSample = dmaHD_GetInterpolatedSampleLinear; + } + else if (dmaHD_Interpolation->integer == 2) + { + dmaHD_GetInterpolatedSample = dmaHD_GetInterpolatedSampleCubic; + } + else //if (dmaHD_Interpolation->integer == 3) // DEFAULT + { + dmaHD_GetInterpolatedSample = dmaHD_GetInterpolatedSampleHermite4pt3oX; + } + + dmaHD_InitTables(); + + // Override function pointers to dmaHD version, the rest keep base. + si->SoundInfo = dmaHD_SoundInfo; + si->Respatialize = dmaHD_Respatialize; + si->Update = dmaHD_Update; + si->SoundList = dmaHD_SoundList; + + return qtrue; +} + +#endif//NO_DMAHD diff --git a/code/client/snd_dmahd.h b/code/client/snd_dmahd.h new file mode 100644 index 0000000..e1e4776 --- /dev/null +++ b/code/client/snd_dmahd.h @@ -0,0 +1,17 @@ +/* + DMAHD Hybrid-HRTF Additions by p5yc0runn3r founder of + Armed, Pissed & Dangerous clan. +*/ + +#ifndef __SND_DMAHD_H__ +#define __SND_DMAHD_H__ + +#ifndef NO_DMAHD + +qboolean dmaHD_LoadSound(sfx_t *); +qboolean dmaHD_Enabled(void); +qboolean dmaHD_Init(soundInterface_t *); + +#endif + +#endif//__SND_DMAHD_H__ diff --git a/code/client/snd_local.h b/code/client/snd_local.h index 29783c1..c9969e4 100644 --- a/code/client/snd_local.h +++ b/code/client/snd_local.h @@ -59,6 +59,7 @@ typedef struct sfx_s { char soundName[MAX_QPATH]; int lastTimeUsed; struct sfx_s *next; + qboolean weaponsound; } sfx_t; typedef struct { @@ -87,14 +88,22 @@ typedef struct loopSound_s { int framenum; } loopSound_t; +typedef struct +{ + int vol; // Must be first member due to union (see channel_t) + int offset; + int bassvol; + int bassoffset; + int reverbvol; + int reverboffset; +} ch_side_t; + typedef struct { int allocTime; int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix int entnum; // to allow overriding a specific sound int entchannel; // to allow overriding a specific sound - int leftvol; // 0-255 volume after spatialization - int rightvol; // 0-255 volume after spatialization int master_vol; // 0-255 volume before spatialization float dopplerScale; float oldDopplerScale; @@ -102,6 +111,19 @@ typedef struct qboolean fixed_origin; // use origin instead of fetching entnum's origin sfx_t *thesfx; // sfx structure qboolean doppler; + union + { + int leftvol; // 0-255 volume after spatialization + + ch_side_t l; + }; + union + { + int rightvol; // 0-255 volume after spatialization + + ch_side_t r; + }; + vec3_t sodrot; } channel_t; diff --git a/code/client/snd_mem.c b/code/client/snd_mem.c index ee40a75..354efe5 100644 --- a/code/client/snd_mem.c +++ b/code/client/snd_mem.c @@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "snd_local.h" #include "snd_codec.h" +#include "snd_dmahd.h" #define DEF_COMSOUNDMEGS "8" @@ -81,7 +82,12 @@ void SND_setup(void) { cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE ); - scs = (cv->integer*1536); + // dmaHD optimization + if (cv->integer < 16) + Cvar_Set( "com_soundMegs", "16" ); + + // not needed by dmaHD + scs = (dmaHD_Enabled()) ? (2*1536) : (cv->integer*1536); buffer = malloc(scs*sizeof(sndBuffer) ); // allocate the stack based hunk allocator @@ -97,7 +103,7 @@ void SND_setup(void) { *(sndBuffer **)q = NULL; freelist = p + scs - 1; - Com_Printf("Sound memory manager started\n"); + Com_Printf("Sound memory manager started\n"); } void SND_shutdown(void) @@ -208,6 +214,11 @@ qboolean S_LoadSound( sfx_t *sfx ) snd_info_t info; // int size; +#ifndef NO_DMAHD + if (dmaHD_Enabled()) + return dmaHD_LoadSound(sfx); +#endif + // player specific sounds are never directly loaded if ( sfx->soundName[0] == '*') { return qfalse; diff --git a/code/game/g_bot.c b/code/game/g_bot.c index db495d1..fb5ed7d 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -589,7 +589,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); - Info_SetValueForKey( userinfo, "rate", "25000" ); + Info_SetValueForKey( userinfo, "sacc_rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); diff --git a/code/q3_ui/ui_network.c b/code/q3_ui/ui_network.c index 0b4bf83..3801782 100644 --- a/code/q3_ui/ui_network.c +++ b/code/q3_ui/ui_network.c @@ -65,7 +65,7 @@ typedef struct { menutext_s sound; menutext_s network; - menulist_s rate; + menulist_s sacc_rate; menubitmap_s back; } networkOptionsInfo_t; @@ -103,20 +103,20 @@ static void UI_NetworkOptionsMenu_Event( void* ptr, int event ) { break; case ID_RATE: - if( networkOptionsInfo.rate.curvalue == 0 ) { - trap_Cvar_SetValue( "rate", 2500 ); + if( networkOptionsInfo.sacc_rate.curvalue == 0 ) { + trap_Cvar_SetValue( "sacc_rate", 2500 ); } - else if( networkOptionsInfo.rate.curvalue == 1 ) { - trap_Cvar_SetValue( "rate", 3000 ); + else if( networkOptionsInfo.sacc_rate.curvalue == 1 ) { + trap_Cvar_SetValue( "sacc_rate", 3000 ); } - else if( networkOptionsInfo.rate.curvalue == 2 ) { - trap_Cvar_SetValue( "rate", 4000 ); + else if( networkOptionsInfo.sacc_rate.curvalue == 2 ) { + trap_Cvar_SetValue( "sacc_rate", 4000 ); } - else if( networkOptionsInfo.rate.curvalue == 3 ) { - trap_Cvar_SetValue( "rate", 5000 ); + else if( networkOptionsInfo.sacc_rate.curvalue == 3 ) { + trap_Cvar_SetValue( "sacc_rate", 5000 ); } - else if( networkOptionsInfo.rate.curvalue == 4 ) { - trap_Cvar_SetValue( "rate", 25000 ); + else if( networkOptionsInfo.sacc_rate.curvalue == 4 ) { + trap_Cvar_SetValue( "sacc_rate", 25000 ); } break; @@ -134,7 +134,7 @@ UI_NetworkOptionsMenu_Init */ static void UI_NetworkOptionsMenu_Init( void ) { int y; - int rate; + int sacc_rate; memset( &networkOptionsInfo, 0, sizeof(networkOptionsInfo) ); @@ -207,14 +207,14 @@ static void UI_NetworkOptionsMenu_Init( void ) { networkOptionsInfo.network.color = color_red; y = 240 - 1 * (BIGCHAR_HEIGHT+2); - networkOptionsInfo.rate.generic.type = MTYPE_SPINCONTROL; - networkOptionsInfo.rate.generic.name = "Data Rate:"; - networkOptionsInfo.rate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; - networkOptionsInfo.rate.generic.callback = UI_NetworkOptionsMenu_Event; - networkOptionsInfo.rate.generic.id = ID_RATE; - networkOptionsInfo.rate.generic.x = 400; - networkOptionsInfo.rate.generic.y = y; - networkOptionsInfo.rate.itemnames = rate_items; + networkOptionsInfo.sacc_rate.generic.type = MTYPE_SPINCONTROL; + networkOptionsInfo.sacc_rate.generic.name = "Data Rate:"; + networkOptionsInfo.sacc_rate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT; + networkOptionsInfo.sacc_rate.generic.callback = UI_NetworkOptionsMenu_Event; + networkOptionsInfo.sacc_rate.generic.id = ID_RATE; + networkOptionsInfo.sacc_rate.generic.x = 400; + networkOptionsInfo.sacc_rate.generic.y = y; + networkOptionsInfo.sacc_rate.itemnames = rate_items; networkOptionsInfo.back.generic.type = MTYPE_BITMAP; networkOptionsInfo.back.generic.name = ART_BACK0; @@ -234,24 +234,24 @@ static void UI_NetworkOptionsMenu_Init( void ) { Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.display ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.sound ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.network ); - Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.rate ); + Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.sacc_rate ); Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.back ); - rate = trap_Cvar_VariableValue( "rate" ); - if( rate <= 2500 ) { - networkOptionsInfo.rate.curvalue = 0; + sacc_rate = trap_Cvar_VariableValue( "sacc_rate" ); + if( sacc_rate <= 2500 ) { + networkOptionsInfo.sacc_rate.curvalue = 0; } - else if( rate <= 3000 ) { - networkOptionsInfo.rate.curvalue = 1; + else if( sacc_rate <= 3000 ) { + networkOptionsInfo.sacc_rate.curvalue = 1; } - else if( rate <= 4000 ) { - networkOptionsInfo.rate.curvalue = 2; + else if( sacc_rate <= 4000 ) { + networkOptionsInfo.sacc_rate.curvalue = 2; } - else if( rate <= 5000 ) { - networkOptionsInfo.rate.curvalue = 3; + else if( sacc_rate <= 5000 ) { + networkOptionsInfo.sacc_rate.curvalue = 3; } else { - networkOptionsInfo.rate.curvalue = 4; + networkOptionsInfo.sacc_rate.curvalue = 4; } } diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index fc667c0..fef73b2 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -27,29 +27,29 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // A user mod should never modify this file #ifdef STANDALONE - #define PRODUCT_NAME "iofoo3" - #define BASEGAME "foobar" - #define CLIENT_WINDOW_TITLE "changeme" - #define CLIENT_WINDOW_MIN_TITLE "changeme2" - #define HOMEPATH_NAME_UNIX ".foo" - #define HOMEPATH_NAME_WIN "FooBar" + #define PRODUCT_NAME "SWiSH_ioUrT-1.1" + #define BASEGAME "q3ut4" + #define CLIENT_WINDOW_TITLE "SWiSH_ioUrbanTerror" + #define CLIENT_WINDOW_MIN_TITLE "SWiSH_ioUrT" + #define HOMEPATH_NAME_UNIX ".q3a" + #define HOMEPATH_NAME_WIN "q3ut4" #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN - #define GAMENAME_FOR_MASTER "foobar" // must NOT contain whitespace -// #define LEGACY_PROTOCOL // You probably don't need this for your standalone game + #define GAMENAME_FOR_MASTER "Quake3Arena" // must NOT contain whitespace + #define LEGACY_PROTOCOL // You probably don't need this for your standalone game #else - #define PRODUCT_NAME "ioq3" - #define BASEGAME "baseq3" - #define CLIENT_WINDOW_TITLE "ioquake3" - #define CLIENT_WINDOW_MIN_TITLE "ioq3" + #define PRODUCT_NAME "SWiSH_ioUrT-1.1" + #define BASEGAME "q3ut4" + #define CLIENT_WINDOW_TITLE "SWiSH_ioUrbanTerror" + #define CLIENT_WINDOW_MIN_TITLE "SWiSH_ioUrT" #define HOMEPATH_NAME_UNIX ".q3a" - #define HOMEPATH_NAME_WIN "Quake3" + #define HOMEPATH_NAME_WIN "q3ut4" #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN #define GAMENAME_FOR_MASTER "Quake3Arena" #define LEGACY_PROTOCOL #endif // Heartbeat for dpmaster protocol. You shouldn't change this unless you know what you're doing -#define HEARTBEAT_FOR_MASTER "DarkPlaces" +#define HEARTBEAT_FOR_MASTER "QuakeArena-1" #define BASETA "missionpack" diff --git a/code/sdl/sdl_icon.h b/code/sdl/sdl_icon.h index 866c549..e1631e6 100644 --- a/code/sdl/sdl_icon.h +++ b/code/sdl/sdl_icon.h @@ -12,121 +12,146 @@ static const struct { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377w\0\0" - "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0" - "\0\377w\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377\210\0\0\377" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\210\0\0\377\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377\210\0\0" - "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\252\0\0" - "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0\377\210\0\0\377\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\252\0\0\377\0\0\0\0\273\0\0\377\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\231\0\0\377\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\273\0\0\377\314\0\0\377\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\231\0\0\377\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0" - "\377\314\0\0\377\0\0\0\0\335\0\0\377\314\0\0\377\231\0\0\377\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\231\0\0\377\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0\377\314\0\0\377\335" - "\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0\377\314\0\0\377\335\0\0\377\335" - "\0\0\377\273\0\0\377\231\0\0\377\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\231\0\0\377\252\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\252\0\0\377\273\0\0\377\335\0\0\377\335\0\0" - "\377\314\0\0\377\231\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\252\0\0\377\314\0\0\377\314\0\0\377\314\0\0\377\314\0\0\377" - "\314\0\0\377\314\0\0\377\314\0\0\377\314\0\0\377\0\0\0\0\252\0\0\377\252" - "\0\0\377\0\0\0\0\314\0\0\377\314\0\0\377\314\0\0\377\314\0\0\377\314\0\0" - "\377\314\0\0\377\314\0\0\377\314\0\0\377\252\0\0\377\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\210\0\0\377\231\0\0\377\273\0\0\377\314\0\0\377\314" - "\0\0\377\0\0\0\0\252\0\0\377\252\0\0\377\0\0\0\0\314\0\0\377\314\0\0\377" - "\273\0\0\377\231\0\0\377\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377" - "\273\0\0\377\0\0\0\0\252\0\0\377\252\0\0\377\0\0\0\0\273\0\0\377\210\0\0" - "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377\273\0\0\377" - "\0\0\0\0\231\0\0\377\252\0\0\377\0\0\0\0\273\0\0\377w\0\0\377\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\252\0\0\377\0\0\0\0\231\0\0" - "\377\231\0\0\377\0\0\0\0\252\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\252\0\0\377\0\0\0\0\210\0\0\377\231\0\0\377" - "\0\0\0\0\252\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\231\0\0\377\0\0\0\0\210\0\0\377\210\0\0\377\0\0\0\0\231\0\0" - "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231" - "\0\0\377\0\0\0\0w\0\0\377\210\0\0\377\0\0\0\0\231\0\0\377\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377\0\0\0\0\210" - "\0\0\377\210\0\0\377\0\0\0\0\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\0\0\377\0\0\0\0w\0\0\377\210\0\0\377" - "\0\0\0\0\210\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0w\0\0\377\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0w\0\0\377\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377\0\0\0\0" - "w\0\0\377w\0\0\377\0\0\0\0w\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0w\0\0\377\0\0\0\0w\0\0\377w\0\0\377\0\0\0\0w\0" - "\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0f\0\0\377f\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0f\0\0\377f\0\0\377" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0", + "\330\332\334\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\326\330\333\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\14Ws\237" + "\337?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377" + "?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`" + "\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377?`\224\377Ur\236" + "\342\377\377\377\15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\312\314\321S\0&p\377\0-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0" + "-t\377\0-t\377\0*r\377\0-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0" + "-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0-t\377\0&p\377\237\244\254h\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\302\306\314R\0'n\377\0" + ",r\377\0,r\377\0,r\377\0,r\377\0,r\377\0,r\377\0,r\377\0)p\377h|\235\377" + "Xq\230\377\14""6x\377\0'n\377\0,r\377\0,r\377\0,r\377\0,r\377\0,r\377\0," + "r\377\0,r\377\0,r\377\0,r\377\0'o\377\226\234\243j\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\275\301\306R\0&l\377\0+o\377\0+o\377\0" + "+o\377\0+o\377\0+o\377\0+o\377\0+o\377\0)o\377\251\250\247\377\235\235\234" + "\377\272\270\265\377\217\232\254\377If\223\377\0)n\377\0,o\377\0+o\377\0" + "+o\377\0+o\377\0+o\377\0+o\377\0+o\377\0&l\377\216\224\234d\0\0\0\0\325\325" + "\325\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\271\273\301R\0$i\377\0*l\377" + "\0*l\377\0*l\377\0*l\377\0*l\377\0*l\377\0*l\377Hd\217\377\227\226\225\377" + "\241\241\241\377\254\254\254\377\247\247\247\377\235\235\234\377\256\257" + "\261\377\0\"g\377\0*l\377\0*l\377\0*l\377\0*l\377\0*l\377\0*l\377\0!g\377" + "\266\267\270\340\303\303\303\223\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\263\267\273R\0#g\377\0)j\377\0)j\377\0)j\377\0)j\377\0)j\377\0)" + "j\377\0#g\377\204\216\237\377\232\232\232\377\246\246\246\377\255\255\255" + "\377\244\244\244\377\227\227\227\377as\215\377\0(j\377\0)j\377\0)j\377\0" + ")j\377\0)j\377\0)j\377\3,m\377\220\230\246\376\226\225\225\377\241\241\241" + "\377\323\323\323^\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\252\254\262M\0" + "\33`\377\0\40b\377\0(g\377\0(g\377\0(g\377\0(g\377\0(g\377\14""3p\377\250" + "\246\244\377\237\237\237\377\252\252\252\377\252\252\252\377\240\240\240" + "\377\244\243\240\377\31;o\377\0'g\377\0(g\377\0(g\377\0(g\377\0(g\377\23" + """8s\377\234\240\246\377\226\226\226\377\240\240\240\377\247\247\247\376" + "\263\263\263\377\337\337\3373///\0\322\322\322W\303\303\303}\303\303\303" + "\237\273\273\274\326\240\244\252\377\257\260\262\377\0\37`\377\0'e\377\0" + "'e\377\0'e\377\0&e\377Wm\220\377\226\226\225\377\242\242\242\377\255\255" + "\255\377\247\247\247\377\233\233\233\377\220\225\234\377\0\37_\377\0'e\377" + "\0'e\377\0'e\377\0%d\377*I|\377\242\242\243\377\231\231\231\377\241\241\241" + "\377\251\251\251\377\255\255\255\377\247\247\247\376\254\254\254\377\366" + "\366\366\27\246\246\246\360\234\234\234\377\250\250\250\377\252\252\252\377" + "\237\237\237\377\257\256\253\377\0\35\\\377\0&b\377\0&b\377\0&b\377\0\35" + "]\377\227\233\243\377\234\234\234\377\247\247\247\377\255\255\255\377\243" + "\243\243\377\226\226\226\377Qe\205\377\0&b\377\0&b\377\0&b\377\0!_\377Ia" + "\210\377\242\241\237\377\233\233\233\377\242\242\242\377\252\252\252\377" + "\255\255\255\377\245\245\245\377\236\236\236\376\226\226\226\377\264\264" + "\264\247\254\254\254\305\234\234\234\377\250\250\250\377\254\254\254\377" + "\240\240\240\377\245\244\242\377\23""5i\377\0%`\377\0%`\377\0%`\377!At\377" + "\241\237\235\377\240\240\240\377\253\253\253\377\251\251\251\377\237\237" + "\237\377\252\251\246\377\6(]\377\0$_\377\0%`\377\0\35[\377k{\227\377\236" + "\235\233\377\235\235\235\377\244\244\244\377\253\253\253\377\254\254\254" + "\377\244\244\244\377\235\235\235\376\232\232\232\377\246\246\246\243\0\0" + "\0\0\257\257\257\253\232\232\232\377\246\246\246\377\254\254\254\377\242" + "\242\242\377\231\230\227\3773Nz\377\0$]\377\0$]\377\0\"\\\377gx\221\377\227" + "\227\227\377\243\243\243\377\255\255\255\377\246\246\246\377\232\232\232" + "\377~\206\223\377\0\37Z\377\0$]\377\0\"\\\377\207\222\242\377\230\227\226" + "\377\236\236\236\377\245\245\245\377\255\255\255\377\252\252\252\377\243" + "\243\243\377\233\233\233\376\237\237\237\377\231\231\231z\0\0\0\0\5\5\5\0" + "\273\273\273\205\230\230\230\377\245\245\245\377\255\255\255\377\243\243" + "\243\377\230\227\226\377H^\201\377\0\"Z\377\0#[\377\0\33U\377\243\245\247" + "\377\235\235\235\377\250\250\250\377\254\254\254\377\242\242\242\377\230" + "\230\227\377BXz\377\0#[\377\0\37Y\377|\211\236\377\223\223\223\377\237\237" + "\237\377\247\247\247\377\255\255\255\377\251\251\251\377\242\242\242\377" + "\231\231\231\376\243\243\243\374\205\205\205P\0\0\0\0\0\0\0\0\0\0\0\0\306" + "\306\306h\225\225\225\377\243\243\243\377\255\255\255\377\245\245\245\377" + "\227\227\227\377^n\210\377\0!X\377\0\"X\377>W~\377\231\230\226\377\241\241" + "\241\377\254\254\254\377\250\250\250\377\236\236\236\377\247\247\247\377" + "\0\33P\377\37\224\224\224\377" + "\242\242\242\377\255\255\255\377\246\246\246\377\232\232\232\377s~\217\377" + "\0\36S\377\0\37T\377>W}\377Qc\200\377\233\237\245\377\272\270\267\377\243" + "\243\243\377\230\230\230\377pz\212\377\0\31P\377\214\224\242\377\230\230" + "\230\377\256\256\255\377.Is\377\265\265\266\377\246\246\246\377\237\237\237" + "\377\227\226\226\377\220\225\235\377\0\36O\377mqwj\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\370\370\370\37\233\233\233\377\241\241\241\377\254\254\254\377" + "\247\247\247\377\234\234\234\377\210\216\230\377\0\32N\377\0\36R\377\253" + "\252\250\377\257\257\257\377o|\222\377L`\200\377_m\205\377\251\251\252\377" + ",Ch\377\0\40S\377\0\37R\377\243\247\255\377\251\252\252\377\262\263\265\377" + "0Iq\377\253\252\250\377\233\232\232\377z\203\221\377\0\30J\377\0\34Q\377" + "hkqj\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\246\246\246\377\237\237\237" + "\377\253\253\253\377\251\251\251\377\236\236\236\377\235\237\241\377\0\27" + "K\3774Ls\377\254\253\251\377\243\242\242\377\253\253\253\377\256\255\253" + "\377\243\246\252\377Qb\177\377\0\32L\377\0\37P\377\0\37P\377\14*Y\377\262" + "\263\264\377\242\242\242\377\234\240\247\377EZz\377[i~\377\0\27I\377\0\36" + "P\377\0\33N\377dglj\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\251\251\365" + "\236\236\236\377\251\251\251\377\253\253\253\377\237\237\237\377\257\256" + "\254\377\0\26G\377\0\33M\377\0\33K\377J\\y\377\220\226\240\377\265\264\262" + "\377\225\225\225\377^k\177\377\0\35N\377\0\36N\377\0\36N\377\0\36N\377#<" + "d\377\247\246\245\377\241\240\240\3770Ei\377\0\34M\377\0\35N\377\0\36N\377" + "\0\32M\377`dhj\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\256\256\256\314\234" + "\234\234\377\250\250\250\377\254\254\254\377\240\240\240\377\242\241\237" + "\377\27""0Y\377\0\35K\377\0\35K\377\0\35K\377\0\27G\377\15'Q\377^l\202\377" + "\31""0W\377\0\34K\377\0\35K\377\0\35K\377\0\35K\377\0\34J\377?Ss\377\37""4" + "V\377\0\34K\377\0\34K\377\0\35K\377\0\35K\377\0\31I\377\\_ej\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\266\266\266\261\247\247\247\377\266\266\266" + "\377\240\243\247\376\203\211\222\377eo\177\377\24,T\377\0\34I\377\0\34I\377" + "\0\34I\377\0\34I\377\0\33H\377\0\33H\377\0\33I\377\0\34I\377\0\34I\377\0" + "\34I\377\0\34I\377\0\34I\377\0\33H\377\0\34I\377\0\34H\377\0\34I\377\0\34" + "I\377\0\34I\377\0\30G\377X\\`j\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\330" + "\330\3304\266\266\266^\231\232\233\234`m\203\377\200\210\226\377\220\225" + "\234\377J[w\377\0\32F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377" + "\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0" + "\33F\377\0\33F\377\0\33F\377\0\33F\377\0\33F\377\0\30E\377TX\\j\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\310\310\310h\223\223\223\377\241\241\241\377" + "\254\254\254\376\243\243\243\377\227\227\226\377`k\177\377\0\31C\377\0\32" + "C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377" + "\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0\32C\377\0" + "\32C\377\0\32C\377\0\27B\377PSXj\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\347\347\347?\246\246\246\377\246\246\246\351\203\211\223\377eo\177\377P" + "]r\377#7V\377\0\30A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377" + "\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0" + "\31A\377\0\31A\377\0\31A\377\0\31A\377\0\31A\377\0\26@\377MOTj\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0SSSK\0\20""9\377\0\27>\376" + "\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0" + "\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27" + ">\377\0\27>\377\0\27>\377\0\27>\377\0\27>\377\0\27>\376\0\23<\377MMOf\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8AP\225!-C" + "\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C" + "\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C\323\40-C" + "\323\40-C\323\40-C\323\40-C\323\40-C\32408E\257\0\0\0\21\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ZZZ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", }; diff --git a/code/server/server.h b/code/server/server.h index be74916..c712e48 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -173,7 +173,7 @@ typedef struct client_s { int timeoutCount; // must timeout a few frames in a row so debugging doesn't break clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here int ping; - int rate; // bytes / second + int sacc_rate; // bytes / second int snapshotMsec; // requests a snapshot every snapshotMsec unless rate choked int pureAuthentic; qboolean gotCP; // TTimo - additional flag to distinguish between a bad pure checksum, and no cp command at all diff --git a/code/server/sv_bot.c b/code/server/sv_bot.c index 3400a09..350e46a 100644 --- a/code/server/sv_bot.c +++ b/code/server/sv_bot.c @@ -64,7 +64,7 @@ int SV_BotAllocateClient(void) { cl->state = CS_ACTIVE; cl->lastPacketTime = svs.time; cl->netchan.remoteAddress.type = NA_BOT; - cl->rate = 16384; + cl->sacc_rate = 16384; return i; } diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index 8ddb94d..d719d53 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -1125,7 +1125,7 @@ static void SV_Status_f( void ) { Com_Printf ("%5i", cl->netchan.qport); - Com_Printf (" %5i", cl->rate); + Com_Printf (" %5i", cl->sacc_rate); Com_Printf ("\n"); } diff --git a/code/server/sv_client.c b/code/server/sv_client.c index 0326782..670cdb8 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -916,6 +916,8 @@ Fill up msg with data, return number of download blocks added int SV_WriteDownloadToClient(client_t *cl, msg_t *msg) { int curindex; + int sacc_rate; + int blockspersnap; int unreferenced = 1; char errorMessage[1024]; char pakbuf[MAX_QPATH], *pakptr; @@ -1071,42 +1073,70 @@ int SV_WriteDownloadToClient(client_t *cl, msg_t *msg) cl->downloadEOF = qtrue; // We have added the EOF block } + sacc_rate = cl->sacc_rate; + if ( sv_maxRate->integer ) { + if ( sv_maxRate->integer < 1000 ) { + Cvar_Set ( "sv_maxRate", "1000" ); + } + if ( sv_maxRate->integer < sacc_rate ) { + sacc_rate = sv_maxRate->integer; + } + } + if ( sv_minRate->integer ) { + if ( sv_minRate->integer < 1000) { + Cvar_Set ( "sv_minRate", "1000" ); + } + if ( sv_minRate->integer > sacc_rate ) { + sacc_rate = sv_minRate->integer; + } + } + + if ( !sacc_rate ) { + blockspersnap = 1; + } else { + blockspersnap = ( (sacc_rate * cl->snapshotMsec) / 1000 * MAX_DOWNLOAD_BLKSIZE ) / MAX_DOWNLOAD_BLKSIZE; + } + + if ( blockspersnap < 0 ) blockspersnap = 1; + if (cl->downloadClientBlock == cl->downloadCurrentBlock) return 0; // Nothing to transmit - // Write out the next section of the file, if we have already reached our window, - // automatically start retransmitting - if (cl->downloadXmitBlock == cl->downloadCurrentBlock) - { - // We have transmitted the complete window, should we start resending? - if (svs.time - cl->downloadSendTime > 1000) - cl->downloadXmitBlock = cl->downloadClientBlock; - else - return 0; - } + while ( blockspersnap-- ) { + // Write out the next section of the file, if we have already reached our window, + // automatically start retransmitting + if (cl->downloadXmitBlock == cl->downloadCurrentBlock) + { + // We have transmitted the complete window, should we start resending? + if (svs.time - cl->downloadSendTime > 1000) + cl->downloadXmitBlock = cl->downloadClientBlock; + else + return 0; + } - // Send current block - curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW); + // Send current block + curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW); - MSG_WriteByte( msg, svc_download ); - MSG_WriteShort( msg, cl->downloadXmitBlock ); + MSG_WriteByte( msg, svc_download ); + MSG_WriteShort( msg, cl->downloadXmitBlock ); - // block zero is special, contains file size - if ( cl->downloadXmitBlock == 0 ) - MSG_WriteLong( msg, cl->downloadSize ); + // block zero is special, contains file size + if ( cl->downloadXmitBlock == 0 ) + MSG_WriteLong( msg, cl->downloadSize ); - MSG_WriteShort( msg, cl->downloadBlockSize[curindex] ); + MSG_WriteShort( msg, cl->downloadBlockSize[curindex] ); - // Write the block - if(cl->downloadBlockSize[curindex]) - MSG_WriteData(msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex]); + // Write the block + if(cl->downloadBlockSize[curindex]) + MSG_WriteData(msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex]); - Com_DPrintf( "clientDownload: %d : writing block %d\n", (int) (cl - svs.clients), cl->downloadXmitBlock ); + Com_DPrintf( "clientDownload: %d : writing block %d\n", (int) (cl - svs.clients), cl->downloadXmitBlock ); - // Move on to the next block - // It will get sent with next snap shot. The rate will keep us in line. - cl->downloadXmitBlock++; - cl->downloadSendTime = svs.time; + // Move on to the next block + // It will get sent with next snap shot. The rate will keep us in line. + cl->downloadXmitBlock++; + cl->downloadSendTime = svs.time; + } return 1; } @@ -1388,19 +1418,19 @@ void SV_UserinfoChanged( client_t *cl ) { // if the client is on the same subnet as the server and we aren't running an // internet public server, assume they don't need a rate choke if ( Sys_IsLANAddress( cl->netchan.remoteAddress ) && com_dedicated->integer != 2 && sv_lanForceRate->integer == 1) { - cl->rate = 99999; // lans should not rate limit + cl->sacc_rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey (cl->userinfo, "rate"); if (strlen(val)) { i = atoi(val); - cl->rate = i; - if (cl->rate < 1000) { - cl->rate = 1000; - } else if (cl->rate > 90000) { - cl->rate = 90000; + cl->sacc_rate = i; + if (cl->sacc_rate < 1000) { + cl->sacc_rate = 1000; + } else if (cl->sacc_rate > 90000) { + cl->sacc_rate = 90000; } } else { - cl->rate = 3000; + cl->sacc_rate = 3000; } } val = Info_ValueForKey (cl->userinfo, "handicap"); diff --git a/code/server/sv_main.c b/code/server/sv_main.c index d7b88ed..21bdc49 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -1170,26 +1170,27 @@ a client based on its rate settings int SV_RateMsec(client_t *client) { - int rate, rateMsec; + int sacc_rate, rateMsec; int messageSize; messageSize = client->netchan.lastSentSize; - rate = client->rate; + + sacc_rate = client->sacc_rate; if(sv_maxRate->integer) { - if(sv_maxRate->integer < 1000) - Cvar_Set( "sv_MaxRate", "1000" ); - if(sv_maxRate->integer < rate) - rate = sv_maxRate->integer; + if(sv_maxRate->integer < 10000) + Cvar_Set( "sv_MaxRate", "10000" ); + if(sv_maxRate->integer < sacc_rate) + sacc_rate = sv_maxRate->integer; } if(sv_minRate->integer) { - if(sv_minRate->integer < 1000) - Cvar_Set("sv_minRate", "1000"); - if(sv_minRate->integer > rate) - rate = sv_minRate->integer; + if(sv_minRate->integer < 10000) + Cvar_Set("sv_minRate", "10000"); + if(sv_minRate->integer > sacc_rate) + sacc_rate = sv_minRate->integer; } if(client->netchan.remoteAddress.type == NA_IP6) @@ -1197,13 +1198,13 @@ int SV_RateMsec(client_t *client) else messageSize += UDPIP_HEADER_SIZE; - rateMsec = messageSize * 1000 / ((int) (rate * com_timescale->value)); - rate = Sys_Milliseconds() - client->netchan.lastSentTime; + rateMsec = messageSize * 1000 / ((int) (sacc_rate * com_timescale->value)); + sacc_rate = Sys_Milliseconds() - client->netchan.lastSentTime; - if(rate > rateMsec) + if(sacc_rate > rateMsec) return 0; else - return rateMsec - rate; + return rateMsec - sacc_rate; } /* diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index ed00b65..7997d1f 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -629,6 +629,16 @@ int main( int argc, char **argv ) CON_Init( ); + Com_Printf("\n" + " ^7S^5ACC ^7Wi^5th ^7S^5ound ^7H^5ax by strata\n" + " ^7 ___ _ __ ___ _ __\n" + " ^7,' _////7/ /(),' _/ /// /\n" + " ^7_\\ `.| V V //7_\\ `. / ` /\n" + " ^7/___,'|_n_,'///___,'/_n_/\n" + " ^5version 1^7.^51\n"); + + Com_Printf("\n http://swish.dropswitch.net\n"); + signal( SIGILL, Sys_SigHandler ); signal( SIGFPE, Sys_SigHandler ); signal( SIGSEGV, Sys_SigHandler ); diff --git a/code/sys/win_resource.rc b/code/sys/win_resource.rc index b1c39d5..da46221 100644 --- a/code/sys/win_resource.rc +++ b/code/sys/win_resource.rc @@ -54,9 +54,9 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. #ifndef __MINGW32__ -IDI_ICON1 ICON DISCARDABLE "../quake3.ico" +IDI_ICON1 ICON DISCARDABLE "../q3ut4.ico" #else -IDI_ICON1 ICON DISCARDABLE "misc/quake3.ico" +IDI_ICON1 ICON DISCARDABLE "misc/q3ut4.ico" #endif @@ -67,7 +67,7 @@ IDI_ICON1 ICON DISCARDABLE "misc/quake3.ico" STRINGTABLE DISCARDABLE BEGIN - IDS_STRING1 "Quake3" + IDS_STRING1 "SWiSH_ioUrT" END #endif // English (U.S.) resources diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index e887c8c..44c6c91 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3075,13 +3075,13 @@ static void UI_Update(const char *name) { if (Q_stricmp(name, "ui_SetName") == 0) { trap_Cvar_Set( "name", UI_Cvar_VariableString("ui_Name")); } else if (Q_stricmp(name, "ui_setRate") == 0) { - float rate = trap_Cvar_VariableValue("rate"); - if (rate >= 5000) { - trap_Cvar_Set("cl_maxpackets", "30"); - trap_Cvar_Set("cl_packetdup", "1"); - } else if (rate >= 4000) { - trap_Cvar_Set("cl_maxpackets", "15"); - trap_Cvar_Set("cl_packetdup", "2"); // favor less prediction errors when there's packet loss + float sacc_rate = trap_Cvar_VariableValue("sacc_rate"); + if (sacc_rate >= 25000) { + trap_Cvar_Set("sacc_cl_maxpackets", "125"); + trap_Cvar_Set("sacc_cl_packetdup", "0"); + } else if (sacc_rate >= 4000) { + trap_Cvar_Set("sacc_cl_maxpackets", "30"); + trap_Cvar_Set("sacc_cl_packetdup", "2"); // favor less prediction errors when there's packet loss } else { trap_Cvar_Set("cl_maxpackets", "15"); trap_Cvar_Set("cl_packetdup", "1"); // favor lower bandwidth diff --git a/misc/nsis/ioquake3-q3a.nsi b/misc/nsis/ioquake3-q3a.nsi index 9975609..8de1dc3 100644 --- a/misc/nsis/ioquake3-q3a.nsi +++ b/misc/nsis/ioquake3-q3a.nsi @@ -22,7 +22,7 @@ Var q3a_pak0 Var q3ta_pak0 !include "MUI2.nsh" -!define MUI_ICON "../quake3.ico" +!define MUI_ICON "../q3ut4.ico" ; The name of the installer Name "${NAME}-${VERSION} for ioquake3" diff --git a/misc/nsis/ioquake3-q3ctc.nsi b/misc/nsis/ioquake3-q3ctc.nsi index e4f27e3..95f2315 100644 --- a/misc/nsis/ioquake3-q3ctc.nsi +++ b/misc/nsis/ioquake3-q3ctc.nsi @@ -10,7 +10,7 @@ ; uncomment if the mod works without baseq3 ;!define STANDALONE -!define MUI_ICON "../quake3.ico" +!define MUI_ICON "../q3ut4.ico" !macro FILES File ctc0.pk3 diff --git a/misc/nsis/ioquake3.nsi.in b/misc/nsis/ioquake3.nsi.in index 201ea32..39ddbae 100644 --- a/misc/nsis/ioquake3.nsi.in +++ b/misc/nsis/ioquake3.nsi.in @@ -18,7 +18,7 @@ !include MultiUser.nsh !include "MUI2.nsh" -!define MUI_ICON "../quake3.ico" +!define MUI_ICON "../q3ut4.ico" ; The name of the installer Name "ioquake3"