The Quake III Arena sources as originally released under the GPL license on August 20, 2005.
This commit is contained in:
commit
dbe4ddb103
1409 changed files with 806066 additions and 0 deletions
1029
code/client/cl_cgame.c
Normal file
1029
code/client/cl_cgame.c
Normal file
File diff suppressed because it is too large
Load diff
1740
code/client/cl_cin.c
Normal file
1740
code/client/cl_cin.c
Normal file
File diff suppressed because it is too large
Load diff
786
code/client/cl_console.c
Normal file
786
code/client/cl_console.c
Normal file
|
@ -0,0 +1,786 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// console.c
|
||||
|
||||
#include "client.h"
|
||||
|
||||
|
||||
int g_console_field_width = 78;
|
||||
|
||||
|
||||
#define NUM_CON_TIMES 4
|
||||
|
||||
#define CON_TEXTSIZE 32768
|
||||
typedef struct {
|
||||
qboolean initialized;
|
||||
|
||||
short text[CON_TEXTSIZE];
|
||||
int current; // line where next message will be printed
|
||||
int x; // offset in current line for next print
|
||||
int display; // bottom of console displays this line
|
||||
|
||||
int linewidth; // characters across screen
|
||||
int totallines; // total lines in console scrollback
|
||||
|
||||
float xadjust; // for wide aspect screens
|
||||
|
||||
float displayFrac; // aproaches finalFrac at scr_conspeed
|
||||
float finalFrac; // 0.0 to 1.0 lines of console to display
|
||||
|
||||
int vislines; // in scanlines
|
||||
|
||||
int times[NUM_CON_TIMES]; // cls.realtime time the line was generated
|
||||
// for transparent notify lines
|
||||
vec4_t color;
|
||||
} console_t;
|
||||
|
||||
extern console_t con;
|
||||
|
||||
console_t con;
|
||||
|
||||
cvar_t *con_conspeed;
|
||||
cvar_t *con_notifytime;
|
||||
|
||||
#define DEFAULT_CONSOLE_WIDTH 78
|
||||
|
||||
vec4_t console_color = {1.0, 1.0, 1.0, 1.0};
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_ToggleConsole_f
|
||||
================
|
||||
*/
|
||||
void Con_ToggleConsole_f (void) {
|
||||
// closing a full screen console restarts the demo loop
|
||||
if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) {
|
||||
CL_StartDemoLoop();
|
||||
return;
|
||||
}
|
||||
|
||||
Field_Clear( &g_consoleField );
|
||||
g_consoleField.widthInChars = g_console_field_width;
|
||||
|
||||
Con_ClearNotify ();
|
||||
cls.keyCatchers ^= KEYCATCH_CONSOLE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode_f (void) {
|
||||
chat_playerNum = -1;
|
||||
chat_team = qfalse;
|
||||
Field_Clear( &chatField );
|
||||
chatField.widthInChars = 30;
|
||||
|
||||
cls.keyCatchers ^= KEYCATCH_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode2_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode2_f (void) {
|
||||
chat_playerNum = -1;
|
||||
chat_team = qtrue;
|
||||
Field_Clear( &chatField );
|
||||
chatField.widthInChars = 25;
|
||||
cls.keyCatchers ^= KEYCATCH_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode3_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode3_f (void) {
|
||||
chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
|
||||
if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
|
||||
chat_playerNum = -1;
|
||||
return;
|
||||
}
|
||||
chat_team = qfalse;
|
||||
Field_Clear( &chatField );
|
||||
chatField.widthInChars = 30;
|
||||
cls.keyCatchers ^= KEYCATCH_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode4_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode4_f (void) {
|
||||
chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER );
|
||||
if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
|
||||
chat_playerNum = -1;
|
||||
return;
|
||||
}
|
||||
chat_team = qfalse;
|
||||
Field_Clear( &chatField );
|
||||
chatField.widthInChars = 30;
|
||||
cls.keyCatchers ^= KEYCATCH_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Clear_f
|
||||
================
|
||||
*/
|
||||
void Con_Clear_f (void) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
|
||||
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
|
||||
}
|
||||
|
||||
Con_Bottom(); // go to end
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Dump_f
|
||||
|
||||
Save the console contents out to a file
|
||||
================
|
||||
*/
|
||||
void Con_Dump_f (void)
|
||||
{
|
||||
int l, x, i;
|
||||
short *line;
|
||||
fileHandle_t f;
|
||||
char buffer[1024];
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
Com_Printf ("usage: condump <filename>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) );
|
||||
|
||||
f = FS_FOpenFileWrite( Cmd_Argv( 1 ) );
|
||||
if (!f)
|
||||
{
|
||||
Com_Printf ("ERROR: couldn't open.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// skip empty lines
|
||||
for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
|
||||
{
|
||||
line = con.text + (l%con.totallines)*con.linewidth;
|
||||
for (x=0 ; x<con.linewidth ; x++)
|
||||
if ((line[x] & 0xff) != ' ')
|
||||
break;
|
||||
if (x != con.linewidth)
|
||||
break;
|
||||
}
|
||||
|
||||
// write the remaining lines
|
||||
buffer[con.linewidth] = 0;
|
||||
for ( ; l <= con.current ; l++)
|
||||
{
|
||||
line = con.text + (l%con.totallines)*con.linewidth;
|
||||
for(i=0; i<con.linewidth; i++)
|
||||
buffer[i] = line[i] & 0xff;
|
||||
for (x=con.linewidth-1 ; x>=0 ; x--)
|
||||
{
|
||||
if (buffer[x] == ' ')
|
||||
buffer[x] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
strcat( buffer, "\n" );
|
||||
FS_Write(buffer, strlen(buffer), f);
|
||||
}
|
||||
|
||||
FS_FCloseFile( f );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_ClearNotify
|
||||
================
|
||||
*/
|
||||
void Con_ClearNotify( void ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
|
||||
con.times[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_CheckResize
|
||||
|
||||
If the line width has changed, reformat the buffer.
|
||||
================
|
||||
*/
|
||||
void Con_CheckResize (void)
|
||||
{
|
||||
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
|
||||
MAC_STATIC short tbuf[CON_TEXTSIZE];
|
||||
|
||||
width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2;
|
||||
|
||||
if (width == con.linewidth)
|
||||
return;
|
||||
|
||||
if (width < 1) // video hasn't been initialized yet
|
||||
{
|
||||
width = DEFAULT_CONSOLE_WIDTH;
|
||||
con.linewidth = width;
|
||||
con.totallines = CON_TEXTSIZE / con.linewidth;
|
||||
for(i=0; i<CON_TEXTSIZE; i++)
|
||||
|
||||
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
oldwidth = con.linewidth;
|
||||
con.linewidth = width;
|
||||
oldtotallines = con.totallines;
|
||||
con.totallines = CON_TEXTSIZE / con.linewidth;
|
||||
numlines = oldtotallines;
|
||||
|
||||
if (con.totallines < numlines)
|
||||
numlines = con.totallines;
|
||||
|
||||
numchars = oldwidth;
|
||||
|
||||
if (con.linewidth < numchars)
|
||||
numchars = con.linewidth;
|
||||
|
||||
Com_Memcpy (tbuf, con.text, CON_TEXTSIZE * sizeof(short));
|
||||
for(i=0; i<CON_TEXTSIZE; i++)
|
||||
|
||||
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
|
||||
|
||||
|
||||
for (i=0 ; i<numlines ; i++)
|
||||
{
|
||||
for (j=0 ; j<numchars ; j++)
|
||||
{
|
||||
con.text[(con.totallines - 1 - i) * con.linewidth + j] =
|
||||
tbuf[((con.current - i + oldtotallines) %
|
||||
oldtotallines) * oldwidth + j];
|
||||
}
|
||||
}
|
||||
|
||||
Con_ClearNotify ();
|
||||
}
|
||||
|
||||
con.current = con.totallines - 1;
|
||||
con.display = con.current;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Init
|
||||
================
|
||||
*/
|
||||
void Con_Init (void) {
|
||||
int i;
|
||||
|
||||
con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
|
||||
con_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
|
||||
|
||||
Field_Clear( &g_consoleField );
|
||||
g_consoleField.widthInChars = g_console_field_width;
|
||||
for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
|
||||
Field_Clear( &historyEditLines[i] );
|
||||
historyEditLines[i].widthInChars = g_console_field_width;
|
||||
}
|
||||
|
||||
Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
|
||||
Cmd_AddCommand ("messagemode", Con_MessageMode_f);
|
||||
Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
|
||||
Cmd_AddCommand ("messagemode3", Con_MessageMode3_f);
|
||||
Cmd_AddCommand ("messagemode4", Con_MessageMode4_f);
|
||||
Cmd_AddCommand ("clear", Con_Clear_f);
|
||||
Cmd_AddCommand ("condump", Con_Dump_f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
Con_Linefeed
|
||||
===============
|
||||
*/
|
||||
void Con_Linefeed (qboolean skipnotify)
|
||||
{
|
||||
int i;
|
||||
|
||||
// mark time for transparent overlay
|
||||
if (con.current >= 0)
|
||||
{
|
||||
if (skipnotify)
|
||||
con.times[con.current % NUM_CON_TIMES] = 0;
|
||||
else
|
||||
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
|
||||
}
|
||||
|
||||
con.x = 0;
|
||||
if (con.display == con.current)
|
||||
con.display++;
|
||||
con.current++;
|
||||
for(i=0; i<con.linewidth; i++)
|
||||
con.text[(con.current%con.totallines)*con.linewidth+i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_ConsolePrint
|
||||
|
||||
Handles cursor positioning, line wrapping, etc
|
||||
All console printing must go through this in order to be logged to disk
|
||||
If no console is visible, the text will appear at the top of the game window
|
||||
================
|
||||
*/
|
||||
void CL_ConsolePrint( char *txt ) {
|
||||
int y;
|
||||
int c, l;
|
||||
int color;
|
||||
qboolean skipnotify = qfalse; // NERVE - SMF
|
||||
int prev; // NERVE - SMF
|
||||
|
||||
// TTimo - prefix for text that shows up in console but not in notify
|
||||
// backported from RTCW
|
||||
if ( !Q_strncmp( txt, "[skipnotify]", 12 ) ) {
|
||||
skipnotify = qtrue;
|
||||
txt += 12;
|
||||
}
|
||||
|
||||
// for some demos we don't want to ever show anything on the console
|
||||
if ( cl_noprint && cl_noprint->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!con.initialized) {
|
||||
con.color[0] =
|
||||
con.color[1] =
|
||||
con.color[2] =
|
||||
con.color[3] = 1.0f;
|
||||
con.linewidth = -1;
|
||||
Con_CheckResize ();
|
||||
con.initialized = qtrue;
|
||||
}
|
||||
|
||||
color = ColorIndex(COLOR_WHITE);
|
||||
|
||||
while ( (c = *txt) != 0 ) {
|
||||
if ( Q_IsColorString( txt ) ) {
|
||||
color = ColorIndex( *(txt+1) );
|
||||
txt += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// count word length
|
||||
for (l=0 ; l< con.linewidth ; l++) {
|
||||
if ( txt[l] <= ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// word wrap
|
||||
if (l != con.linewidth && (con.x + l >= con.linewidth) ) {
|
||||
Con_Linefeed(skipnotify);
|
||||
|
||||
}
|
||||
|
||||
txt++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
Con_Linefeed (skipnotify);
|
||||
break;
|
||||
case '\r':
|
||||
con.x = 0;
|
||||
break;
|
||||
default: // display character and advance
|
||||
y = con.current % con.totallines;
|
||||
con.text[y*con.linewidth+con.x] = (color << 8) | c;
|
||||
con.x++;
|
||||
if (con.x >= con.linewidth) {
|
||||
Con_Linefeed(skipnotify);
|
||||
con.x = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mark time for transparent overlay
|
||||
if (con.current >= 0) {
|
||||
// NERVE - SMF
|
||||
if ( skipnotify ) {
|
||||
prev = con.current % NUM_CON_TIMES - 1;
|
||||
if ( prev < 0 )
|
||||
prev = NUM_CON_TIMES - 1;
|
||||
con.times[prev] = 0;
|
||||
}
|
||||
else
|
||||
// -NERVE - SMF
|
||||
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
DRAWING
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawInput
|
||||
|
||||
Draw the editline after a ] prompt
|
||||
================
|
||||
*/
|
||||
void Con_DrawInput (void) {
|
||||
int y;
|
||||
|
||||
if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
y = con.vislines - ( SMALLCHAR_HEIGHT * 2 );
|
||||
|
||||
re.SetColor( con.color );
|
||||
|
||||
SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' );
|
||||
|
||||
Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y,
|
||||
SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawNotify
|
||||
|
||||
Draws the last few lines of output transparently over the game top
|
||||
================
|
||||
*/
|
||||
void Con_DrawNotify (void)
|
||||
{
|
||||
int x, v;
|
||||
short *text;
|
||||
int i;
|
||||
int time;
|
||||
int skip;
|
||||
int currentColor;
|
||||
|
||||
currentColor = 7;
|
||||
re.SetColor( g_color_table[currentColor] );
|
||||
|
||||
v = 0;
|
||||
for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
|
||||
{
|
||||
if (i < 0)
|
||||
continue;
|
||||
time = con.times[i % NUM_CON_TIMES];
|
||||
if (time == 0)
|
||||
continue;
|
||||
time = cls.realtime - time;
|
||||
if (time > con_notifytime->value*1000)
|
||||
continue;
|
||||
text = con.text + (i % con.totallines)*con.linewidth;
|
||||
|
||||
if (cl.snap.ps.pm_type != PM_INTERMISSION && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (x = 0 ; x < con.linewidth ; x++) {
|
||||
if ( ( text[x] & 0xff ) == ' ' ) {
|
||||
continue;
|
||||
}
|
||||
if ( ( (text[x]>>8)&7 ) != currentColor ) {
|
||||
currentColor = (text[x]>>8)&7;
|
||||
re.SetColor( g_color_table[currentColor] );
|
||||
}
|
||||
SCR_DrawSmallChar( cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH, v, text[x] & 0xff );
|
||||
}
|
||||
|
||||
v += SMALLCHAR_HEIGHT;
|
||||
}
|
||||
|
||||
re.SetColor( NULL );
|
||||
|
||||
if (cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the chat line
|
||||
if ( cls.keyCatchers & KEYCATCH_MESSAGE )
|
||||
{
|
||||
if (chat_team)
|
||||
{
|
||||
SCR_DrawBigString (8, v, "say_team:", 1.0f );
|
||||
skip = 11;
|
||||
}
|
||||
else
|
||||
{
|
||||
SCR_DrawBigString (8, v, "say:", 1.0f );
|
||||
skip = 5;
|
||||
}
|
||||
|
||||
Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v,
|
||||
SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue );
|
||||
|
||||
v += BIGCHAR_HEIGHT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawSolidConsole
|
||||
|
||||
Draws the console with the solid background
|
||||
================
|
||||
*/
|
||||
void Con_DrawSolidConsole( float frac ) {
|
||||
int i, x, y;
|
||||
int rows;
|
||||
short *text;
|
||||
int row;
|
||||
int lines;
|
||||
// qhandle_t conShader;
|
||||
int currentColor;
|
||||
vec4_t color;
|
||||
|
||||
lines = cls.glconfig.vidHeight * frac;
|
||||
if (lines <= 0)
|
||||
return;
|
||||
|
||||
if (lines > cls.glconfig.vidHeight )
|
||||
lines = cls.glconfig.vidHeight;
|
||||
|
||||
// on wide screens, we will center the text
|
||||
con.xadjust = 0;
|
||||
SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL );
|
||||
|
||||
// draw the background
|
||||
y = frac * SCREEN_HEIGHT - 2;
|
||||
if ( y < 1 ) {
|
||||
y = 0;
|
||||
}
|
||||
else {
|
||||
SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader );
|
||||
}
|
||||
|
||||
color[0] = 1;
|
||||
color[1] = 0;
|
||||
color[2] = 0;
|
||||
color[3] = 1;
|
||||
SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color );
|
||||
|
||||
|
||||
// draw the version number
|
||||
|
||||
re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
|
||||
|
||||
i = strlen( Q3_VERSION );
|
||||
|
||||
for (x=0 ; x<i ; x++) {
|
||||
|
||||
SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH,
|
||||
|
||||
(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), Q3_VERSION[x] );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// draw the text
|
||||
con.vislines = lines;
|
||||
rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH; // rows of text to draw
|
||||
|
||||
y = lines - (SMALLCHAR_HEIGHT*3);
|
||||
|
||||
// draw from the bottom up
|
||||
if (con.display != con.current)
|
||||
{
|
||||
// draw arrows to show the buffer is backscrolled
|
||||
re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
|
||||
for (x=0 ; x<con.linewidth ; x+=4)
|
||||
SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, '^' );
|
||||
y -= SMALLCHAR_HEIGHT;
|
||||
rows--;
|
||||
}
|
||||
|
||||
row = con.display;
|
||||
|
||||
if ( con.x == 0 ) {
|
||||
row--;
|
||||
}
|
||||
|
||||
currentColor = 7;
|
||||
re.SetColor( g_color_table[currentColor] );
|
||||
|
||||
for (i=0 ; i<rows ; i++, y -= SMALLCHAR_HEIGHT, row--)
|
||||
{
|
||||
if (row < 0)
|
||||
break;
|
||||
if (con.current - row >= con.totallines) {
|
||||
// past scrollback wrap point
|
||||
continue;
|
||||
}
|
||||
|
||||
text = con.text + (row % con.totallines)*con.linewidth;
|
||||
|
||||
for (x=0 ; x<con.linewidth ; x++) {
|
||||
if ( ( text[x] & 0xff ) == ' ' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( (text[x]>>8)&7 ) != currentColor ) {
|
||||
currentColor = (text[x]>>8)&7;
|
||||
re.SetColor( g_color_table[currentColor] );
|
||||
}
|
||||
SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff );
|
||||
}
|
||||
}
|
||||
|
||||
// draw the input prompt, user text, and cursor if desired
|
||||
Con_DrawInput ();
|
||||
|
||||
re.SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
Con_DrawConsole
|
||||
==================
|
||||
*/
|
||||
void Con_DrawConsole( void ) {
|
||||
// check for console width changes from a vid mode change
|
||||
Con_CheckResize ();
|
||||
|
||||
// if disconnected, render console full screen
|
||||
if ( cls.state == CA_DISCONNECTED ) {
|
||||
if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) {
|
||||
Con_DrawSolidConsole( 1.0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( con.displayFrac ) {
|
||||
Con_DrawSolidConsole( con.displayFrac );
|
||||
} else {
|
||||
// draw notify lines
|
||||
if ( cls.state == CA_ACTIVE ) {
|
||||
Con_DrawNotify ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
Con_RunConsole
|
||||
|
||||
Scroll it up or down
|
||||
==================
|
||||
*/
|
||||
void Con_RunConsole (void) {
|
||||
// decide on the destination height of the console
|
||||
if ( cls.keyCatchers & KEYCATCH_CONSOLE )
|
||||
con.finalFrac = 0.5; // half screen
|
||||
else
|
||||
con.finalFrac = 0; // none visible
|
||||
|
||||
// scroll towards the destination height
|
||||
if (con.finalFrac < con.displayFrac)
|
||||
{
|
||||
con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001;
|
||||
if (con.finalFrac > con.displayFrac)
|
||||
con.displayFrac = con.finalFrac;
|
||||
|
||||
}
|
||||
else if (con.finalFrac > con.displayFrac)
|
||||
{
|
||||
con.displayFrac += con_conspeed->value*cls.realFrametime*0.001;
|
||||
if (con.finalFrac < con.displayFrac)
|
||||
con.displayFrac = con.finalFrac;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Con_PageUp( void ) {
|
||||
con.display -= 2;
|
||||
if ( con.current - con.display >= con.totallines ) {
|
||||
con.display = con.current - con.totallines + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Con_PageDown( void ) {
|
||||
con.display += 2;
|
||||
if (con.display > con.current) {
|
||||
con.display = con.current;
|
||||
}
|
||||
}
|
||||
|
||||
void Con_Top( void ) {
|
||||
con.display = con.totallines;
|
||||
if ( con.current - con.display >= con.totallines ) {
|
||||
con.display = con.current - con.totallines + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Con_Bottom( void ) {
|
||||
con.display = con.current;
|
||||
}
|
||||
|
||||
|
||||
void Con_Close( void ) {
|
||||
if ( !com_cl_running->integer ) {
|
||||
return;
|
||||
}
|
||||
Field_Clear( &g_consoleField );
|
||||
Con_ClearNotify ();
|
||||
cls.keyCatchers &= ~KEYCATCH_CONSOLE;
|
||||
con.finalFrac = 0; // none visible
|
||||
con.displayFrac = 0;
|
||||
}
|
901
code/client/cl_input.c
Normal file
901
code/client/cl_input.c
Normal file
|
@ -0,0 +1,901 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// cl.input.c -- builds an intended movement command to send to the server
|
||||
|
||||
#include "client.h"
|
||||
|
||||
unsigned frame_msec;
|
||||
int old_com_frameTime;
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
KEY BUTTONS
|
||||
|
||||
Continuous button event tracking is complicated by the fact that two different
|
||||
input sources (say, mouse button 1 and the control key) can both press the
|
||||
same button, but the button should only be released when both of the
|
||||
pressing key have been released.
|
||||
|
||||
When a key event issues a button command (+forward, +attack, etc), it appends
|
||||
its key number as argv(1) so it can be matched up with the release.
|
||||
|
||||
argv(2) will be set to the time the event happened, which allows exact
|
||||
control even at low framerates when the down and up events may both get qued
|
||||
at the same time.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
|
||||
kbutton_t in_left, in_right, in_forward, in_back;
|
||||
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
|
||||
kbutton_t in_strafe, in_speed;
|
||||
kbutton_t in_up, in_down;
|
||||
|
||||
kbutton_t in_buttons[16];
|
||||
|
||||
|
||||
qboolean in_mlooking;
|
||||
|
||||
|
||||
void IN_MLookDown( void ) {
|
||||
in_mlooking = qtrue;
|
||||
}
|
||||
|
||||
void IN_MLookUp( void ) {
|
||||
in_mlooking = qfalse;
|
||||
if ( !cl_freelook->integer ) {
|
||||
IN_CenterView ();
|
||||
}
|
||||
}
|
||||
|
||||
void IN_KeyDown( kbutton_t *b ) {
|
||||
int k;
|
||||
char *c;
|
||||
|
||||
c = Cmd_Argv(1);
|
||||
if ( c[0] ) {
|
||||
k = atoi(c);
|
||||
} else {
|
||||
k = -1; // typed manually at the console for continuous down
|
||||
}
|
||||
|
||||
if ( k == b->down[0] || k == b->down[1] ) {
|
||||
return; // repeating key
|
||||
}
|
||||
|
||||
if ( !b->down[0] ) {
|
||||
b->down[0] = k;
|
||||
} else if ( !b->down[1] ) {
|
||||
b->down[1] = k;
|
||||
} else {
|
||||
Com_Printf ("Three keys down for a button!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( b->active ) {
|
||||
return; // still down
|
||||
}
|
||||
|
||||
// save timestamp for partial frame summing
|
||||
c = Cmd_Argv(2);
|
||||
b->downtime = atoi(c);
|
||||
|
||||
b->active = qtrue;
|
||||
b->wasPressed = qtrue;
|
||||
}
|
||||
|
||||
void IN_KeyUp( kbutton_t *b ) {
|
||||
int k;
|
||||
char *c;
|
||||
unsigned uptime;
|
||||
|
||||
c = Cmd_Argv(1);
|
||||
if ( c[0] ) {
|
||||
k = atoi(c);
|
||||
} else {
|
||||
// typed manually at the console, assume for unsticking, so clear all
|
||||
b->down[0] = b->down[1] = 0;
|
||||
b->active = qfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( b->down[0] == k ) {
|
||||
b->down[0] = 0;
|
||||
} else if ( b->down[1] == k ) {
|
||||
b->down[1] = 0;
|
||||
} else {
|
||||
return; // key up without coresponding down (menu pass through)
|
||||
}
|
||||
if ( b->down[0] || b->down[1] ) {
|
||||
return; // some other key is still holding it down
|
||||
}
|
||||
|
||||
b->active = qfalse;
|
||||
|
||||
// save timestamp for partial frame summing
|
||||
c = Cmd_Argv(2);
|
||||
uptime = atoi(c);
|
||||
if ( uptime ) {
|
||||
b->msec += uptime - b->downtime;
|
||||
} else {
|
||||
b->msec += frame_msec / 2;
|
||||
}
|
||||
|
||||
b->active = qfalse;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_KeyState
|
||||
|
||||
Returns the fraction of the frame that the key was down
|
||||
===============
|
||||
*/
|
||||
float CL_KeyState( kbutton_t *key ) {
|
||||
float val;
|
||||
int msec;
|
||||
|
||||
msec = key->msec;
|
||||
key->msec = 0;
|
||||
|
||||
if ( key->active ) {
|
||||
// still down
|
||||
if ( !key->downtime ) {
|
||||
msec = com_frameTime;
|
||||
} else {
|
||||
msec += com_frameTime - key->downtime;
|
||||
}
|
||||
key->downtime = com_frameTime;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (msec) {
|
||||
Com_Printf ("%i ", msec);
|
||||
}
|
||||
#endif
|
||||
|
||||
val = (float)msec / frame_msec;
|
||||
if ( val < 0 ) {
|
||||
val = 0;
|
||||
}
|
||||
if ( val > 1 ) {
|
||||
val = 1;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void IN_UpDown(void) {IN_KeyDown(&in_up);}
|
||||
void IN_UpUp(void) {IN_KeyUp(&in_up);}
|
||||
void IN_DownDown(void) {IN_KeyDown(&in_down);}
|
||||
void IN_DownUp(void) {IN_KeyUp(&in_down);}
|
||||
void IN_LeftDown(void) {IN_KeyDown(&in_left);}
|
||||
void IN_LeftUp(void) {IN_KeyUp(&in_left);}
|
||||
void IN_RightDown(void) {IN_KeyDown(&in_right);}
|
||||
void IN_RightUp(void) {IN_KeyUp(&in_right);}
|
||||
void IN_ForwardDown(void) {IN_KeyDown(&in_forward);}
|
||||
void IN_ForwardUp(void) {IN_KeyUp(&in_forward);}
|
||||
void IN_BackDown(void) {IN_KeyDown(&in_back);}
|
||||
void IN_BackUp(void) {IN_KeyUp(&in_back);}
|
||||
void IN_LookupDown(void) {IN_KeyDown(&in_lookup);}
|
||||
void IN_LookupUp(void) {IN_KeyUp(&in_lookup);}
|
||||
void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);}
|
||||
void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);}
|
||||
void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);}
|
||||
void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);}
|
||||
void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);}
|
||||
void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);}
|
||||
|
||||
void IN_SpeedDown(void) {IN_KeyDown(&in_speed);}
|
||||
void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
|
||||
void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
|
||||
void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
|
||||
|
||||
void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);}
|
||||
void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);}
|
||||
void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);}
|
||||
void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);}
|
||||
void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);}
|
||||
void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);}
|
||||
void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);}
|
||||
void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);}
|
||||
void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);}
|
||||
void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);}
|
||||
void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);}
|
||||
void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);}
|
||||
void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);}
|
||||
void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);}
|
||||
void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);}
|
||||
void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);}
|
||||
void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);}
|
||||
void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);}
|
||||
void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);}
|
||||
void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);}
|
||||
void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);}
|
||||
void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);}
|
||||
void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);}
|
||||
void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);}
|
||||
void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);}
|
||||
void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);}
|
||||
void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);}
|
||||
void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);}
|
||||
void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);}
|
||||
void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);}
|
||||
void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);}
|
||||
void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);}
|
||||
|
||||
void IN_ButtonDown (void) {
|
||||
IN_KeyDown(&in_buttons[1]);}
|
||||
void IN_ButtonUp (void) {
|
||||
IN_KeyUp(&in_buttons[1]);}
|
||||
|
||||
void IN_CenterView (void) {
|
||||
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
|
||||
cvar_t *cl_upspeed;
|
||||
cvar_t *cl_forwardspeed;
|
||||
cvar_t *cl_sidespeed;
|
||||
|
||||
cvar_t *cl_yawspeed;
|
||||
cvar_t *cl_pitchspeed;
|
||||
|
||||
cvar_t *cl_run;
|
||||
|
||||
cvar_t *cl_anglespeedkey;
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CL_AdjustAngles
|
||||
|
||||
Moves the local angle positions
|
||||
================
|
||||
*/
|
||||
void CL_AdjustAngles( void ) {
|
||||
float speed;
|
||||
|
||||
if ( in_speed.active ) {
|
||||
speed = 0.001 * cls.frametime * cl_anglespeedkey->value;
|
||||
} else {
|
||||
speed = 0.001 * cls.frametime;
|
||||
}
|
||||
|
||||
if ( !in_strafe.active ) {
|
||||
cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
|
||||
cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
|
||||
}
|
||||
|
||||
cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup);
|
||||
cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_KeyMove
|
||||
|
||||
Sets the usercmd_t based on key states
|
||||
================
|
||||
*/
|
||||
void CL_KeyMove( usercmd_t *cmd ) {
|
||||
int movespeed;
|
||||
int forward, side, up;
|
||||
|
||||
//
|
||||
// adjust for speed key / running
|
||||
// the walking flag is to keep animations consistant
|
||||
// even during acceleration and develeration
|
||||
//
|
||||
if ( in_speed.active ^ cl_run->integer ) {
|
||||
movespeed = 127;
|
||||
cmd->buttons &= ~BUTTON_WALKING;
|
||||
} else {
|
||||
cmd->buttons |= BUTTON_WALKING;
|
||||
movespeed = 64;
|
||||
}
|
||||
|
||||
forward = 0;
|
||||
side = 0;
|
||||
up = 0;
|
||||
if ( in_strafe.active ) {
|
||||
side += movespeed * CL_KeyState (&in_right);
|
||||
side -= movespeed * CL_KeyState (&in_left);
|
||||
}
|
||||
|
||||
side += movespeed * CL_KeyState (&in_moveright);
|
||||
side -= movespeed * CL_KeyState (&in_moveleft);
|
||||
|
||||
|
||||
up += movespeed * CL_KeyState (&in_up);
|
||||
up -= movespeed * CL_KeyState (&in_down);
|
||||
|
||||
forward += movespeed * CL_KeyState (&in_forward);
|
||||
forward -= movespeed * CL_KeyState (&in_back);
|
||||
|
||||
cmd->forwardmove = ClampChar( forward );
|
||||
cmd->rightmove = ClampChar( side );
|
||||
cmd->upmove = ClampChar( up );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_MouseEvent
|
||||
=================
|
||||
*/
|
||||
void CL_MouseEvent( int dx, int dy, int time ) {
|
||||
if ( cls.keyCatchers & KEYCATCH_UI ) {
|
||||
VM_Call( uivm, UI_MOUSE_EVENT, dx, dy );
|
||||
} else if (cls.keyCatchers & KEYCATCH_CGAME) {
|
||||
VM_Call (cgvm, CG_MOUSE_EVENT, dx, dy);
|
||||
} else {
|
||||
cl.mouseDx[cl.mouseIndex] += dx;
|
||||
cl.mouseDy[cl.mouseIndex] += dy;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_JoystickEvent
|
||||
|
||||
Joystick values stay set until changed
|
||||
=================
|
||||
*/
|
||||
void CL_JoystickEvent( int axis, int value, int time ) {
|
||||
if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) {
|
||||
Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis );
|
||||
}
|
||||
cl.joystickAxis[axis] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_JoystickMove
|
||||
=================
|
||||
*/
|
||||
void CL_JoystickMove( usercmd_t *cmd ) {
|
||||
int movespeed;
|
||||
float anglespeed;
|
||||
|
||||
if ( in_speed.active ^ cl_run->integer ) {
|
||||
movespeed = 2;
|
||||
} else {
|
||||
movespeed = 1;
|
||||
cmd->buttons |= BUTTON_WALKING;
|
||||
}
|
||||
|
||||
if ( in_speed.active ) {
|
||||
anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value;
|
||||
} else {
|
||||
anglespeed = 0.001 * cls.frametime;
|
||||
}
|
||||
|
||||
if ( !in_strafe.active ) {
|
||||
cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE];
|
||||
} else {
|
||||
cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] );
|
||||
}
|
||||
|
||||
if ( in_mlooking ) {
|
||||
cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD];
|
||||
} else {
|
||||
cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] );
|
||||
}
|
||||
|
||||
cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_MouseMove
|
||||
=================
|
||||
*/
|
||||
void CL_MouseMove( usercmd_t *cmd ) {
|
||||
float mx, my;
|
||||
float accelSensitivity;
|
||||
float rate;
|
||||
|
||||
// allow mouse smoothing
|
||||
if ( m_filter->integer ) {
|
||||
mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5;
|
||||
my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5;
|
||||
} else {
|
||||
mx = cl.mouseDx[cl.mouseIndex];
|
||||
my = cl.mouseDy[cl.mouseIndex];
|
||||
}
|
||||
cl.mouseIndex ^= 1;
|
||||
cl.mouseDx[cl.mouseIndex] = 0;
|
||||
cl.mouseDy[cl.mouseIndex] = 0;
|
||||
|
||||
rate = sqrt( mx * mx + my * my ) / (float)frame_msec;
|
||||
accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value;
|
||||
|
||||
// scale by FOV
|
||||
accelSensitivity *= cl.cgameSensitivity;
|
||||
|
||||
if ( rate && cl_showMouseRate->integer ) {
|
||||
Com_Printf( "%f : %f\n", rate, accelSensitivity );
|
||||
}
|
||||
|
||||
mx *= accelSensitivity;
|
||||
my *= accelSensitivity;
|
||||
|
||||
if (!mx && !my) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add mouse X/Y movement to cmd
|
||||
if ( in_strafe.active ) {
|
||||
cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx );
|
||||
} else {
|
||||
cl.viewangles[YAW] -= m_yaw->value * mx;
|
||||
}
|
||||
|
||||
if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) {
|
||||
cl.viewangles[PITCH] += m_pitch->value * my;
|
||||
} else {
|
||||
cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_CmdButtons
|
||||
==============
|
||||
*/
|
||||
void CL_CmdButtons( usercmd_t *cmd ) {
|
||||
int i;
|
||||
|
||||
//
|
||||
// figure button bits
|
||||
// send a button bit even if the key was pressed and released in
|
||||
// less than a frame
|
||||
//
|
||||
for (i = 0 ; i < 15 ; i++) {
|
||||
if ( in_buttons[i].active || in_buttons[i].wasPressed ) {
|
||||
cmd->buttons |= 1 << i;
|
||||
}
|
||||
in_buttons[i].wasPressed = qfalse;
|
||||
}
|
||||
|
||||
if ( cls.keyCatchers ) {
|
||||
cmd->buttons |= BUTTON_TALK;
|
||||
}
|
||||
|
||||
// allow the game to know if any key at all is
|
||||
// currently pressed, even if it isn't bound to anything
|
||||
if ( anykeydown && !cls.keyCatchers ) {
|
||||
cmd->buttons |= BUTTON_ANY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_FinishMove
|
||||
==============
|
||||
*/
|
||||
void CL_FinishMove( usercmd_t *cmd ) {
|
||||
int i;
|
||||
|
||||
// copy the state that the cgame is currently sending
|
||||
cmd->weapon = cl.cgameUserCmdValue;
|
||||
|
||||
// send the current server time so the amount of movement
|
||||
// can be determined without allowing cheating
|
||||
cmd->serverTime = cl.serverTime;
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_CreateCmd
|
||||
=================
|
||||
*/
|
||||
usercmd_t CL_CreateCmd( void ) {
|
||||
usercmd_t cmd;
|
||||
vec3_t oldAngles;
|
||||
|
||||
VectorCopy( cl.viewangles, oldAngles );
|
||||
|
||||
// keyboard angle adjustment
|
||||
CL_AdjustAngles ();
|
||||
|
||||
Com_Memset( &cmd, 0, sizeof( cmd ) );
|
||||
|
||||
CL_CmdButtons( &cmd );
|
||||
|
||||
// get basic movement from keyboard
|
||||
CL_KeyMove( &cmd );
|
||||
|
||||
// get basic movement from mouse
|
||||
CL_MouseMove( &cmd );
|
||||
|
||||
// get basic movement from joystick
|
||||
CL_JoystickMove( &cmd );
|
||||
|
||||
// check to make sure the angles haven't wrapped
|
||||
if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
|
||||
cl.viewangles[PITCH] = oldAngles[PITCH] + 90;
|
||||
} else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) {
|
||||
cl.viewangles[PITCH] = oldAngles[PITCH] - 90;
|
||||
}
|
||||
|
||||
// store out the final values
|
||||
CL_FinishMove( &cmd );
|
||||
|
||||
// draw debug graphs of turning for mouse testing
|
||||
if ( cl_debugMove->integer ) {
|
||||
if ( cl_debugMove->integer == 1 ) {
|
||||
SCR_DebugGraph( abs(cl.viewangles[YAW] - oldAngles[YAW]), 0 );
|
||||
}
|
||||
if ( cl_debugMove->integer == 2 ) {
|
||||
SCR_DebugGraph( abs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_CreateNewCommands
|
||||
|
||||
Create a new usercmd_t structure for this frame
|
||||
=================
|
||||
*/
|
||||
void CL_CreateNewCommands( void ) {
|
||||
usercmd_t *cmd;
|
||||
int cmdNum;
|
||||
|
||||
// no need to create usercmds until we have a gamestate
|
||||
if ( cls.state < CA_PRIMED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame_msec = com_frameTime - old_com_frameTime;
|
||||
|
||||
// if running less than 5fps, truncate the extra time to prevent
|
||||
// unexpected moves after a hitch
|
||||
if ( frame_msec > 200 ) {
|
||||
frame_msec = 200;
|
||||
}
|
||||
old_com_frameTime = com_frameTime;
|
||||
|
||||
|
||||
// generate a command for this frame
|
||||
cl.cmdNumber++;
|
||||
cmdNum = cl.cmdNumber & CMD_MASK;
|
||||
cl.cmds[cmdNum] = CL_CreateCmd ();
|
||||
cmd = &cl.cmds[cmdNum];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_ReadyToSendPacket
|
||||
|
||||
Returns qfalse if we are over the maxpackets limit
|
||||
and should choke back the bandwidth a bit by not sending
|
||||
a packet this frame. All the commands will still get
|
||||
delivered in the next packet, but saving a header and
|
||||
getting more delta compression will reduce total bandwidth.
|
||||
=================
|
||||
*/
|
||||
qboolean CL_ReadyToSendPacket( void ) {
|
||||
int oldPacketNum;
|
||||
int delta;
|
||||
|
||||
// don't send anything if playing back a demo
|
||||
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// If we are downloading, we send no less than 50ms between packets
|
||||
if ( *clc.downloadTempName &&
|
||||
cls.realtime - clc.lastPacketSentTime < 50 ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// if we don't have a valid gamestate yet, only send
|
||||
// one packet a second
|
||||
if ( cls.state != CA_ACTIVE &&
|
||||
cls.state != CA_PRIMED &&
|
||||
!*clc.downloadTempName &&
|
||||
cls.realtime - clc.lastPacketSentTime < 1000 ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// send every frame for loopbacks
|
||||
if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) {
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// send every frame for LAN
|
||||
if ( Sys_IsLANAddress( clc.netchan.remoteAddress ) ) {
|
||||
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" );
|
||||
}
|
||||
oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK;
|
||||
delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime;
|
||||
if ( delta < 1000 / cl_maxpackets->integer ) {
|
||||
// the accumulated commands will go out in the next packet
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
CL_WritePacket
|
||||
|
||||
Create and send the command packet to the server
|
||||
Including both the reliable commands and the usercmds
|
||||
|
||||
During normal gameplay, a client packet will contain something like:
|
||||
|
||||
4 sequence number
|
||||
2 qport
|
||||
4 serverid
|
||||
4 acknowledged sequence number
|
||||
4 clc.serverCommandSequence
|
||||
<optional reliable commands>
|
||||
1 clc_move or clc_moveNoDelta
|
||||
1 command count
|
||||
<count * usercmds>
|
||||
|
||||
===================
|
||||
*/
|
||||
void CL_WritePacket( void ) {
|
||||
msg_t buf;
|
||||
byte data[MAX_MSGLEN];
|
||||
int i, j;
|
||||
usercmd_t *cmd, *oldcmd;
|
||||
usercmd_t nullcmd;
|
||||
int packetNum;
|
||||
int oldPacketNum;
|
||||
int count, key;
|
||||
|
||||
// don't send anything if playing back a demo
|
||||
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
|
||||
oldcmd = &nullcmd;
|
||||
|
||||
MSG_Init( &buf, data, sizeof(data) );
|
||||
|
||||
MSG_Bitstream( &buf );
|
||||
// write the current serverId so the server
|
||||
// can tell if this is from the current gameState
|
||||
MSG_WriteLong( &buf, cl.serverId );
|
||||
|
||||
// write the last message we received, which can
|
||||
// be used for delta compression, and is also used
|
||||
// to tell if we dropped a gamestate
|
||||
MSG_WriteLong( &buf, clc.serverMessageSequence );
|
||||
|
||||
// write the last reliable message we received
|
||||
MSG_WriteLong( &buf, clc.serverCommandSequence );
|
||||
|
||||
// write any unacknowledged clientCommands
|
||||
for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) {
|
||||
MSG_WriteByte( &buf, clc_clientCommand );
|
||||
MSG_WriteLong( &buf, i );
|
||||
MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
|
||||
}
|
||||
|
||||
// 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" );
|
||||
}
|
||||
oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK;
|
||||
count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber;
|
||||
if ( count > MAX_PACKET_USERCMDS ) {
|
||||
count = MAX_PACKET_USERCMDS;
|
||||
Com_Printf("MAX_PACKET_USERCMDS\n");
|
||||
}
|
||||
if ( count >= 1 ) {
|
||||
if ( cl_showSend->integer ) {
|
||||
Com_Printf( "(%i)", count );
|
||||
}
|
||||
|
||||
// begin a client move command
|
||||
if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting
|
||||
|| clc.serverMessageSequence != cl.snap.messageNum ) {
|
||||
MSG_WriteByte (&buf, clc_moveNoDelta);
|
||||
} else {
|
||||
MSG_WriteByte (&buf, clc_move);
|
||||
}
|
||||
|
||||
// write the command count
|
||||
MSG_WriteByte( &buf, count );
|
||||
|
||||
// use the checksum feed in the key
|
||||
key = clc.checksumFeed;
|
||||
// also use the message acknowledge
|
||||
key ^= clc.serverMessageSequence;
|
||||
// also use the last acknowledged server command in the key
|
||||
key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32);
|
||||
|
||||
// write all the commands, including the predicted command
|
||||
for ( i = 0 ; i < count ; i++ ) {
|
||||
j = (cl.cmdNumber - count + i + 1) & CMD_MASK;
|
||||
cmd = &cl.cmds[j];
|
||||
MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd);
|
||||
oldcmd = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// deliver the message
|
||||
//
|
||||
packetNum = clc.netchan.outgoingSequence & PACKET_MASK;
|
||||
cl.outPackets[ packetNum ].p_realtime = cls.realtime;
|
||||
cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime;
|
||||
cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber;
|
||||
clc.lastPacketSentTime = cls.realtime;
|
||||
|
||||
if ( cl_showSend->integer ) {
|
||||
Com_Printf( "%i ", buf.cursize );
|
||||
}
|
||||
|
||||
CL_Netchan_Transmit (&clc.netchan, &buf);
|
||||
|
||||
// clients never really should have messages large enough
|
||||
// to fragment, but in case they do, fire them all off
|
||||
// at once
|
||||
// TTimo: this causes a packet burst, which is bad karma for winsock
|
||||
// added a WARNING message, we'll see if there are legit situations where this happens
|
||||
while ( clc.netchan.unsentFragments ) {
|
||||
Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" );
|
||||
CL_Netchan_TransmitNextFragment( &clc.netchan );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_SendCmd
|
||||
|
||||
Called every frame to builds and sends a command packet to the server.
|
||||
=================
|
||||
*/
|
||||
void CL_SendCmd( void ) {
|
||||
// don't send any message if not connected
|
||||
if ( cls.state < CA_CONNECTED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't send commands if paused
|
||||
if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we create commands even if a demo is playing,
|
||||
CL_CreateNewCommands();
|
||||
|
||||
// don't send a packet if the last packet was sent too recently
|
||||
if ( !CL_ReadyToSendPacket() ) {
|
||||
if ( cl_showSend->integer ) {
|
||||
Com_Printf( ". " );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CL_WritePacket();
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
CL_InitInput
|
||||
============
|
||||
*/
|
||||
void CL_InitInput( void ) {
|
||||
Cmd_AddCommand ("centerview",IN_CenterView);
|
||||
|
||||
Cmd_AddCommand ("+moveup",IN_UpDown);
|
||||
Cmd_AddCommand ("-moveup",IN_UpUp);
|
||||
Cmd_AddCommand ("+movedown",IN_DownDown);
|
||||
Cmd_AddCommand ("-movedown",IN_DownUp);
|
||||
Cmd_AddCommand ("+left",IN_LeftDown);
|
||||
Cmd_AddCommand ("-left",IN_LeftUp);
|
||||
Cmd_AddCommand ("+right",IN_RightDown);
|
||||
Cmd_AddCommand ("-right",IN_RightUp);
|
||||
Cmd_AddCommand ("+forward",IN_ForwardDown);
|
||||
Cmd_AddCommand ("-forward",IN_ForwardUp);
|
||||
Cmd_AddCommand ("+back",IN_BackDown);
|
||||
Cmd_AddCommand ("-back",IN_BackUp);
|
||||
Cmd_AddCommand ("+lookup", IN_LookupDown);
|
||||
Cmd_AddCommand ("-lookup", IN_LookupUp);
|
||||
Cmd_AddCommand ("+lookdown", IN_LookdownDown);
|
||||
Cmd_AddCommand ("-lookdown", IN_LookdownUp);
|
||||
Cmd_AddCommand ("+strafe", IN_StrafeDown);
|
||||
Cmd_AddCommand ("-strafe", IN_StrafeUp);
|
||||
Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
|
||||
Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
|
||||
Cmd_AddCommand ("+moveright", IN_MoverightDown);
|
||||
Cmd_AddCommand ("-moveright", IN_MoverightUp);
|
||||
Cmd_AddCommand ("+speed", IN_SpeedDown);
|
||||
Cmd_AddCommand ("-speed", IN_SpeedUp);
|
||||
Cmd_AddCommand ("+attack", IN_Button0Down);
|
||||
Cmd_AddCommand ("-attack", IN_Button0Up);
|
||||
Cmd_AddCommand ("+button0", IN_Button0Down);
|
||||
Cmd_AddCommand ("-button0", IN_Button0Up);
|
||||
Cmd_AddCommand ("+button1", IN_Button1Down);
|
||||
Cmd_AddCommand ("-button1", IN_Button1Up);
|
||||
Cmd_AddCommand ("+button2", IN_Button2Down);
|
||||
Cmd_AddCommand ("-button2", IN_Button2Up);
|
||||
Cmd_AddCommand ("+button3", IN_Button3Down);
|
||||
Cmd_AddCommand ("-button3", IN_Button3Up);
|
||||
Cmd_AddCommand ("+button4", IN_Button4Down);
|
||||
Cmd_AddCommand ("-button4", IN_Button4Up);
|
||||
Cmd_AddCommand ("+button5", IN_Button5Down);
|
||||
Cmd_AddCommand ("-button5", IN_Button5Up);
|
||||
Cmd_AddCommand ("+button6", IN_Button6Down);
|
||||
Cmd_AddCommand ("-button6", IN_Button6Up);
|
||||
Cmd_AddCommand ("+button7", IN_Button7Down);
|
||||
Cmd_AddCommand ("-button7", IN_Button7Up);
|
||||
Cmd_AddCommand ("+button8", IN_Button8Down);
|
||||
Cmd_AddCommand ("-button8", IN_Button8Up);
|
||||
Cmd_AddCommand ("+button9", IN_Button9Down);
|
||||
Cmd_AddCommand ("-button9", IN_Button9Up);
|
||||
Cmd_AddCommand ("+button10", IN_Button10Down);
|
||||
Cmd_AddCommand ("-button10", IN_Button10Up);
|
||||
Cmd_AddCommand ("+button11", IN_Button11Down);
|
||||
Cmd_AddCommand ("-button11", IN_Button11Up);
|
||||
Cmd_AddCommand ("+button12", IN_Button12Down);
|
||||
Cmd_AddCommand ("-button12", IN_Button12Up);
|
||||
Cmd_AddCommand ("+button13", IN_Button13Down);
|
||||
Cmd_AddCommand ("-button13", IN_Button13Up);
|
||||
Cmd_AddCommand ("+button14", IN_Button14Down);
|
||||
Cmd_AddCommand ("-button14", IN_Button14Up);
|
||||
Cmd_AddCommand ("+mlook", IN_MLookDown);
|
||||
Cmd_AddCommand ("-mlook", IN_MLookUp);
|
||||
|
||||
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
|
||||
cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0);
|
||||
}
|
1252
code/client/cl_keys.c
Normal file
1252
code/client/cl_keys.c
Normal file
File diff suppressed because it is too large
Load diff
3324
code/client/cl_main.c
Normal file
3324
code/client/cl_main.c
Normal file
File diff suppressed because it is too large
Load diff
167
code/client/cl_net_chan.c
Normal file
167
code/client/cl_net_chan.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../game/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_Netchan_Encode
|
||||
|
||||
// first 12 bytes of the data are always:
|
||||
long serverId;
|
||||
long messageAcknowledge;
|
||||
long reliableAcknowledge;
|
||||
|
||||
==============
|
||||
*/
|
||||
static void CL_Netchan_Encode( msg_t *msg ) {
|
||||
int serverId, messageAcknowledge, reliableAcknowledge;
|
||||
int i, index, srdc, sbit, soob;
|
||||
byte key, *string;
|
||||
|
||||
if ( msg->cursize <= CL_ENCODE_START ) {
|
||||
return;
|
||||
}
|
||||
|
||||
srdc = msg->readcount;
|
||||
sbit = msg->bit;
|
||||
soob = msg->oob;
|
||||
|
||||
msg->bit = 0;
|
||||
msg->readcount = 0;
|
||||
msg->oob = 0;
|
||||
|
||||
serverId = MSG_ReadLong(msg);
|
||||
messageAcknowledge = MSG_ReadLong(msg);
|
||||
reliableAcknowledge = MSG_ReadLong(msg);
|
||||
|
||||
msg->oob = soob;
|
||||
msg->bit = sbit;
|
||||
msg->readcount = srdc;
|
||||
|
||||
string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
|
||||
index = 0;
|
||||
//
|
||||
key = clc.challenge ^ serverId ^ messageAcknowledge;
|
||||
for (i = CL_ENCODE_START; i < msg->cursize; i++) {
|
||||
// modify the key with the last received now acknowledged server command
|
||||
if (!string[index])
|
||||
index = 0;
|
||||
if (string[index] > 127 || string[index] == '%') {
|
||||
key ^= '.' << (i & 1);
|
||||
}
|
||||
else {
|
||||
key ^= string[index] << (i & 1);
|
||||
}
|
||||
index++;
|
||||
// encode the data with this key
|
||||
*(msg->data + i) = (*(msg->data + i)) ^ key;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_Netchan_Decode
|
||||
|
||||
// first four bytes of the data are always:
|
||||
long reliableAcknowledge;
|
||||
|
||||
==============
|
||||
*/
|
||||
static void CL_Netchan_Decode( msg_t *msg ) {
|
||||
long reliableAcknowledge, i, index;
|
||||
byte key, *string;
|
||||
int srdc, sbit, soob;
|
||||
|
||||
srdc = msg->readcount;
|
||||
sbit = msg->bit;
|
||||
soob = msg->oob;
|
||||
|
||||
msg->oob = 0;
|
||||
|
||||
reliableAcknowledge = MSG_ReadLong(msg);
|
||||
|
||||
msg->oob = soob;
|
||||
msg->bit = sbit;
|
||||
msg->readcount = srdc;
|
||||
|
||||
string = clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
|
||||
index = 0;
|
||||
// xor the client challenge with the netchan sequence number (need something that changes every message)
|
||||
key = clc.challenge ^ LittleLong( *(unsigned *)msg->data );
|
||||
for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) {
|
||||
// modify the key with the last sent and with this message acknowledged client command
|
||||
if (!string[index])
|
||||
index = 0;
|
||||
if (string[index] > 127 || string[index] == '%') {
|
||||
key ^= '.' << (i & 1);
|
||||
}
|
||||
else {
|
||||
key ^= string[index] << (i & 1);
|
||||
}
|
||||
index++;
|
||||
// decode the data with this key
|
||||
*(msg->data + i) = *(msg->data + i) ^ key;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_Netchan_TransmitNextFragment
|
||||
=================
|
||||
*/
|
||||
void CL_Netchan_TransmitNextFragment( netchan_t *chan ) {
|
||||
Netchan_TransmitNextFragment( chan );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_Netchan_Transmit
|
||||
================
|
||||
*/
|
||||
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
|
||||
MSG_WriteByte( msg, clc_EOF );
|
||||
|
||||
CL_Netchan_Encode( msg );
|
||||
Netchan_Transmit( chan, msg->cursize, msg->data );
|
||||
}
|
||||
|
||||
extern int oldsize;
|
||||
int newsize = 0;
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_Netchan_Process
|
||||
=================
|
||||
*/
|
||||
qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
|
||||
int ret;
|
||||
|
||||
ret = Netchan_Process( chan, msg );
|
||||
if (!ret)
|
||||
return qfalse;
|
||||
CL_Netchan_Decode( msg );
|
||||
newsize += msg->cursize;
|
||||
return qtrue;
|
||||
}
|
655
code/client/cl_parse.c
Normal file
655
code/client/cl_parse.c
Normal file
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// cl_parse.c -- parse a message received from the server
|
||||
|
||||
#include "client.h"
|
||||
|
||||
char *svc_strings[256] = {
|
||||
"svc_bad",
|
||||
|
||||
"svc_nop",
|
||||
"svc_gamestate",
|
||||
"svc_configstring",
|
||||
"svc_baseline",
|
||||
"svc_serverCommand",
|
||||
"svc_download",
|
||||
"svc_snapshot"
|
||||
};
|
||||
|
||||
void SHOWNET( msg_t *msg, char *s) {
|
||||
if ( cl_shownet->integer >= 2) {
|
||||
Com_Printf ("%3i:%s\n", msg->readcount-1, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================================================================
|
||||
|
||||
MESSAGE PARSING
|
||||
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_DeltaEntity
|
||||
|
||||
Parses deltas from the given base and adds the resulting entity
|
||||
to the current frame
|
||||
==================
|
||||
*/
|
||||
void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
|
||||
qboolean unchanged) {
|
||||
entityState_t *state;
|
||||
|
||||
// save the parsed entity state into the big circular buffer so
|
||||
// it can be used as the source for a later delta
|
||||
state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
|
||||
|
||||
if ( unchanged ) {
|
||||
*state = *old;
|
||||
} else {
|
||||
MSG_ReadDeltaEntity( msg, old, state, newnum );
|
||||
}
|
||||
|
||||
if ( state->number == (MAX_GENTITIES-1) ) {
|
||||
return; // entity was delta removed
|
||||
}
|
||||
cl.parseEntitiesNum++;
|
||||
frame->numEntities++;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_ParsePacketEntities
|
||||
|
||||
==================
|
||||
*/
|
||||
void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
|
||||
int newnum;
|
||||
entityState_t *oldstate;
|
||||
int oldindex, oldnum;
|
||||
|
||||
newframe->parseEntitiesNum = cl.parseEntitiesNum;
|
||||
newframe->numEntities = 0;
|
||||
|
||||
// delta from the entities present in oldframe
|
||||
oldindex = 0;
|
||||
oldstate = NULL;
|
||||
if (!oldframe) {
|
||||
oldnum = 99999;
|
||||
} else {
|
||||
if ( oldindex >= oldframe->numEntities ) {
|
||||
oldnum = 99999;
|
||||
} else {
|
||||
oldstate = &cl.parseEntities[
|
||||
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
||||
oldnum = oldstate->number;
|
||||
}
|
||||
}
|
||||
|
||||
while ( 1 ) {
|
||||
// read the entity index number
|
||||
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
|
||||
|
||||
if ( newnum == (MAX_GENTITIES-1) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( msg->readcount > msg->cursize ) {
|
||||
Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
|
||||
}
|
||||
|
||||
while ( oldnum < newnum ) {
|
||||
// one or more entities from the old packet are unchanged
|
||||
if ( cl_shownet->integer == 3 ) {
|
||||
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
|
||||
}
|
||||
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
|
||||
|
||||
oldindex++;
|
||||
|
||||
if ( oldindex >= oldframe->numEntities ) {
|
||||
oldnum = 99999;
|
||||
} else {
|
||||
oldstate = &cl.parseEntities[
|
||||
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
||||
oldnum = oldstate->number;
|
||||
}
|
||||
}
|
||||
if (oldnum == newnum) {
|
||||
// delta from previous state
|
||||
if ( cl_shownet->integer == 3 ) {
|
||||
Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum);
|
||||
}
|
||||
CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
|
||||
|
||||
oldindex++;
|
||||
|
||||
if ( oldindex >= oldframe->numEntities ) {
|
||||
oldnum = 99999;
|
||||
} else {
|
||||
oldstate = &cl.parseEntities[
|
||||
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
||||
oldnum = oldstate->number;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( oldnum > newnum ) {
|
||||
// delta from baseline
|
||||
if ( cl_shownet->integer == 3 ) {
|
||||
Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum);
|
||||
}
|
||||
CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// any remaining entities in the old frame are copied over
|
||||
while ( oldnum != 99999 ) {
|
||||
// one or more entities from the old packet are unchanged
|
||||
if ( cl_shownet->integer == 3 ) {
|
||||
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
|
||||
}
|
||||
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
|
||||
|
||||
oldindex++;
|
||||
|
||||
if ( oldindex >= oldframe->numEntities ) {
|
||||
oldnum = 99999;
|
||||
} else {
|
||||
oldstate = &cl.parseEntities[
|
||||
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
||||
oldnum = oldstate->number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CL_ParseSnapshot
|
||||
|
||||
If the snapshot is parsed properly, it will be copied to
|
||||
cl.snap and saved in cl.snapshots[]. If the snapshot is invalid
|
||||
for any reason, no changes to the state will be made at all.
|
||||
================
|
||||
*/
|
||||
void CL_ParseSnapshot( msg_t *msg ) {
|
||||
int len;
|
||||
clSnapshot_t *old;
|
||||
clSnapshot_t newSnap;
|
||||
int deltaNum;
|
||||
int oldMessageNum;
|
||||
int i, packetNum;
|
||||
|
||||
// get the reliable sequence acknowledge number
|
||||
// NOTE: now sent with all server to client messages
|
||||
//clc.reliableAcknowledge = MSG_ReadLong( msg );
|
||||
|
||||
// read in the new snapshot to a temporary buffer
|
||||
// we will only copy to cl.snap if it is valid
|
||||
Com_Memset (&newSnap, 0, sizeof(newSnap));
|
||||
|
||||
// we will have read any new server commands in this
|
||||
// message before we got to svc_snapshot
|
||||
newSnap.serverCommandNum = clc.serverCommandSequence;
|
||||
|
||||
newSnap.serverTime = MSG_ReadLong( msg );
|
||||
|
||||
newSnap.messageNum = clc.serverMessageSequence;
|
||||
|
||||
deltaNum = MSG_ReadByte( msg );
|
||||
if ( !deltaNum ) {
|
||||
newSnap.deltaNum = -1;
|
||||
} else {
|
||||
newSnap.deltaNum = newSnap.messageNum - deltaNum;
|
||||
}
|
||||
newSnap.snapFlags = MSG_ReadByte( msg );
|
||||
|
||||
// If the frame is delta compressed from data that we
|
||||
// no longer have available, we must suck up the rest of
|
||||
// the frame, but not use it, then ask for a non-compressed
|
||||
// message
|
||||
if ( newSnap.deltaNum <= 0 ) {
|
||||
newSnap.valid = qtrue; // uncompressed frame
|
||||
old = NULL;
|
||||
clc.demowaiting = qfalse; // we can start recording now
|
||||
} else {
|
||||
old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
|
||||
if ( !old->valid ) {
|
||||
// should never happen
|
||||
Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
|
||||
} else if ( old->messageNum != newSnap.deltaNum ) {
|
||||
// The frame that the server did the delta from
|
||||
// is too old, so we can't reconstruct it properly.
|
||||
Com_Printf ("Delta frame too old.\n");
|
||||
} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
|
||||
Com_Printf ("Delta parseEntitiesNum too old.\n");
|
||||
} else {
|
||||
newSnap.valid = qtrue; // valid delta parse
|
||||
}
|
||||
}
|
||||
|
||||
// read areamask
|
||||
len = MSG_ReadByte( msg );
|
||||
MSG_ReadData( msg, &newSnap.areamask, len);
|
||||
|
||||
// read playerinfo
|
||||
SHOWNET( msg, "playerstate" );
|
||||
if ( old ) {
|
||||
MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
|
||||
} else {
|
||||
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
|
||||
}
|
||||
|
||||
// read packet entities
|
||||
SHOWNET( msg, "packet entities" );
|
||||
CL_ParsePacketEntities( msg, old, &newSnap );
|
||||
|
||||
// if not valid, dump the entire thing now that it has
|
||||
// been properly read
|
||||
if ( !newSnap.valid ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clear the valid flags of any snapshots between the last
|
||||
// received and this one, so if there was a dropped packet
|
||||
// it won't look like something valid to delta from next
|
||||
// time we wrap around in the buffer
|
||||
oldMessageNum = cl.snap.messageNum + 1;
|
||||
|
||||
if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
|
||||
oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
|
||||
}
|
||||
for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
|
||||
cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
|
||||
}
|
||||
|
||||
// copy to the current good spot
|
||||
cl.snap = newSnap;
|
||||
cl.snap.ping = 999;
|
||||
// calculate ping time
|
||||
for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
|
||||
packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
|
||||
if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
|
||||
cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// save the frame off in the backup array for later delta comparisons
|
||||
cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
|
||||
|
||||
if (cl_shownet->integer == 3) {
|
||||
Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum,
|
||||
cl.snap.deltaNum, cl.snap.ping );
|
||||
}
|
||||
|
||||
cl.newSnapshots = qtrue;
|
||||
}
|
||||
|
||||
|
||||
//=====================================================================
|
||||
|
||||
int cl_connectedToPureServer;
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_SystemInfoChanged
|
||||
|
||||
The systeminfo configstring has been changed, so parse
|
||||
new information out of it. This will happen at every
|
||||
gamestate, and possibly during gameplay.
|
||||
==================
|
||||
*/
|
||||
void CL_SystemInfoChanged( void ) {
|
||||
char *systemInfo;
|
||||
const char *s, *t;
|
||||
char key[BIG_INFO_KEY];
|
||||
char value[BIG_INFO_VALUE];
|
||||
qboolean gameSet;
|
||||
|
||||
systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
|
||||
// NOTE TTimo:
|
||||
// when the serverId changes, any further messages we send to the server will use this new serverId
|
||||
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
|
||||
// in some cases, outdated cp commands might get sent with this news serverId
|
||||
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
|
||||
|
||||
// don't set any vars when playing a demo
|
||||
if ( clc.demoplaying ) {
|
||||
return;
|
||||
}
|
||||
|
||||
s = Info_ValueForKey( systemInfo, "sv_cheats" );
|
||||
if ( atoi(s) == 0 ) {
|
||||
Cvar_SetCheatState();
|
||||
}
|
||||
|
||||
// check pure server string
|
||||
s = Info_ValueForKey( systemInfo, "sv_paks" );
|
||||
t = Info_ValueForKey( systemInfo, "sv_pakNames" );
|
||||
FS_PureServerSetLoadedPaks( s, t );
|
||||
|
||||
s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
|
||||
t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
|
||||
FS_PureServerSetReferencedPaks( s, t );
|
||||
|
||||
gameSet = qfalse;
|
||||
// scan through all the variables in the systeminfo and locally set cvars to match
|
||||
s = systemInfo;
|
||||
while ( s ) {
|
||||
Info_NextPair( &s, key, value );
|
||||
if ( !key[0] ) {
|
||||
break;
|
||||
}
|
||||
// ehw!
|
||||
if ( !Q_stricmp( key, "fs_game" ) ) {
|
||||
gameSet = qtrue;
|
||||
}
|
||||
|
||||
Cvar_Set( key, value );
|
||||
}
|
||||
// if game folder should not be set and it is set at the client side
|
||||
if ( !gameSet && *Cvar_VariableString("fs_game") ) {
|
||||
Cvar_Set( "fs_game", "" );
|
||||
}
|
||||
cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_ParseGamestate
|
||||
==================
|
||||
*/
|
||||
void CL_ParseGamestate( msg_t *msg ) {
|
||||
int i;
|
||||
entityState_t *es;
|
||||
int newnum;
|
||||
entityState_t nullstate;
|
||||
int cmd;
|
||||
char *s;
|
||||
|
||||
Con_Close();
|
||||
|
||||
clc.connectPacketCount = 0;
|
||||
|
||||
// wipe local client state
|
||||
CL_ClearState();
|
||||
|
||||
// a gamestate always marks a server command sequence
|
||||
clc.serverCommandSequence = MSG_ReadLong( msg );
|
||||
|
||||
// parse all the configstrings and baselines
|
||||
cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
|
||||
while ( 1 ) {
|
||||
cmd = MSG_ReadByte( msg );
|
||||
|
||||
if ( cmd == svc_EOF ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cmd == svc_configstring ) {
|
||||
int len;
|
||||
|
||||
i = MSG_ReadShort( msg );
|
||||
if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
|
||||
Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
|
||||
}
|
||||
s = MSG_ReadBigString( msg );
|
||||
len = strlen( s );
|
||||
|
||||
if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
|
||||
Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
|
||||
}
|
||||
|
||||
// append it to the gameState string buffer
|
||||
cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
|
||||
Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
|
||||
cl.gameState.dataCount += len + 1;
|
||||
} else if ( cmd == svc_baseline ) {
|
||||
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
|
||||
if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
|
||||
Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
|
||||
}
|
||||
Com_Memset (&nullstate, 0, sizeof(nullstate));
|
||||
es = &cl.entityBaselines[ newnum ];
|
||||
MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
|
||||
} else {
|
||||
Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
|
||||
}
|
||||
}
|
||||
|
||||
clc.clientNum = MSG_ReadLong(msg);
|
||||
// read the checksum feed
|
||||
clc.checksumFeed = MSG_ReadLong( msg );
|
||||
|
||||
// parse serverId and other cvars
|
||||
CL_SystemInfoChanged();
|
||||
|
||||
// reinitialize the filesystem if the game directory has changed
|
||||
FS_ConditionalRestart( clc.checksumFeed );
|
||||
|
||||
// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
|
||||
// cgame
|
||||
CL_InitDownloads();
|
||||
|
||||
// make sure the game starts
|
||||
Cvar_Set( "cl_paused", "0" );
|
||||
}
|
||||
|
||||
|
||||
//=====================================================================
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseDownload
|
||||
|
||||
A download message has been received from the server
|
||||
=====================
|
||||
*/
|
||||
void CL_ParseDownload ( msg_t *msg ) {
|
||||
int size;
|
||||
unsigned char data[MAX_MSGLEN];
|
||||
int block;
|
||||
|
||||
// read the data
|
||||
block = MSG_ReadShort ( msg );
|
||||
|
||||
if ( !block )
|
||||
{
|
||||
// block zero is special, contains file size
|
||||
clc.downloadSize = MSG_ReadLong ( msg );
|
||||
|
||||
Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
|
||||
|
||||
if (clc.downloadSize < 0)
|
||||
{
|
||||
Com_Error(ERR_DROP, MSG_ReadString( msg ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size = MSG_ReadShort ( msg );
|
||||
if (size > 0)
|
||||
MSG_ReadData( msg, data, size );
|
||||
|
||||
if (clc.downloadBlock != block) {
|
||||
Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
|
||||
return;
|
||||
}
|
||||
|
||||
// open the file if not opened yet
|
||||
if (!clc.download)
|
||||
{
|
||||
if (!*clc.downloadTempName) {
|
||||
Com_Printf("Server sending download, but no download was requested\n");
|
||||
CL_AddReliableCommand( "stopdl" );
|
||||
return;
|
||||
}
|
||||
|
||||
clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
|
||||
|
||||
if (!clc.download) {
|
||||
Com_Printf( "Could not create %s\n", clc.downloadTempName );
|
||||
CL_AddReliableCommand( "stopdl" );
|
||||
CL_NextDownload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (size)
|
||||
FS_Write( data, size, clc.download );
|
||||
|
||||
CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
|
||||
clc.downloadBlock++;
|
||||
|
||||
clc.downloadCount += size;
|
||||
|
||||
// So UI gets access to it
|
||||
Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
|
||||
|
||||
if (!size) { // A zero length block means EOF
|
||||
if (clc.download) {
|
||||
FS_FCloseFile( clc.download );
|
||||
clc.download = 0;
|
||||
|
||||
// rename the file
|
||||
FS_SV_Rename ( clc.downloadTempName, clc.downloadName );
|
||||
}
|
||||
*clc.downloadTempName = *clc.downloadName = 0;
|
||||
Cvar_Set( "cl_downloadName", "" );
|
||||
|
||||
// send intentions now
|
||||
// We need this because without it, we would hold the last nextdl and then start
|
||||
// loading right away. If we take a while to load, the server is happily trying
|
||||
// to send us that last block over and over.
|
||||
// Write it twice to help make sure we acknowledge the download
|
||||
CL_WritePacket();
|
||||
CL_WritePacket();
|
||||
|
||||
// get another file if needed
|
||||
CL_NextDownload ();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseCommandString
|
||||
|
||||
Command strings are just saved off until cgame asks for them
|
||||
when it transitions a snapshot
|
||||
=====================
|
||||
*/
|
||||
void CL_ParseCommandString( msg_t *msg ) {
|
||||
char *s;
|
||||
int seq;
|
||||
int index;
|
||||
|
||||
seq = MSG_ReadLong( msg );
|
||||
s = MSG_ReadString( msg );
|
||||
|
||||
// see if we have already executed stored it off
|
||||
if ( clc.serverCommandSequence >= seq ) {
|
||||
return;
|
||||
}
|
||||
clc.serverCommandSequence = seq;
|
||||
|
||||
index = seq & (MAX_RELIABLE_COMMANDS-1);
|
||||
Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseServerMessage
|
||||
=====================
|
||||
*/
|
||||
void CL_ParseServerMessage( msg_t *msg ) {
|
||||
int cmd;
|
||||
|
||||
if ( cl_shownet->integer == 1 ) {
|
||||
Com_Printf ("%i ",msg->cursize);
|
||||
} else if ( cl_shownet->integer >= 2 ) {
|
||||
Com_Printf ("------------------\n");
|
||||
}
|
||||
|
||||
MSG_Bitstream(msg);
|
||||
|
||||
// get the reliable sequence acknowledge number
|
||||
clc.reliableAcknowledge = MSG_ReadLong( msg );
|
||||
//
|
||||
if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
|
||||
clc.reliableAcknowledge = clc.reliableSequence;
|
||||
}
|
||||
|
||||
//
|
||||
// parse the message
|
||||
//
|
||||
while ( 1 ) {
|
||||
if ( msg->readcount > msg->cursize ) {
|
||||
Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
|
||||
break;
|
||||
}
|
||||
|
||||
cmd = MSG_ReadByte( msg );
|
||||
|
||||
if ( cmd == svc_EOF) {
|
||||
SHOWNET( msg, "END OF MESSAGE" );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cl_shownet->integer >= 2 ) {
|
||||
if ( !svc_strings[cmd] ) {
|
||||
Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
|
||||
} else {
|
||||
SHOWNET( msg, svc_strings[cmd] );
|
||||
}
|
||||
}
|
||||
|
||||
// other commands
|
||||
switch ( cmd ) {
|
||||
default:
|
||||
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
|
||||
break;
|
||||
case svc_nop:
|
||||
break;
|
||||
case svc_serverCommand:
|
||||
CL_ParseCommandString( msg );
|
||||
break;
|
||||
case svc_gamestate:
|
||||
CL_ParseGamestate( msg );
|
||||
break;
|
||||
case svc_snapshot:
|
||||
CL_ParseSnapshot( msg );
|
||||
break;
|
||||
case svc_download:
|
||||
CL_ParseDownload( msg );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
547
code/client/cl_scrn.c
Normal file
547
code/client/cl_scrn.c
Normal file
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
|
||||
|
||||
#include "client.h"
|
||||
|
||||
qboolean scr_initialized; // ready to draw
|
||||
|
||||
cvar_t *cl_timegraph;
|
||||
cvar_t *cl_debuggraph;
|
||||
cvar_t *cl_graphheight;
|
||||
cvar_t *cl_graphscale;
|
||||
cvar_t *cl_graphshift;
|
||||
|
||||
/*
|
||||
================
|
||||
SCR_DrawNamedPic
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
|
||||
qhandle_t hShader;
|
||||
|
||||
assert( width != 0 );
|
||||
|
||||
hShader = re.RegisterShader( picname );
|
||||
SCR_AdjustFrom640( &x, &y, &width, &height );
|
||||
re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SCR_AdjustFrom640
|
||||
|
||||
Adjusted for resolution and screen aspect ratio
|
||||
================
|
||||
*/
|
||||
void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) {
|
||||
float xscale;
|
||||
float yscale;
|
||||
|
||||
#if 0
|
||||
// adjust for wide screens
|
||||
if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
|
||||
*x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
// scale for screen sizes
|
||||
xscale = cls.glconfig.vidWidth / 640.0;
|
||||
yscale = cls.glconfig.vidHeight / 480.0;
|
||||
if ( x ) {
|
||||
*x *= xscale;
|
||||
}
|
||||
if ( y ) {
|
||||
*y *= yscale;
|
||||
}
|
||||
if ( w ) {
|
||||
*w *= xscale;
|
||||
}
|
||||
if ( h ) {
|
||||
*h *= yscale;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SCR_FillRect
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void SCR_FillRect( float x, float y, float width, float height, const float *color ) {
|
||||
re.SetColor( color );
|
||||
|
||||
SCR_AdjustFrom640( &x, &y, &width, &height );
|
||||
re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader );
|
||||
|
||||
re.SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SCR_DrawPic
|
||||
|
||||
Coordinates are 640*480 virtual values
|
||||
=================
|
||||
*/
|
||||
void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
|
||||
SCR_AdjustFrom640( &x, &y, &width, &height );
|
||||
re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** SCR_DrawChar
|
||||
** chars are drawn at 640*480 virtual screen size
|
||||
*/
|
||||
static void SCR_DrawChar( int x, int y, float size, int ch ) {
|
||||
int row, col;
|
||||
float frow, fcol;
|
||||
float ax, ay, aw, ah;
|
||||
|
||||
ch &= 255;
|
||||
|
||||
if ( ch == ' ' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( y < -size ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ax = x;
|
||||
ay = y;
|
||||
aw = size;
|
||||
ah = size;
|
||||
SCR_AdjustFrom640( &ax, &ay, &aw, &ah );
|
||||
|
||||
row = ch>>4;
|
||||
col = ch&15;
|
||||
|
||||
frow = row*0.0625;
|
||||
fcol = col*0.0625;
|
||||
size = 0.0625;
|
||||
|
||||
re.DrawStretchPic( ax, ay, aw, ah,
|
||||
fcol, frow,
|
||||
fcol + size, frow + size,
|
||||
cls.charSetShader );
|
||||
}
|
||||
|
||||
/*
|
||||
** SCR_DrawSmallChar
|
||||
** small chars are drawn at native screen resolution
|
||||
*/
|
||||
void SCR_DrawSmallChar( int x, int y, int ch ) {
|
||||
int row, col;
|
||||
float frow, fcol;
|
||||
float size;
|
||||
|
||||
ch &= 255;
|
||||
|
||||
if ( ch == ' ' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( y < -SMALLCHAR_HEIGHT ) {
|
||||
return;
|
||||
}
|
||||
|
||||
row = ch>>4;
|
||||
col = ch&15;
|
||||
|
||||
frow = row*0.0625;
|
||||
fcol = col*0.0625;
|
||||
size = 0.0625;
|
||||
|
||||
re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
|
||||
fcol, frow,
|
||||
fcol + size, frow + size,
|
||||
cls.charSetShader );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawBigString[Color]
|
||||
|
||||
Draws a multi-colored string with a drop shadow, optionally forcing
|
||||
to a fixed color.
|
||||
|
||||
Coordinates are at 640 by 480 virtual resolution
|
||||
==================
|
||||
*/
|
||||
void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor ) {
|
||||
vec4_t color;
|
||||
const char *s;
|
||||
int xx;
|
||||
|
||||
// draw the drop shadow
|
||||
color[0] = color[1] = color[2] = 0;
|
||||
color[3] = setColor[3];
|
||||
re.SetColor( color );
|
||||
s = string;
|
||||
xx = x;
|
||||
while ( *s ) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
SCR_DrawChar( xx+2, y+2, size, *s );
|
||||
xx += size;
|
||||
s++;
|
||||
}
|
||||
|
||||
|
||||
// draw the colored text
|
||||
s = string;
|
||||
xx = x;
|
||||
re.SetColor( setColor );
|
||||
while ( *s ) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
if ( !forceColor ) {
|
||||
Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
|
||||
color[3] = setColor[3];
|
||||
re.SetColor( color );
|
||||
}
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
SCR_DrawChar( xx, y, size, *s );
|
||||
xx += size;
|
||||
s++;
|
||||
}
|
||||
re.SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
void SCR_DrawBigString( int x, int y, const char *s, float alpha ) {
|
||||
float color[4];
|
||||
|
||||
color[0] = color[1] = color[2] = 1.0;
|
||||
color[3] = alpha;
|
||||
SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse );
|
||||
}
|
||||
|
||||
void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) {
|
||||
SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawSmallString[Color]
|
||||
|
||||
Draws a multi-colored string with a drop shadow, optionally forcing
|
||||
to a fixed color.
|
||||
|
||||
Coordinates are at 640 by 480 virtual resolution
|
||||
==================
|
||||
*/
|
||||
void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ) {
|
||||
vec4_t color;
|
||||
const char *s;
|
||||
int xx;
|
||||
|
||||
// draw the colored text
|
||||
s = string;
|
||||
xx = x;
|
||||
re.SetColor( setColor );
|
||||
while ( *s ) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
if ( !forceColor ) {
|
||||
Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
|
||||
color[3] = setColor[3];
|
||||
re.SetColor( color );
|
||||
}
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
SCR_DrawSmallChar( xx, y, *s );
|
||||
xx += SMALLCHAR_WIDTH;
|
||||
s++;
|
||||
}
|
||||
re.SetColor( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** SCR_Strlen -- skips color escape codes
|
||||
*/
|
||||
static int SCR_Strlen( const char *str ) {
|
||||
const char *s = str;
|
||||
int count = 0;
|
||||
|
||||
while ( *s ) {
|
||||
if ( Q_IsColorString( s ) ) {
|
||||
s += 2;
|
||||
} else {
|
||||
count++;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
** SCR_GetBigStringWidth
|
||||
*/
|
||||
int SCR_GetBigStringWidth( const char *str ) {
|
||||
return SCR_Strlen( str ) * 16;
|
||||
}
|
||||
|
||||
|
||||
//===============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
SCR_DrawDemoRecording
|
||||
=================
|
||||
*/
|
||||
void SCR_DrawDemoRecording( void ) {
|
||||
char string[1024];
|
||||
int pos;
|
||||
|
||||
if ( !clc.demorecording ) {
|
||||
return;
|
||||
}
|
||||
if ( clc.spDemoRecording ) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos = FS_FTell( clc.demofile );
|
||||
sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 );
|
||||
|
||||
SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
DEBUG GRAPH
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float value;
|
||||
int color;
|
||||
} graphsamp_t;
|
||||
|
||||
static int current;
|
||||
static graphsamp_t values[1024];
|
||||
|
||||
/*
|
||||
==============
|
||||
SCR_DebugGraph
|
||||
==============
|
||||
*/
|
||||
void SCR_DebugGraph (float value, int color)
|
||||
{
|
||||
values[current&1023].value = value;
|
||||
values[current&1023].color = color;
|
||||
current++;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SCR_DrawDebugGraph
|
||||
==============
|
||||
*/
|
||||
void SCR_DrawDebugGraph (void)
|
||||
{
|
||||
int a, x, y, w, i, h;
|
||||
float v;
|
||||
int color;
|
||||
|
||||
//
|
||||
// draw the graph
|
||||
//
|
||||
w = cls.glconfig.vidWidth;
|
||||
x = 0;
|
||||
y = cls.glconfig.vidHeight;
|
||||
re.SetColor( g_color_table[0] );
|
||||
re.DrawStretchPic(x, y - cl_graphheight->integer,
|
||||
w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader );
|
||||
re.SetColor( NULL );
|
||||
|
||||
for (a=0 ; a<w ; a++)
|
||||
{
|
||||
i = (current-1-a+1024) & 1023;
|
||||
v = values[i].value;
|
||||
color = values[i].color;
|
||||
v = v * cl_graphscale->integer + cl_graphshift->integer;
|
||||
|
||||
if (v < 0)
|
||||
v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer));
|
||||
h = (int)v % cl_graphheight->integer;
|
||||
re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader );
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_Init
|
||||
==================
|
||||
*/
|
||||
void SCR_Init( void ) {
|
||||
cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT);
|
||||
cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT);
|
||||
cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT);
|
||||
cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT);
|
||||
cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT);
|
||||
|
||||
scr_initialized = qtrue;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawScreenField
|
||||
|
||||
This will be called twice if rendering in stereo mode
|
||||
==================
|
||||
*/
|
||||
void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
|
||||
re.BeginFrame( stereoFrame );
|
||||
|
||||
// wide aspect ratio screens need to have the sides cleared
|
||||
// unless they are displaying game renderings
|
||||
if ( cls.state != CA_ACTIVE ) {
|
||||
if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
|
||||
re.SetColor( g_color_table[0] );
|
||||
re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader );
|
||||
re.SetColor( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !uivm ) {
|
||||
Com_DPrintf("draw screen without UI loaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// if the menu is going to cover the entire screen, we
|
||||
// don't need to render anything under it
|
||||
if ( !VM_Call( uivm, UI_IS_FULLSCREEN )) {
|
||||
switch( cls.state ) {
|
||||
default:
|
||||
Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" );
|
||||
break;
|
||||
case CA_CINEMATIC:
|
||||
SCR_DrawCinematic();
|
||||
break;
|
||||
case CA_DISCONNECTED:
|
||||
// force menu up
|
||||
S_StopAllSounds();
|
||||
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
|
||||
break;
|
||||
case CA_CONNECTING:
|
||||
case CA_CHALLENGING:
|
||||
case CA_CONNECTED:
|
||||
// connecting clients will only show the connection dialog
|
||||
// refresh to update the time
|
||||
VM_Call( uivm, UI_REFRESH, cls.realtime );
|
||||
VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
|
||||
break;
|
||||
case CA_LOADING:
|
||||
case CA_PRIMED:
|
||||
// draw the game information screen and loading progress
|
||||
CL_CGameRendering( stereoFrame );
|
||||
|
||||
// also draw the connection information, so it doesn't
|
||||
// flash away too briefly on local or lan games
|
||||
// refresh to update the time
|
||||
VM_Call( uivm, UI_REFRESH, cls.realtime );
|
||||
VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
|
||||
break;
|
||||
case CA_ACTIVE:
|
||||
CL_CGameRendering( stereoFrame );
|
||||
SCR_DrawDemoRecording();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// the menu draws next
|
||||
if ( cls.keyCatchers & KEYCATCH_UI && uivm ) {
|
||||
VM_Call( uivm, UI_REFRESH, cls.realtime );
|
||||
}
|
||||
|
||||
// console draws next
|
||||
Con_DrawConsole ();
|
||||
|
||||
// debug graph can be drawn on top of anything
|
||||
if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) {
|
||||
SCR_DrawDebugGraph ();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_UpdateScreen
|
||||
|
||||
This is called every frame, and can also be called explicitly to flush
|
||||
text to the screen.
|
||||
==================
|
||||
*/
|
||||
void SCR_UpdateScreen( void ) {
|
||||
static int recursive;
|
||||
|
||||
if ( !scr_initialized ) {
|
||||
return; // not initialized yet
|
||||
}
|
||||
|
||||
if ( ++recursive > 2 ) {
|
||||
Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
|
||||
}
|
||||
recursive = 1;
|
||||
|
||||
// if running in stereo, we need to draw the frame twice
|
||||
if ( cls.glconfig.stereoEnabled ) {
|
||||
SCR_DrawScreenField( STEREO_LEFT );
|
||||
SCR_DrawScreenField( STEREO_RIGHT );
|
||||
} else {
|
||||
SCR_DrawScreenField( STEREO_CENTER );
|
||||
}
|
||||
|
||||
if ( com_speeds->integer ) {
|
||||
re.EndFrame( &time_frontend, &time_backend );
|
||||
} else {
|
||||
re.EndFrame( NULL, NULL );
|
||||
}
|
||||
|
||||
recursive = 0;
|
||||
}
|
||||
|
1200
code/client/cl_ui.c
Normal file
1200
code/client/cl_ui.c
Normal file
File diff suppressed because it is too large
Load diff
519
code/client/client.h
Normal file
519
code/client/client.h
Normal file
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// client.h -- primary header for client
|
||||
|
||||
#include "../game/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "../renderer/tr_public.h"
|
||||
#include "../ui/ui_public.h"
|
||||
#include "keys.h"
|
||||
#include "snd_public.h"
|
||||
#include "../cgame/cg_public.h"
|
||||
#include "../game/bg_public.h"
|
||||
|
||||
#define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits
|
||||
|
||||
|
||||
// snapshots are a view of the server at a given time
|
||||
typedef struct {
|
||||
qboolean valid; // cleared if delta parsing was invalid
|
||||
int snapFlags; // rate delayed and dropped commands
|
||||
|
||||
int serverTime; // server time the message is valid for (in msec)
|
||||
|
||||
int messageNum; // copied from netchan->incoming_sequence
|
||||
int deltaNum; // messageNum the delta is from
|
||||
int ping; // time from when cmdNum-1 was sent to time packet was reeceived
|
||||
byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
|
||||
|
||||
int cmdNum; // the next cmdNum the server is expecting
|
||||
playerState_t ps; // complete information about the current player at this time
|
||||
|
||||
int numEntities; // all of the entities that need to be presented
|
||||
int parseEntitiesNum; // at the time of this snapshot
|
||||
|
||||
int serverCommandNum; // execute all commands up to this before
|
||||
// making the snapshot current
|
||||
} clSnapshot_t;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
the clientActive_t structure is wiped completely at every
|
||||
new gamestate_t, potentially several times during an established connection
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int p_cmdNumber; // cl.cmdNumber when packet was sent
|
||||
int p_serverTime; // usercmd->serverTime when packet was sent
|
||||
int p_realtime; // cls.realtime when packet was sent
|
||||
} outPacket_t;
|
||||
|
||||
// the parseEntities array must be large enough to hold PACKET_BACKUP frames of
|
||||
// entities, so that when a delta compressed message arives from the server
|
||||
// it can be un-deltad from the original
|
||||
#define MAX_PARSE_ENTITIES 2048
|
||||
|
||||
extern int g_console_field_width;
|
||||
|
||||
typedef struct {
|
||||
int timeoutcount; // it requres several frames in a timeout condition
|
||||
// to disconnect, preventing debugging breaks from
|
||||
// causing immediate disconnects on continue
|
||||
clSnapshot_t snap; // latest received from server
|
||||
|
||||
int serverTime; // may be paused during play
|
||||
int oldServerTime; // to prevent time from flowing bakcwards
|
||||
int oldFrameServerTime; // to check tournament restarts
|
||||
int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta
|
||||
// this value changes as net lag varies
|
||||
qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate
|
||||
// cleared when CL_AdjustTimeDelta looks at it
|
||||
qboolean newSnapshots; // set on parse of any valid packet
|
||||
|
||||
gameState_t gameState; // configstrings
|
||||
char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO
|
||||
|
||||
int parseEntitiesNum; // index (not anded off) into cl_parse_entities[]
|
||||
|
||||
int mouseDx[2], mouseDy[2]; // added to by mouse events
|
||||
int mouseIndex;
|
||||
int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events
|
||||
|
||||
// cgame communicates a few values to the client system
|
||||
int cgameUserCmdValue; // current weapon to add to usercmd_t
|
||||
float cgameSensitivity;
|
||||
|
||||
// cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last
|
||||
// properly generated command
|
||||
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
|
||||
int cmdNumber; // incremented each frame, because multiple
|
||||
// frames may need to be packed into a single packet
|
||||
|
||||
outPacket_t outPackets[PACKET_BACKUP]; // information about each packet we have sent out
|
||||
|
||||
// the client maintains its own idea of view angles, which are
|
||||
// sent to the server each frame. It is cleared to 0 upon entering each level.
|
||||
// the server sends a delta each frame which is added to the locally
|
||||
// tracked view angles to account for standing on rotating objects,
|
||||
// and teleport direction changes
|
||||
vec3_t viewangles;
|
||||
|
||||
int serverId; // included in each client message so the server
|
||||
// can tell if it is for a prior map_restart
|
||||
// big stuff at end of structure so most offsets are 15 bits or less
|
||||
clSnapshot_t snapshots[PACKET_BACKUP];
|
||||
|
||||
entityState_t entityBaselines[MAX_GENTITIES]; // for delta compression when not in previous frame
|
||||
|
||||
entityState_t parseEntities[MAX_PARSE_ENTITIES];
|
||||
} clientActive_t;
|
||||
|
||||
extern clientActive_t cl;
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
the clientConnection_t structure is wiped when disconnecting from a server,
|
||||
either to go to a full screen console, play a demo, or connect to a different server
|
||||
|
||||
A connection can be to either a server through the network layer or a
|
||||
demo through a file.
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
int clientNum;
|
||||
int lastPacketSentTime; // for retransmits during connection
|
||||
int lastPacketTime; // for timeouts
|
||||
|
||||
netadr_t serverAddress;
|
||||
int connectTime; // for connection retransmits
|
||||
int connectPacketCount; // for display on connection dialog
|
||||
char serverMessage[MAX_STRING_TOKENS]; // for display on connection dialog
|
||||
|
||||
int challenge; // from the server to use for connecting
|
||||
int checksumFeed; // from the server for checksum calculations
|
||||
|
||||
// these are our reliable messages that go to the server
|
||||
int reliableSequence;
|
||||
int reliableAcknowledge; // the last one the server has executed
|
||||
char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
|
||||
|
||||
// server message (unreliable) and command (reliable) sequence
|
||||
// numbers are NOT cleared at level changes, but continue to
|
||||
// increase as long as the connection is valid
|
||||
|
||||
// message sequence is used by both the network layer and the
|
||||
// delta compression layer
|
||||
int serverMessageSequence;
|
||||
|
||||
// reliable messages received from server
|
||||
int serverCommandSequence;
|
||||
int lastExecutedServerCommand; // last server command grabbed or executed with CL_GetServerCommand
|
||||
char serverCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
|
||||
|
||||
// file transfer from server
|
||||
fileHandle_t download;
|
||||
char downloadTempName[MAX_OSPATH];
|
||||
char downloadName[MAX_OSPATH];
|
||||
int downloadNumber;
|
||||
int downloadBlock; // block we are waiting for
|
||||
int downloadCount; // how many bytes we got
|
||||
int downloadSize; // how many bytes we got
|
||||
char downloadList[MAX_INFO_STRING]; // list of paks we need to download
|
||||
qboolean downloadRestart; // if true, we need to do another FS_Restart because we downloaded a pak
|
||||
|
||||
// demo information
|
||||
char demoName[MAX_QPATH];
|
||||
qboolean spDemoRecording;
|
||||
qboolean demorecording;
|
||||
qboolean demoplaying;
|
||||
qboolean demowaiting; // don't record until a non-delta message is received
|
||||
qboolean firstDemoFrameSkipped;
|
||||
fileHandle_t demofile;
|
||||
|
||||
int timeDemoFrames; // counter of rendered frames
|
||||
int timeDemoStart; // cls.realtime before first frame
|
||||
int timeDemoBaseTime; // each frame will be at this time + frameNum * 50
|
||||
|
||||
// big stuff at end of structure so most offsets are 15 bits or less
|
||||
netchan_t netchan;
|
||||
} clientConnection_t;
|
||||
|
||||
extern clientConnection_t clc;
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
the clientStatic_t structure is never wiped, and is used even when
|
||||
no client connection is active at all
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
netadr_t adr;
|
||||
int start;
|
||||
int time;
|
||||
char info[MAX_INFO_STRING];
|
||||
} ping_t;
|
||||
|
||||
typedef struct {
|
||||
netadr_t adr;
|
||||
char hostName[MAX_NAME_LENGTH];
|
||||
char mapName[MAX_NAME_LENGTH];
|
||||
char game[MAX_NAME_LENGTH];
|
||||
int netType;
|
||||
int gameType;
|
||||
int clients;
|
||||
int maxClients;
|
||||
int minPing;
|
||||
int maxPing;
|
||||
int ping;
|
||||
qboolean visible;
|
||||
int punkbuster;
|
||||
} serverInfo_t;
|
||||
|
||||
typedef struct {
|
||||
byte ip[4];
|
||||
unsigned short port;
|
||||
} serverAddress_t;
|
||||
|
||||
typedef struct {
|
||||
connstate_t state; // connection status
|
||||
int keyCatchers; // bit flags
|
||||
|
||||
qboolean cddialog; // bring up the cd needed dialog next frame
|
||||
|
||||
char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect)
|
||||
|
||||
// when the server clears the hunk, all of these must be restarted
|
||||
qboolean rendererStarted;
|
||||
qboolean soundStarted;
|
||||
qboolean soundRegistered;
|
||||
qboolean uiStarted;
|
||||
qboolean cgameStarted;
|
||||
|
||||
int framecount;
|
||||
int frametime; // msec since last frame
|
||||
|
||||
int realtime; // ignores pause
|
||||
int realFrametime; // ignoring pause, so console always works
|
||||
|
||||
int numlocalservers;
|
||||
serverInfo_t localServers[MAX_OTHER_SERVERS];
|
||||
|
||||
int numglobalservers;
|
||||
serverInfo_t globalServers[MAX_GLOBAL_SERVERS];
|
||||
// additional global servers
|
||||
int numGlobalServerAddresses;
|
||||
serverAddress_t globalServerAddresses[MAX_GLOBAL_SERVERS];
|
||||
|
||||
int numfavoriteservers;
|
||||
serverInfo_t favoriteServers[MAX_OTHER_SERVERS];
|
||||
|
||||
int nummplayerservers;
|
||||
serverInfo_t mplayerServers[MAX_OTHER_SERVERS];
|
||||
|
||||
int pingUpdateSource; // source currently pinging or updating
|
||||
|
||||
int masterNum;
|
||||
|
||||
// update server info
|
||||
netadr_t updateServer;
|
||||
char updateChallenge[MAX_TOKEN_CHARS];
|
||||
char updateInfoString[MAX_INFO_STRING];
|
||||
|
||||
netadr_t authorizeServer;
|
||||
|
||||
// rendering info
|
||||
glconfig_t glconfig;
|
||||
qhandle_t charSetShader;
|
||||
qhandle_t whiteShader;
|
||||
qhandle_t consoleShader;
|
||||
} clientStatic_t;
|
||||
|
||||
extern clientStatic_t cls;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
extern vm_t *cgvm; // interface to cgame dll or vm
|
||||
extern vm_t *uivm; // interface to ui dll or vm
|
||||
extern refexport_t re; // interface to refresh .dll
|
||||
|
||||
|
||||
//
|
||||
// cvars
|
||||
//
|
||||
extern cvar_t *cl_nodelta;
|
||||
extern cvar_t *cl_debugMove;
|
||||
extern cvar_t *cl_noprint;
|
||||
extern cvar_t *cl_timegraph;
|
||||
extern cvar_t *cl_maxpackets;
|
||||
extern cvar_t *cl_packetdup;
|
||||
extern cvar_t *cl_shownet;
|
||||
extern cvar_t *cl_showSend;
|
||||
extern cvar_t *cl_timeNudge;
|
||||
extern cvar_t *cl_showTimeDelta;
|
||||
extern cvar_t *cl_freezeDemo;
|
||||
|
||||
extern cvar_t *cl_yawspeed;
|
||||
extern cvar_t *cl_pitchspeed;
|
||||
extern cvar_t *cl_run;
|
||||
extern cvar_t *cl_anglespeedkey;
|
||||
|
||||
extern cvar_t *cl_sensitivity;
|
||||
extern cvar_t *cl_freelook;
|
||||
|
||||
extern cvar_t *cl_mouseAccel;
|
||||
extern cvar_t *cl_showMouseRate;
|
||||
|
||||
extern cvar_t *m_pitch;
|
||||
extern cvar_t *m_yaw;
|
||||
extern cvar_t *m_forward;
|
||||
extern cvar_t *m_side;
|
||||
extern cvar_t *m_filter;
|
||||
|
||||
extern cvar_t *cl_timedemo;
|
||||
|
||||
extern cvar_t *cl_activeAction;
|
||||
|
||||
extern cvar_t *cl_allowDownload;
|
||||
extern cvar_t *cl_conXOffset;
|
||||
extern cvar_t *cl_inGameVideo;
|
||||
|
||||
//=================================================
|
||||
|
||||
//
|
||||
// cl_main
|
||||
//
|
||||
|
||||
void CL_Init (void);
|
||||
void CL_FlushMemory(void);
|
||||
void CL_ShutdownAll(void);
|
||||
void CL_AddReliableCommand( const char *cmd );
|
||||
|
||||
void CL_StartHunkUsers( void );
|
||||
|
||||
void CL_Disconnect_f (void);
|
||||
void CL_GetChallengePacket (void);
|
||||
void CL_Vid_Restart_f( void );
|
||||
void CL_Snd_Restart_f (void);
|
||||
void CL_StartDemoLoop( void );
|
||||
void CL_NextDemo( void );
|
||||
void CL_ReadDemoMessage( void );
|
||||
|
||||
void CL_InitDownloads(void);
|
||||
void CL_NextDownload(void);
|
||||
|
||||
void CL_GetPing( int n, char *buf, int buflen, int *pingtime );
|
||||
void CL_GetPingInfo( int n, char *buf, int buflen );
|
||||
void CL_ClearPing( int n );
|
||||
int CL_GetPingQueueCount( void );
|
||||
|
||||
void CL_ShutdownRef( void );
|
||||
void CL_InitRef( void );
|
||||
qboolean CL_CDKeyValidate( const char *key, const char *checksum );
|
||||
int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen );
|
||||
|
||||
|
||||
//
|
||||
// cl_input
|
||||
//
|
||||
typedef struct {
|
||||
int down[2]; // key nums holding it down
|
||||
unsigned downtime; // msec timestamp
|
||||
unsigned msec; // msec down this frame if both a down and up happened
|
||||
qboolean active; // current state
|
||||
qboolean wasPressed; // set when down, not cleared when up
|
||||
} kbutton_t;
|
||||
|
||||
extern kbutton_t in_mlook, in_klook;
|
||||
extern kbutton_t in_strafe;
|
||||
extern kbutton_t in_speed;
|
||||
|
||||
void CL_InitInput (void);
|
||||
void CL_SendCmd (void);
|
||||
void CL_ClearState (void);
|
||||
void CL_ReadPackets (void);
|
||||
|
||||
void CL_WritePacket( void );
|
||||
void IN_CenterView (void);
|
||||
|
||||
void CL_VerifyCode( void );
|
||||
|
||||
float CL_KeyState (kbutton_t *key);
|
||||
char *Key_KeynumToString (int keynum);
|
||||
|
||||
//
|
||||
// cl_parse.c
|
||||
//
|
||||
extern int cl_connectedToPureServer;
|
||||
|
||||
void CL_SystemInfoChanged( void );
|
||||
void CL_ParseServerMessage( msg_t *msg );
|
||||
|
||||
//====================================================================
|
||||
|
||||
void CL_ServerInfoPacket( netadr_t from, msg_t *msg );
|
||||
void CL_LocalServers_f( void );
|
||||
void CL_GlobalServers_f( void );
|
||||
void CL_FavoriteServers_f( void );
|
||||
void CL_Ping_f( void );
|
||||
qboolean CL_UpdateVisiblePings_f( int source );
|
||||
|
||||
|
||||
//
|
||||
// console
|
||||
//
|
||||
void Con_DrawCharacter (int cx, int line, int num);
|
||||
|
||||
void Con_CheckResize (void);
|
||||
void Con_Init (void);
|
||||
void Con_Clear_f (void);
|
||||
void Con_ToggleConsole_f (void);
|
||||
void Con_DrawNotify (void);
|
||||
void Con_ClearNotify (void);
|
||||
void Con_RunConsole (void);
|
||||
void Con_DrawConsole (void);
|
||||
void Con_PageUp( void );
|
||||
void Con_PageDown( void );
|
||||
void Con_Top( void );
|
||||
void Con_Bottom( void );
|
||||
void Con_Close( void );
|
||||
|
||||
|
||||
//
|
||||
// cl_scrn.c
|
||||
//
|
||||
void SCR_Init (void);
|
||||
void SCR_UpdateScreen (void);
|
||||
|
||||
void SCR_DebugGraph (float value, int color);
|
||||
|
||||
int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates
|
||||
|
||||
void SCR_AdjustFrom640( float *x, float *y, float *w, float *h );
|
||||
void SCR_FillRect( float x, float y, float width, float height,
|
||||
const float *color );
|
||||
void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
|
||||
void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname );
|
||||
|
||||
void SCR_DrawBigString( int x, int y, const char *s, float alpha ); // draws a string with embedded color control characters with fade
|
||||
void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); // ignores embedded color control characters
|
||||
void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor );
|
||||
void SCR_DrawSmallChar( int x, int y, int ch );
|
||||
|
||||
|
||||
//
|
||||
// cl_cin.c
|
||||
//
|
||||
|
||||
void CL_PlayCinematic_f( void );
|
||||
void SCR_DrawCinematic (void);
|
||||
void SCR_RunCinematic (void);
|
||||
void SCR_StopCinematic (void);
|
||||
int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits);
|
||||
e_status CIN_StopCinematic(int handle);
|
||||
e_status CIN_RunCinematic (int handle);
|
||||
void CIN_DrawCinematic (int handle);
|
||||
void CIN_SetExtents (int handle, int x, int y, int w, int h);
|
||||
void CIN_SetLooping (int handle, qboolean loop);
|
||||
void CIN_UploadCinematic(int handle);
|
||||
void CIN_CloseAllVideos(void);
|
||||
|
||||
//
|
||||
// cl_cgame.c
|
||||
//
|
||||
void CL_InitCGame( void );
|
||||
void CL_ShutdownCGame( void );
|
||||
qboolean CL_GameCommand( void );
|
||||
void CL_CGameRendering( stereoFrame_t stereo );
|
||||
void CL_SetCGameTime( void );
|
||||
void CL_FirstSnapshot( void );
|
||||
void CL_ShaderStateChanged(void);
|
||||
|
||||
//
|
||||
// cl_ui.c
|
||||
//
|
||||
void CL_InitUI( void );
|
||||
void CL_ShutdownUI( void );
|
||||
int Key_GetCatcher( void );
|
||||
void Key_SetCatcher( int catcher );
|
||||
void LAN_LoadCachedServers();
|
||||
void LAN_SaveServersToCache();
|
||||
|
||||
|
||||
//
|
||||
// cl_net_chan.c
|
||||
//
|
||||
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data );
|
||||
void CL_Netchan_TransmitNextFragment( netchan_t *chan );
|
||||
qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg );
|
57
code/client/keys.h
Normal file
57
code/client/keys.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../ui/keycodes.h"
|
||||
|
||||
#define MAX_KEYS 256
|
||||
|
||||
typedef struct {
|
||||
qboolean down;
|
||||
int repeats; // if > 1, it is autorepeating
|
||||
char *binding;
|
||||
} qkey_t;
|
||||
|
||||
extern qboolean key_overstrikeMode;
|
||||
extern qkey_t keys[MAX_KEYS];
|
||||
|
||||
// NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h
|
||||
void Field_KeyDownEvent( field_t *edit, int key );
|
||||
void Field_CharEvent( field_t *edit, int ch );
|
||||
void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor );
|
||||
void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor );
|
||||
|
||||
#define COMMAND_HISTORY 32
|
||||
extern field_t historyEditLines[COMMAND_HISTORY];
|
||||
|
||||
extern field_t g_consoleField;
|
||||
extern field_t chatField;
|
||||
extern qboolean anykeydown;
|
||||
extern qboolean chat_team;
|
||||
extern int chat_playerNum;
|
||||
|
||||
void Key_WriteBindings( fileHandle_t f );
|
||||
void Key_SetBinding( int keynum, const char *binding );
|
||||
char *Key_GetBinding( int keynum );
|
||||
qboolean Key_IsDown( int keynum );
|
||||
qboolean Key_GetOverstrikeMode( void );
|
||||
void Key_SetOverstrikeMode( qboolean state );
|
||||
void Key_ClearStates( void );
|
||||
int Key_GetKey(const char *binding);
|
330
code/client/snd_adpcm.c
Normal file
330
code/client/snd_adpcm.c
Normal file
|
@ -0,0 +1,330 @@
|
|||
/***********************************************************
|
||||
Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
|
||||
Netherlands.
|
||||
|
||||
All Rights Reserved
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the names of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
******************************************************************/
|
||||
|
||||
/*
|
||||
** Intel/DVI ADPCM coder/decoder.
|
||||
**
|
||||
** The algorithm for this coder was taken from the IMA Compatability Project
|
||||
** proceedings, Vol 2, Number 2; May 1992.
|
||||
**
|
||||
** Version 1.2, 18-Dec-92.
|
||||
*/
|
||||
|
||||
#include "snd_local.h"
|
||||
|
||||
|
||||
/* Intel ADPCM step variation table */
|
||||
static int indexTable[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
};
|
||||
|
||||
static int stepsizeTable[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
|
||||
void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) {
|
||||
short *inp; /* Input buffer pointer */
|
||||
signed char *outp; /* output buffer pointer */
|
||||
int val; /* Current input sample value */
|
||||
int sign; /* Current adpcm sign bit */
|
||||
int delta; /* Current adpcm output value */
|
||||
int diff; /* Difference between val and sample */
|
||||
int step; /* Stepsize */
|
||||
int valpred; /* Predicted output value */
|
||||
int vpdiff; /* Current change to valpred */
|
||||
int index; /* Current step change index */
|
||||
int outputbuffer; /* place to keep previous 4-bit value */
|
||||
int bufferstep; /* toggle between outputbuffer/output */
|
||||
|
||||
outp = (signed char *)outdata;
|
||||
inp = indata;
|
||||
|
||||
valpred = state->sample;
|
||||
index = state->index;
|
||||
step = stepsizeTable[index];
|
||||
|
||||
outputbuffer = 0; // quiet a compiler warning
|
||||
bufferstep = 1;
|
||||
|
||||
for ( ; len > 0 ; len-- ) {
|
||||
val = *inp++;
|
||||
|
||||
/* Step 1 - compute difference with previous value */
|
||||
diff = val - valpred;
|
||||
sign = (diff < 0) ? 8 : 0;
|
||||
if ( sign ) diff = (-diff);
|
||||
|
||||
/* Step 2 - Divide and clamp */
|
||||
/* Note:
|
||||
** This code *approximately* computes:
|
||||
** delta = diff*4/step;
|
||||
** vpdiff = (delta+0.5)*step/4;
|
||||
** but in shift step bits are dropped. The net result of this is
|
||||
** that even if you have fast mul/div hardware you cannot put it to
|
||||
** good use since the fixup would be too expensive.
|
||||
*/
|
||||
delta = 0;
|
||||
vpdiff = (step >> 3);
|
||||
|
||||
if ( diff >= step ) {
|
||||
delta = 4;
|
||||
diff -= step;
|
||||
vpdiff += step;
|
||||
}
|
||||
step >>= 1;
|
||||
if ( diff >= step ) {
|
||||
delta |= 2;
|
||||
diff -= step;
|
||||
vpdiff += step;
|
||||
}
|
||||
step >>= 1;
|
||||
if ( diff >= step ) {
|
||||
delta |= 1;
|
||||
vpdiff += step;
|
||||
}
|
||||
|
||||
/* Step 3 - Update previous value */
|
||||
if ( sign )
|
||||
valpred -= vpdiff;
|
||||
else
|
||||
valpred += vpdiff;
|
||||
|
||||
/* Step 4 - Clamp previous value to 16 bits */
|
||||
if ( valpred > 32767 )
|
||||
valpred = 32767;
|
||||
else if ( valpred < -32768 )
|
||||
valpred = -32768;
|
||||
|
||||
/* Step 5 - Assemble value, update index and step values */
|
||||
delta |= sign;
|
||||
|
||||
index += indexTable[delta];
|
||||
if ( index < 0 ) index = 0;
|
||||
if ( index > 88 ) index = 88;
|
||||
step = stepsizeTable[index];
|
||||
|
||||
/* Step 6 - Output value */
|
||||
if ( bufferstep ) {
|
||||
outputbuffer = (delta << 4) & 0xf0;
|
||||
} else {
|
||||
*outp++ = (delta & 0x0f) | outputbuffer;
|
||||
}
|
||||
bufferstep = !bufferstep;
|
||||
}
|
||||
|
||||
/* Output last step, if needed */
|
||||
if ( !bufferstep )
|
||||
*outp++ = outputbuffer;
|
||||
|
||||
state->sample = valpred;
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
|
||||
/* static */ void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) {
|
||||
signed char *inp; /* Input buffer pointer */
|
||||
int outp; /* output buffer pointer */
|
||||
int sign; /* Current adpcm sign bit */
|
||||
int delta; /* Current adpcm output value */
|
||||
int step; /* Stepsize */
|
||||
int valpred; /* Predicted value */
|
||||
int vpdiff; /* Current change to valpred */
|
||||
int index; /* Current step change index */
|
||||
int inputbuffer; /* place to keep next 4-bit value */
|
||||
int bufferstep; /* toggle between inputbuffer/input */
|
||||
|
||||
outp = 0;
|
||||
inp = (signed char *)indata;
|
||||
|
||||
valpred = state->sample;
|
||||
index = state->index;
|
||||
step = stepsizeTable[index];
|
||||
|
||||
bufferstep = 0;
|
||||
inputbuffer = 0; // quiet a compiler warning
|
||||
for ( ; len > 0 ; len-- ) {
|
||||
|
||||
/* Step 1 - get the delta value */
|
||||
if ( bufferstep ) {
|
||||
delta = inputbuffer & 0xf;
|
||||
} else {
|
||||
inputbuffer = *inp++;
|
||||
delta = (inputbuffer >> 4) & 0xf;
|
||||
}
|
||||
bufferstep = !bufferstep;
|
||||
|
||||
/* Step 2 - Find new index value (for later) */
|
||||
index += indexTable[delta];
|
||||
if ( index < 0 ) index = 0;
|
||||
if ( index > 88 ) index = 88;
|
||||
|
||||
/* Step 3 - Separate sign and magnitude */
|
||||
sign = delta & 8;
|
||||
delta = delta & 7;
|
||||
|
||||
/* Step 4 - Compute difference and new predicted value */
|
||||
/*
|
||||
** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
|
||||
** in adpcm_coder.
|
||||
*/
|
||||
vpdiff = step >> 3;
|
||||
if ( delta & 4 ) vpdiff += step;
|
||||
if ( delta & 2 ) vpdiff += step>>1;
|
||||
if ( delta & 1 ) vpdiff += step>>2;
|
||||
|
||||
if ( sign )
|
||||
valpred -= vpdiff;
|
||||
else
|
||||
valpred += vpdiff;
|
||||
|
||||
/* Step 5 - clamp output value */
|
||||
if ( valpred > 32767 )
|
||||
valpred = 32767;
|
||||
else if ( valpred < -32768 )
|
||||
valpred = -32768;
|
||||
|
||||
/* Step 6 - Update step value */
|
||||
step = stepsizeTable[index];
|
||||
|
||||
/* Step 7 - Output value */
|
||||
outdata[outp] = valpred;
|
||||
outp++;
|
||||
}
|
||||
|
||||
state->sample = valpred;
|
||||
state->index = index;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
S_AdpcmMemoryNeeded
|
||||
|
||||
Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format
|
||||
====================
|
||||
*/
|
||||
int S_AdpcmMemoryNeeded( const wavinfo_t *info ) {
|
||||
float scale;
|
||||
int scaledSampleCount;
|
||||
int sampleMemory;
|
||||
int blockCount;
|
||||
int headerMemory;
|
||||
|
||||
// determine scale to convert from input sampling rate to desired sampling rate
|
||||
scale = (float)info->rate / dma.speed;
|
||||
|
||||
// calc number of samples at playback sampling rate
|
||||
scaledSampleCount = info->samples / scale;
|
||||
|
||||
// calc memory need to store those samples using ADPCM at 4 bits per sample
|
||||
sampleMemory = scaledSampleCount / 2;
|
||||
|
||||
// calc number of sample blocks needed of PAINTBUFFER_SIZE
|
||||
blockCount = scaledSampleCount / PAINTBUFFER_SIZE;
|
||||
if( scaledSampleCount % PAINTBUFFER_SIZE ) {
|
||||
blockCount++;
|
||||
}
|
||||
|
||||
// calc memory needed to store the block headers
|
||||
headerMemory = blockCount * sizeof(adpcm_state_t);
|
||||
|
||||
return sampleMemory + headerMemory;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
S_AdpcmGetSamples
|
||||
====================
|
||||
*/
|
||||
void S_AdpcmGetSamples(sndBuffer *chunk, short *to) {
|
||||
adpcm_state_t state;
|
||||
byte *out;
|
||||
|
||||
// get the starting state from the block header
|
||||
state.index = chunk->adpcm.index;
|
||||
state.sample = chunk->adpcm.sample;
|
||||
|
||||
out = (byte *)chunk->sndChunk;
|
||||
// get samples
|
||||
S_AdpcmDecode( out, to, SND_CHUNK_SIZE_BYTE*2, &state );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
S_AdpcmEncodeSound
|
||||
====================
|
||||
*/
|
||||
void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
|
||||
adpcm_state_t state;
|
||||
int inOffset;
|
||||
int count;
|
||||
int n;
|
||||
sndBuffer *newchunk, *chunk;
|
||||
byte *out;
|
||||
|
||||
inOffset = 0;
|
||||
count = sfx->soundLength;
|
||||
state.index = 0;
|
||||
state.sample = samples[0];
|
||||
|
||||
chunk = NULL;
|
||||
while( count ) {
|
||||
n = count;
|
||||
if( n > SND_CHUNK_SIZE_BYTE*2 ) {
|
||||
n = SND_CHUNK_SIZE_BYTE*2;
|
||||
}
|
||||
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
|
||||
// output the header
|
||||
chunk->adpcm.index = state.index;
|
||||
chunk->adpcm.sample = state.sample;
|
||||
|
||||
out = (byte *)chunk->sndChunk;
|
||||
|
||||
// encode the samples
|
||||
S_AdpcmEncode( samples + inOffset, out, n, &state );
|
||||
|
||||
inOffset += n;
|
||||
count -= n;
|
||||
}
|
||||
}
|
1636
code/client/snd_dma.c
Normal file
1636
code/client/snd_dma.c
Normal file
File diff suppressed because it is too large
Load diff
204
code/client/snd_local.h
Normal file
204
code/client/snd_local.h
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// snd_local.h -- private sound definations
|
||||
|
||||
|
||||
#include "../game/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "snd_public.h"
|
||||
|
||||
#define PAINTBUFFER_SIZE 4096 // this is in samples
|
||||
|
||||
#define SND_CHUNK_SIZE 1024 // samples
|
||||
#define SND_CHUNK_SIZE_FLOAT (SND_CHUNK_SIZE/2) // floats
|
||||
#define SND_CHUNK_SIZE_BYTE (SND_CHUNK_SIZE*2) // floats
|
||||
|
||||
typedef struct {
|
||||
int left; // the final values will be clamped to +/- 0x00ffff00 and shifted down
|
||||
int right;
|
||||
} portable_samplepair_t;
|
||||
|
||||
typedef struct adpcm_state {
|
||||
short sample; /* Previous output value */
|
||||
char index; /* Index into stepsize table */
|
||||
} adpcm_state_t;
|
||||
|
||||
typedef struct sndBuffer_s {
|
||||
short sndChunk[SND_CHUNK_SIZE];
|
||||
struct sndBuffer_s *next;
|
||||
int size;
|
||||
adpcm_state_t adpcm;
|
||||
} sndBuffer;
|
||||
|
||||
typedef struct sfx_s {
|
||||
sndBuffer *soundData;
|
||||
qboolean defaultSound; // couldn't be loaded, so use buzz
|
||||
qboolean inMemory; // not in Memory
|
||||
qboolean soundCompressed; // not in Memory
|
||||
int soundCompressionMethod;
|
||||
int soundLength;
|
||||
char soundName[MAX_QPATH];
|
||||
int lastTimeUsed;
|
||||
struct sfx_s *next;
|
||||
} sfx_t;
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int samples; // mono samples in buffer
|
||||
int submission_chunk; // don't mix less than this #
|
||||
int samplebits;
|
||||
int speed;
|
||||
byte *buffer;
|
||||
} dma_t;
|
||||
|
||||
#define START_SAMPLE_IMMEDIATE 0x7fffffff
|
||||
|
||||
typedef struct loopSound_s {
|
||||
vec3_t origin;
|
||||
vec3_t velocity;
|
||||
sfx_t *sfx;
|
||||
int mergeFrame;
|
||||
qboolean active;
|
||||
qboolean kill;
|
||||
qboolean doppler;
|
||||
float dopplerScale;
|
||||
float oldDopplerScale;
|
||||
int framenum;
|
||||
} loopSound_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;
|
||||
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;
|
||||
} channel_t;
|
||||
|
||||
|
||||
#define WAV_FORMAT_PCM 1
|
||||
|
||||
|
||||
typedef struct {
|
||||
int format;
|
||||
int rate;
|
||||
int width;
|
||||
int channels;
|
||||
int samples;
|
||||
int dataofs; // chunk starts this many bytes from file start
|
||||
} wavinfo_t;
|
||||
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
SYSTEM SPECIFIC FUNCTIONS
|
||||
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
// initializes cycling through a DMA buffer and returns information on it
|
||||
qboolean SNDDMA_Init(void);
|
||||
|
||||
// gets the current DMA position
|
||||
int SNDDMA_GetDMAPos(void);
|
||||
|
||||
// shutdown the DMA xfer.
|
||||
void SNDDMA_Shutdown(void);
|
||||
|
||||
void SNDDMA_BeginPainting (void);
|
||||
|
||||
void SNDDMA_Submit(void);
|
||||
|
||||
//====================================================================
|
||||
|
||||
#define MAX_CHANNELS 96
|
||||
|
||||
extern channel_t s_channels[MAX_CHANNELS];
|
||||
extern channel_t loop_channels[MAX_CHANNELS];
|
||||
extern int numLoopChannels;
|
||||
|
||||
extern int s_paintedtime;
|
||||
extern int s_rawend;
|
||||
extern vec3_t listener_forward;
|
||||
extern vec3_t listener_right;
|
||||
extern vec3_t listener_up;
|
||||
extern dma_t dma;
|
||||
|
||||
#define MAX_RAW_SAMPLES 16384
|
||||
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
|
||||
|
||||
extern cvar_t *s_volume;
|
||||
extern cvar_t *s_nosound;
|
||||
extern cvar_t *s_khz;
|
||||
extern cvar_t *s_show;
|
||||
extern cvar_t *s_mixahead;
|
||||
|
||||
extern cvar_t *s_testsound;
|
||||
extern cvar_t *s_separation;
|
||||
|
||||
qboolean S_LoadSound( sfx_t *sfx );
|
||||
|
||||
void SND_free(sndBuffer *v);
|
||||
sndBuffer* SND_malloc();
|
||||
void SND_setup();
|
||||
|
||||
void S_PaintChannels(int endtime);
|
||||
|
||||
void S_memoryLoad(sfx_t *sfx);
|
||||
portable_samplepair_t *S_GetRawSamplePointer();
|
||||
|
||||
// spatializes a channel
|
||||
void S_Spatialize(channel_t *ch);
|
||||
|
||||
// adpcm functions
|
||||
int S_AdpcmMemoryNeeded( const wavinfo_t *info );
|
||||
void S_AdpcmEncodeSound( sfx_t *sfx, short *samples );
|
||||
void S_AdpcmGetSamples(sndBuffer *chunk, short *to);
|
||||
|
||||
// wavelet function
|
||||
|
||||
#define SENTINEL_MULAW_ZERO_RUN 127
|
||||
#define SENTINEL_MULAW_FOUR_BIT_RUN 126
|
||||
|
||||
void S_FreeOldestSound();
|
||||
|
||||
#define NXStream byte
|
||||
|
||||
void encodeWavelet(sfx_t *sfx, short *packets);
|
||||
void decodeWavelet( sndBuffer *stream, short *packets);
|
||||
|
||||
void encodeMuLaw( sfx_t *sfx, short *packets);
|
||||
extern short mulawToShort[256];
|
||||
|
||||
extern short *sfxScratchBuffer;
|
||||
extern sfx_t *sfxScratchPointer;
|
||||
extern int sfxScratchIndex;
|
||||
|
404
code/client/snd_mem.c
Normal file
404
code/client/snd_mem.c
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* name: snd_mem.c
|
||||
*
|
||||
* desc: sound caching
|
||||
*
|
||||
* $Archive: /MissionPack/code/client/snd_mem.c $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#include "snd_local.h"
|
||||
|
||||
#define DEF_COMSOUNDMEGS "8"
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
memory management
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static sndBuffer *buffer = NULL;
|
||||
static sndBuffer *freelist = NULL;
|
||||
static int inUse = 0;
|
||||
static int totalInUse = 0;
|
||||
|
||||
short *sfxScratchBuffer = NULL;
|
||||
sfx_t *sfxScratchPointer = NULL;
|
||||
int sfxScratchIndex = 0;
|
||||
|
||||
void SND_free(sndBuffer *v) {
|
||||
*(sndBuffer **)v = freelist;
|
||||
freelist = (sndBuffer*)v;
|
||||
inUse += sizeof(sndBuffer);
|
||||
}
|
||||
|
||||
sndBuffer* SND_malloc() {
|
||||
sndBuffer *v;
|
||||
redo:
|
||||
if (freelist == NULL) {
|
||||
S_FreeOldestSound();
|
||||
goto redo;
|
||||
}
|
||||
|
||||
inUse -= sizeof(sndBuffer);
|
||||
totalInUse += sizeof(sndBuffer);
|
||||
|
||||
v = freelist;
|
||||
freelist = *(sndBuffer **)freelist;
|
||||
v->next = NULL;
|
||||
return v;
|
||||
}
|
||||
|
||||
void SND_setup() {
|
||||
sndBuffer *p, *q;
|
||||
cvar_t *cv;
|
||||
int scs;
|
||||
|
||||
cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
|
||||
|
||||
scs = (cv->integer*1536);
|
||||
|
||||
buffer = malloc(scs*sizeof(sndBuffer) );
|
||||
// allocate the stack based hunk allocator
|
||||
sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
|
||||
sfxScratchPointer = NULL;
|
||||
|
||||
inUse = scs*sizeof(sndBuffer);
|
||||
p = buffer;;
|
||||
q = p + scs;
|
||||
while (--q > p)
|
||||
*(sndBuffer **)q = q-1;
|
||||
|
||||
*(sndBuffer **)q = NULL;
|
||||
freelist = p + scs - 1;
|
||||
|
||||
Com_Printf("Sound memory manager started\n");
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
WAV loading
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static byte *data_p;
|
||||
static byte *iff_end;
|
||||
static byte *last_chunk;
|
||||
static byte *iff_data;
|
||||
static int iff_chunk_len;
|
||||
|
||||
static short GetLittleShort(void)
|
||||
{
|
||||
short val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p+1)<<8);
|
||||
data_p += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int GetLittleLong(void)
|
||||
{
|
||||
int val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p+1)<<8);
|
||||
val = val + (*(data_p+2)<<16);
|
||||
val = val + (*(data_p+3)<<24);
|
||||
data_p += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void FindNextChunk(char *name)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
data_p=last_chunk;
|
||||
|
||||
if (data_p >= iff_end)
|
||||
{ // didn't find the chunk
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
iff_chunk_len = GetLittleLong();
|
||||
if (iff_chunk_len < 0)
|
||||
{
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
data_p -= 8;
|
||||
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
|
||||
if (!strncmp((char *)data_p, name, 4))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void FindChunk(char *name)
|
||||
{
|
||||
last_chunk = iff_data;
|
||||
FindNextChunk (name);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
GetWavinfo
|
||||
============
|
||||
*/
|
||||
static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
|
||||
{
|
||||
wavinfo_t info;
|
||||
|
||||
Com_Memset (&info, 0, sizeof(info));
|
||||
|
||||
if (!wav)
|
||||
return info;
|
||||
|
||||
iff_data = wav;
|
||||
iff_end = wav + wavlength;
|
||||
|
||||
// find "RIFF" chunk
|
||||
FindChunk("RIFF");
|
||||
if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
|
||||
{
|
||||
Com_Printf("Missing RIFF/WAVE chunks\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
// get "fmt " chunk
|
||||
iff_data = data_p + 12;
|
||||
// DumpChunks ();
|
||||
|
||||
FindChunk("fmt ");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing fmt chunk\n");
|
||||
return info;
|
||||
}
|
||||
data_p += 8;
|
||||
info.format = GetLittleShort();
|
||||
info.channels = GetLittleShort();
|
||||
info.rate = GetLittleLong();
|
||||
data_p += 4+2;
|
||||
info.width = GetLittleShort() / 8;
|
||||
|
||||
if (info.format != 1)
|
||||
{
|
||||
Com_Printf("Microsoft PCM format only\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
// find data chunk
|
||||
FindChunk("data");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing data chunk\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
info.samples = GetLittleLong () / info.width;
|
||||
info.dataofs = data_p - wav;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
ResampleSfx
|
||||
|
||||
resample / decimate to the current source rate
|
||||
================
|
||||
*/
|
||||
static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
|
||||
int outcount;
|
||||
int srcsample;
|
||||
float stepscale;
|
||||
int i;
|
||||
int sample, samplefrac, fracstep;
|
||||
int part;
|
||||
sndBuffer *chunk;
|
||||
|
||||
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
|
||||
|
||||
outcount = sfx->soundLength / stepscale;
|
||||
sfx->soundLength = outcount;
|
||||
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256;
|
||||
chunk = sfx->soundData;
|
||||
|
||||
for (i=0 ; i<outcount ; i++)
|
||||
{
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
if( inwidth == 2 ) {
|
||||
sample = LittleShort ( ((short *)data)[srcsample] );
|
||||
} else {
|
||||
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
|
||||
}
|
||||
part = (i&(SND_CHUNK_SIZE-1));
|
||||
if (part == 0) {
|
||||
sndBuffer *newchunk;
|
||||
newchunk = SND_malloc();
|
||||
if (chunk == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
}
|
||||
|
||||
chunk->sndChunk[part] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ResampleSfx
|
||||
|
||||
resample / decimate to the current source rate
|
||||
================
|
||||
*/
|
||||
static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
|
||||
int outcount;
|
||||
int srcsample;
|
||||
float stepscale;
|
||||
int i;
|
||||
int sample, samplefrac, fracstep;
|
||||
|
||||
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
|
||||
|
||||
outcount = samples / stepscale;
|
||||
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256;
|
||||
|
||||
for (i=0 ; i<outcount ; i++)
|
||||
{
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
if( inwidth == 2 ) {
|
||||
sample = LittleShort ( ((short *)data)[srcsample] );
|
||||
} else {
|
||||
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
|
||||
}
|
||||
sfx[i] = sample;
|
||||
}
|
||||
return outcount;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
S_LoadSound
|
||||
|
||||
The filename may be different than sfx->name in the case
|
||||
of a forced fallback of a player specific sound
|
||||
==============
|
||||
*/
|
||||
qboolean S_LoadSound( sfx_t *sfx )
|
||||
{
|
||||
byte *data;
|
||||
short *samples;
|
||||
wavinfo_t info;
|
||||
int size;
|
||||
|
||||
// player specific sounds are never directly loaded
|
||||
if ( sfx->soundName[0] == '*') {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
// load it in
|
||||
size = FS_ReadFile( sfx->soundName, (void **)&data );
|
||||
if ( !data ) {
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
info = GetWavinfo( sfx->soundName, data, size );
|
||||
if ( info.channels != 1 ) {
|
||||
Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
|
||||
FS_FreeFile (data);
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( info.width == 1 ) {
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
|
||||
}
|
||||
|
||||
if ( info.rate != 22050 ) {
|
||||
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
|
||||
}
|
||||
|
||||
samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
|
||||
|
||||
sfx->lastTimeUsed = Com_Milliseconds()+1;
|
||||
|
||||
// each of these compression schemes works just fine
|
||||
// but the 16bit quality is much nicer and with a local
|
||||
// install assured we can rely upon the sound memory
|
||||
// manager to do the right thing for us and page
|
||||
// sound in as needed
|
||||
|
||||
if( sfx->soundCompressed == qtrue) {
|
||||
sfx->soundCompressionMethod = 1;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
S_AdpcmEncodeSound(sfx, samples);
|
||||
#if 0
|
||||
} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
|
||||
sfx->soundCompressionMethod = 3;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
encodeMuLaw( sfx, samples);
|
||||
} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
|
||||
sfx->soundCompressionMethod = 2;
|
||||
sfx->soundData = NULL;
|
||||
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
|
||||
encodeWavelet( sfx, samples);
|
||||
#endif
|
||||
} else {
|
||||
sfx->soundCompressionMethod = 0;
|
||||
sfx->soundLength = info.samples;
|
||||
sfx->soundData = NULL;
|
||||
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
|
||||
}
|
||||
|
||||
Hunk_FreeTempMemory(samples);
|
||||
FS_FreeFile( data );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
void S_DisplayFreeMemory() {
|
||||
Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
|
||||
}
|
681
code/client/snd_mix.c
Normal file
681
code/client/snd_mix.c
Normal file
|
@ -0,0 +1,681 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// snd_mix.c -- portable code to mix sounds for snd_dma.c
|
||||
|
||||
#include "snd_local.h"
|
||||
|
||||
static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
|
||||
static int snd_vol;
|
||||
|
||||
// bk001119 - these not static, required by unix/snd_mixa.s
|
||||
int* snd_p;
|
||||
int snd_linear_count;
|
||||
short* snd_out;
|
||||
|
||||
#if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123
|
||||
#if !id386
|
||||
|
||||
void S_WriteLinearBlastStereo16 (void)
|
||||
{
|
||||
int i;
|
||||
int val;
|
||||
|
||||
for (i=0 ; i<snd_linear_count ; i+=2)
|
||||
{
|
||||
val = snd_p[i]>>8;
|
||||
if (val > 0x7fff)
|
||||
snd_out[i] = 0x7fff;
|
||||
else if (val < -32768)
|
||||
snd_out[i] = -32768;
|
||||
else
|
||||
snd_out[i] = val;
|
||||
|
||||
val = snd_p[i+1]>>8;
|
||||
if (val > 0x7fff)
|
||||
snd_out[i+1] = 0x7fff;
|
||||
else if (val < -32768)
|
||||
snd_out[i+1] = -32768;
|
||||
else
|
||||
snd_out[i+1] = val;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
|
||||
{
|
||||
__asm {
|
||||
|
||||
push edi
|
||||
push ebx
|
||||
mov ecx,ds:dword ptr[snd_linear_count]
|
||||
mov ebx,ds:dword ptr[snd_p]
|
||||
mov edi,ds:dword ptr[snd_out]
|
||||
LWLBLoopTop:
|
||||
mov eax,ds:dword ptr[-8+ebx+ecx*4]
|
||||
sar eax,8
|
||||
cmp eax,07FFFh
|
||||
jg LClampHigh
|
||||
cmp eax,0FFFF8000h
|
||||
jnl LClampDone
|
||||
mov eax,0FFFF8000h
|
||||
jmp LClampDone
|
||||
LClampHigh:
|
||||
mov eax,07FFFh
|
||||
LClampDone:
|
||||
mov edx,ds:dword ptr[-4+ebx+ecx*4]
|
||||
sar edx,8
|
||||
cmp edx,07FFFh
|
||||
jg LClampHigh2
|
||||
cmp edx,0FFFF8000h
|
||||
jnl LClampDone2
|
||||
mov edx,0FFFF8000h
|
||||
jmp LClampDone2
|
||||
LClampHigh2:
|
||||
mov edx,07FFFh
|
||||
LClampDone2:
|
||||
shl edx,16
|
||||
and eax,0FFFFh
|
||||
or edx,eax
|
||||
mov ds:dword ptr[-4+edi+ecx*2],edx
|
||||
sub ecx,2
|
||||
jnz LWLBLoopTop
|
||||
pop ebx
|
||||
pop edi
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
// forward declare, implementation somewhere else
|
||||
void S_WriteLinearBlastStereo16 (void);
|
||||
#endif
|
||||
|
||||
void S_TransferStereo16 (unsigned long *pbuf, int endtime)
|
||||
{
|
||||
int lpos;
|
||||
int ls_paintedtime;
|
||||
|
||||
snd_p = (int *) 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
|
||||
S_WriteLinearBlastStereo16 ();
|
||||
|
||||
snd_p += snd_linear_count;
|
||||
ls_paintedtime += (snd_linear_count>>1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
S_TransferPaintBuffer
|
||||
|
||||
===================
|
||||
*/
|
||||
void S_TransferPaintBuffer(int endtime)
|
||||
{
|
||||
int out_idx;
|
||||
int count;
|
||||
int out_mask;
|
||||
int *p;
|
||||
int step;
|
||||
int val;
|
||||
unsigned long *pbuf;
|
||||
|
||||
pbuf = (unsigned long *)dma.buffer;
|
||||
|
||||
|
||||
if ( s_testsound->integer ) {
|
||||
int i;
|
||||
int count;
|
||||
|
||||
// write a fixed sine wave
|
||||
count = (endtime - s_paintedtime);
|
||||
for (i=0 ; i<count ; i++)
|
||||
paintbuffer[i].left = paintbuffer[i].right = sin((s_paintedtime+i)*0.1)*20000*256;
|
||||
}
|
||||
|
||||
|
||||
if (dma.samplebits == 16 && dma.channels == 2)
|
||||
{ // optimized case
|
||||
S_TransferStereo16 (pbuf, endtime);
|
||||
}
|
||||
else
|
||||
{ // general case
|
||||
p = (int *) paintbuffer;
|
||||
count = (endtime - s_paintedtime) * dma.channels;
|
||||
out_mask = dma.samples - 1;
|
||||
out_idx = s_paintedtime * dma.channels & out_mask;
|
||||
step = 3 - dma.channels;
|
||||
|
||||
if (dma.samplebits == 16)
|
||||
{
|
||||
short *out = (short *) pbuf;
|
||||
while (count--)
|
||||
{
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < -32768)
|
||||
val = -32768;
|
||||
out[out_idx] = val;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
else if (dma.samplebits == 8)
|
||||
{
|
||||
unsigned char *out = (unsigned char *) pbuf;
|
||||
while (count--)
|
||||
{
|
||||
val = *p >> 8;
|
||||
p+= step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < -32768)
|
||||
val = -32768;
|
||||
out[out_idx] = (val>>8) + 128;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CHANNEL MIXING
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data, aoff, boff;
|
||||
int leftvol, rightvol;
|
||||
int i, j;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
short *samples;
|
||||
float ooff, fdata, fdiv, fleftvol, frightvol;
|
||||
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
|
||||
if (ch->doppler) {
|
||||
sampleOffset = sampleOffset*ch->oldDopplerScale;
|
||||
}
|
||||
|
||||
chunk = sc->soundData;
|
||||
while (sampleOffset>=SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
sampleOffset -= SND_CHUNK_SIZE;
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ch->doppler || ch->dopplerScale==1.0f) {
|
||||
#if idppc_altivec
|
||||
vector signed short volume_vec;
|
||||
vector unsigned int volume_shift;
|
||||
int vectorCount, samplesLeft, chunkSamplesLeft;
|
||||
#endif
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
samples = chunk->sndChunk;
|
||||
#if idppc_altivec
|
||||
((short *)&volume_vec)[0] = leftvol;
|
||||
((short *)&volume_vec)[1] = leftvol;
|
||||
((short *)&volume_vec)[4] = leftvol;
|
||||
((short *)&volume_vec)[5] = leftvol;
|
||||
((short *)&volume_vec)[2] = rightvol;
|
||||
((short *)&volume_vec)[3] = rightvol;
|
||||
((short *)&volume_vec)[6] = rightvol;
|
||||
((short *)&volume_vec)[7] = rightvol;
|
||||
volume_shift = vec_splat_u32(8);
|
||||
i = 0;
|
||||
|
||||
while(i < count) {
|
||||
/* Try to align destination to 16-byte boundary */
|
||||
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
/* Destination is now aligned. Process as many 8-sample
|
||||
chunks as we can before we run out of room from the current
|
||||
sound chunk. We do 8 per loop to avoid extra source data reads. */
|
||||
samplesLeft = count - i;
|
||||
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
|
||||
if(samplesLeft > chunkSamplesLeft)
|
||||
samplesLeft = chunkSamplesLeft;
|
||||
|
||||
vectorCount = samplesLeft / 8;
|
||||
|
||||
if(vectorCount)
|
||||
{
|
||||
vector unsigned char tmp;
|
||||
vector short s0, s1, sampleData0, sampleData1;
|
||||
vector short samples0, samples1;
|
||||
vector signed int left0, right0;
|
||||
vector signed int merge0, merge1;
|
||||
vector signed int d0, d1, d2, d3;
|
||||
vector unsigned char samplePermute0 =
|
||||
(vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
|
||||
vector unsigned char samplePermute1 =
|
||||
(vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
|
||||
vector unsigned char loadPermute0, loadPermute1;
|
||||
|
||||
// Rather than permute the vectors after we load them to do the sample
|
||||
// replication and rearrangement, we permute the alignment vector so
|
||||
// we do everything in one step below and avoid data shuffling.
|
||||
tmp = vec_lvsl(0,&samples[sampleOffset]);
|
||||
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
|
||||
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
|
||||
|
||||
s0 = *(vector short *)&samples[sampleOffset];
|
||||
while(vectorCount)
|
||||
{
|
||||
/* Load up source (16-bit) sample data */
|
||||
s1 = *(vector short *)&samples[sampleOffset+7];
|
||||
|
||||
/* Load up destination sample data */
|
||||
d0 = *(vector signed int *)&samp[i];
|
||||
d1 = *(vector signed int *)&samp[i+2];
|
||||
d2 = *(vector signed int *)&samp[i+4];
|
||||
d3 = *(vector signed int *)&samp[i+6];
|
||||
|
||||
sampleData0 = vec_perm(s0,s1,loadPermute0);
|
||||
sampleData1 = vec_perm(s0,s1,loadPermute1);
|
||||
|
||||
merge0 = vec_mule(sampleData0,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData0,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d0 = vec_add(merge0,d0);
|
||||
d1 = vec_add(merge1,d1);
|
||||
|
||||
merge0 = vec_mule(sampleData1,volume_vec);
|
||||
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
|
||||
|
||||
merge1 = vec_mulo(sampleData1,volume_vec);
|
||||
merge1 = vec_sra(merge1,volume_shift);
|
||||
|
||||
d2 = vec_add(merge0,d2);
|
||||
d3 = vec_add(merge1,d3);
|
||||
|
||||
/* Store destination sample data */
|
||||
*(vector signed int *)&samp[i] = d0;
|
||||
*(vector signed int *)&samp[i+2] = d1;
|
||||
*(vector signed int *)&samp[i+4] = d2;
|
||||
*(vector signed int *)&samp[i+6] = d3;
|
||||
|
||||
i += 8;
|
||||
vectorCount--;
|
||||
s0 = s1;
|
||||
sampleOffset += 8;
|
||||
}
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
samples = chunk->sndChunk;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
fleftvol = ch->leftvol*snd_vol;
|
||||
frightvol = ch->rightvol*snd_vol;
|
||||
|
||||
ooff = sampleOffset;
|
||||
samples = chunk->sndChunk;
|
||||
|
||||
|
||||
|
||||
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
|
||||
aoff = ooff;
|
||||
ooff = ooff + ch->dopplerScale;
|
||||
boff = ooff;
|
||||
fdata = 0;
|
||||
for (j=aoff; j<boff; j++) {
|
||||
if (j == SND_CHUNK_SIZE) {
|
||||
chunk = chunk->next;
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
samples = chunk->sndChunk;
|
||||
ooff -= SND_CHUNK_SIZE;
|
||||
}
|
||||
fdata += samples[j&(SND_CHUNK_SIZE-1)];
|
||||
}
|
||||
fdiv = 256 * (boff-aoff);
|
||||
samp[i].left += (fdata * fleftvol)/fdiv;
|
||||
samp[i].right += (fdata * frightvol)/fdiv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data;
|
||||
int leftvol, rightvol;
|
||||
int i;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
short *samples;
|
||||
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
|
||||
i = 0;
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
chunk = sc->soundData;
|
||||
while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) {
|
||||
chunk = chunk->next;
|
||||
sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
|
||||
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
|
||||
sfxScratchIndex = i;
|
||||
sfxScratchPointer = sc;
|
||||
}
|
||||
|
||||
samples = sfxScratchBuffer;
|
||||
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE*2) {
|
||||
chunk = chunk->next;
|
||||
decodeWavelet(chunk, sfxScratchBuffer);
|
||||
sfxScratchIndex++;
|
||||
sampleOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data;
|
||||
int leftvol, rightvol;
|
||||
int i;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
short *samples;
|
||||
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
|
||||
i = 0;
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
chunk = sc->soundData;
|
||||
|
||||
if (ch->doppler) {
|
||||
sampleOffset = sampleOffset*ch->oldDopplerScale;
|
||||
}
|
||||
|
||||
while (sampleOffset>=(SND_CHUNK_SIZE*4)) {
|
||||
chunk = chunk->next;
|
||||
sampleOffset -= (SND_CHUNK_SIZE*4);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
|
||||
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
|
||||
sfxScratchIndex = i;
|
||||
sfxScratchPointer = sc;
|
||||
}
|
||||
|
||||
samples = sfxScratchBuffer;
|
||||
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = samples[sampleOffset++];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
|
||||
if (sampleOffset == SND_CHUNK_SIZE*4) {
|
||||
chunk = chunk->next;
|
||||
S_AdpcmGetSamples( chunk, sfxScratchBuffer);
|
||||
sampleOffset = 0;
|
||||
sfxScratchIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
|
||||
int data;
|
||||
int leftvol, rightvol;
|
||||
int i;
|
||||
portable_samplepair_t *samp;
|
||||
sndBuffer *chunk;
|
||||
byte *samples;
|
||||
float ooff;
|
||||
|
||||
leftvol = ch->leftvol*snd_vol;
|
||||
rightvol = ch->rightvol*snd_vol;
|
||||
|
||||
samp = &paintbuffer[ bufferOffset ];
|
||||
chunk = sc->soundData;
|
||||
while (sampleOffset>=(SND_CHUNK_SIZE*2)) {
|
||||
chunk = chunk->next;
|
||||
sampleOffset -= (SND_CHUNK_SIZE*2);
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ch->doppler) {
|
||||
samples = (byte *)chunk->sndChunk + sampleOffset;
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = mulawToShort[*samples];
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
samples++;
|
||||
if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
|
||||
chunk = chunk->next;
|
||||
samples = (byte *)chunk->sndChunk;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ooff = sampleOffset;
|
||||
samples = (byte *)chunk->sndChunk;
|
||||
for ( i=0 ; i<count ; i++ ) {
|
||||
data = mulawToShort[samples[(int)(ooff)]];
|
||||
ooff = ooff + ch->dopplerScale;
|
||||
samp[i].left += (data * leftvol)>>8;
|
||||
samp[i].right += (data * rightvol)>>8;
|
||||
if (ooff >= SND_CHUNK_SIZE*2) {
|
||||
chunk = chunk->next;
|
||||
if (!chunk) {
|
||||
chunk = sc->soundData;
|
||||
}
|
||||
samples = (byte *)chunk->sndChunk;
|
||||
ooff = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
S_PaintChannels
|
||||
===================
|
||||
*/
|
||||
void S_PaintChannels( int endtime ) {
|
||||
int i;
|
||||
int end;
|
||||
channel_t *ch;
|
||||
sfx_t *sc;
|
||||
int ltime, count;
|
||||
int sampleOffset;
|
||||
|
||||
|
||||
snd_vol = s_volume->value*255;
|
||||
|
||||
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
|
||||
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 > PAINTBUFFER_SIZE ) {
|
||||
end = s_paintedtime + PAINTBUFFER_SIZE;
|
||||
}
|
||||
|
||||
// clear the paint buffer to either music or zeros
|
||||
if ( s_rawend < s_paintedtime ) {
|
||||
if ( s_rawend ) {
|
||||
//Com_DPrintf ("background sound underrun\n");
|
||||
}
|
||||
Com_Memset(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);
|
||||
paintbuffer[i-s_paintedtime] = s_rawsamples[s];
|
||||
}
|
||||
// if (i != end)
|
||||
// Com_Printf ("partial stream\n");
|
||||
// else
|
||||
// Com_Printf ("full stream\n");
|
||||
for ( ; i < end ; i++ ) {
|
||||
paintbuffer[i-s_paintedtime].left =
|
||||
paintbuffer[i-s_paintedtime].right = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// paint in the channels.
|
||||
ch = s_channels;
|
||||
for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
|
||||
if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ltime = s_paintedtime;
|
||||
sc = ch->thesfx;
|
||||
|
||||
sampleOffset = ltime - ch->startSample;
|
||||
count = end - ltime;
|
||||
if ( sampleOffset + count > sc->soundLength ) {
|
||||
count = sc->soundLength - sampleOffset;
|
||||
}
|
||||
|
||||
if ( count > 0 ) {
|
||||
if( sc->soundCompressionMethod == 1) {
|
||||
S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else if( sc->soundCompressionMethod == 2) {
|
||||
S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else if( sc->soundCompressionMethod == 3) {
|
||||
S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else {
|
||||
S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// paint in the looped channels.
|
||||
ch = loop_channels;
|
||||
for ( i = 0; i < numLoopChannels ; i++, ch++ ) {
|
||||
if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) {
|
||||
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 ( sampleOffset + count > sc->soundLength ) {
|
||||
count = sc->soundLength - sampleOffset;
|
||||
}
|
||||
|
||||
if ( count > 0 ) {
|
||||
if( sc->soundCompressionMethod == 1) {
|
||||
S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else if( sc->soundCompressionMethod == 2) {
|
||||
S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else if( sc->soundCompressionMethod == 3) {
|
||||
S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
} else {
|
||||
S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime);
|
||||
}
|
||||
ltime += count;
|
||||
}
|
||||
} while ( ltime < end);
|
||||
}
|
||||
|
||||
// transfer out according to DMA format
|
||||
S_TransferPaintBuffer( end );
|
||||
s_paintedtime = end;
|
||||
}
|
||||
}
|
72
code/client/snd_public.h
Normal file
72
code/client/snd_public.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
void S_Init( void );
|
||||
void S_Shutdown( void );
|
||||
|
||||
// if origin is NULL, the sound will be dynamically sourced from the entity
|
||||
void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
|
||||
void S_StartLocalSound( sfxHandle_t sfx, int channelNum );
|
||||
|
||||
void S_StartBackgroundTrack( const char *intro, const char *loop );
|
||||
void S_StopBackgroundTrack( void );
|
||||
|
||||
// cinematics and voice-over-network will send raw samples
|
||||
// 1.0 volume will be direct output of source samples
|
||||
void S_RawSamples (int samples, int rate, int width, int channels,
|
||||
const byte *data, float volume);
|
||||
|
||||
// stop all sounds and the background track
|
||||
void S_StopAllSounds( void );
|
||||
|
||||
// all continuous looping sounds must be added before calling S_Update
|
||||
void S_ClearLoopingSounds( qboolean killall );
|
||||
void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
|
||||
void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
|
||||
void S_StopLoopingSound(int entityNum );
|
||||
|
||||
// recompute the reletive volumes for all running sounds
|
||||
// reletive to the given entityNum / orientation
|
||||
void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
|
||||
|
||||
// let the sound system know where an entity currently is
|
||||
void S_UpdateEntityPosition( int entityNum, const vec3_t origin );
|
||||
|
||||
void S_Update( void );
|
||||
|
||||
void S_DisableSounds( void );
|
||||
|
||||
void S_BeginRegistration( void );
|
||||
|
||||
// RegisterSound will allways return a valid sample, even if it
|
||||
// has to create a placeholder. This prevents continuous filesystem
|
||||
// checks for missing files
|
||||
sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed );
|
||||
|
||||
void S_DisplayFreeMemory(void);
|
||||
|
||||
void S_ClearSoundBuffer( void );
|
||||
|
||||
void SNDDMA_Activate( void );
|
||||
|
||||
void S_UpdateBackgroundTrack( void );
|
253
code/client/snd_wavelet.c
Normal file
253
code/client/snd_wavelet.c
Normal file
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
===========================================================================
|
||||
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 Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "snd_local.h"
|
||||
|
||||
long myftol( float f );
|
||||
|
||||
#define C0 0.4829629131445341
|
||||
#define C1 0.8365163037378079
|
||||
#define C2 0.2241438680420134
|
||||
#define C3 -0.1294095225512604
|
||||
|
||||
void daub4(float b[], unsigned long n, int isign)
|
||||
{
|
||||
float wksp[4097];
|
||||
float *a=b-1; // numerical recipies so a[1] = b[0]
|
||||
|
||||
unsigned long nh,nh1,i,j;
|
||||
|
||||
if (n < 4) return;
|
||||
|
||||
nh1=(nh=n >> 1)+1;
|
||||
if (isign >= 0) {
|
||||
for (i=1,j=1;j<=n-3;j+=2,i++) {
|
||||
wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3];
|
||||
wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3];
|
||||
}
|
||||
wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2];
|
||||
wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2];
|
||||
} else {
|
||||
wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1];
|
||||
wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1];
|
||||
for (i=1,j=3;i<nh;i++) {
|
||||
wksp[j++] = C2*a[i]+C1*a[i+nh]+C0*a[i+1]+C3*a[i+nh1];
|
||||
wksp[j++] = C3*a[i]-C0*a[i+nh]+C1*a[i+1]-C2*a[i+nh1];
|
||||
}
|
||||
}
|
||||
for (i=1;i<=n;i++) {
|
||||
a[i]=wksp[i];
|
||||
}
|
||||
}
|
||||
|
||||
void wt1(float a[], unsigned long n, int isign)
|
||||
{
|
||||
unsigned long nn;
|
||||
int inverseStartLength = n/4;
|
||||
if (n < inverseStartLength) return;
|
||||
if (isign >= 0) {
|
||||
for (nn=n;nn>=inverseStartLength;nn>>=1) daub4(a,nn,isign);
|
||||
} else {
|
||||
for (nn=inverseStartLength;nn<=n;nn<<=1) daub4(a,nn,isign);
|
||||
}
|
||||
}
|
||||
|
||||
/* The number of bits required by each value */
|
||||
static unsigned char numBits[] = {
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
|
||||
byte MuLawEncode(short s) {
|
||||
unsigned long adjusted;
|
||||
byte sign, exponent, mantissa;
|
||||
|
||||
sign = (s<0)?0:0x80;
|
||||
|
||||
if (s<0) s=-s;
|
||||
adjusted = (long)s << (16-sizeof(short)*8);
|
||||
adjusted += 128L + 4L;
|
||||
if (adjusted > 32767) adjusted = 32767;
|
||||
exponent = numBits[(adjusted>>7)&0xff] - 1;
|
||||
mantissa = (adjusted>>(exponent+3))&0xf;
|
||||
return ~(sign | (exponent<<4) | mantissa);
|
||||
}
|
||||
|
||||
short MuLawDecode(byte uLaw) {
|
||||
signed long adjusted;
|
||||
byte exponent, mantissa;
|
||||
|
||||
uLaw = ~uLaw;
|
||||
exponent = (uLaw>>4) & 0x7;
|
||||
mantissa = (uLaw&0xf) + 16;
|
||||
adjusted = (mantissa << (exponent +3)) - 128 - 4;
|
||||
|
||||
return (uLaw & 0x80)? adjusted : -adjusted;
|
||||
}
|
||||
|
||||
short mulawToShort[256];
|
||||
static qboolean madeTable = qfalse;
|
||||
|
||||
static int NXStreamCount;
|
||||
|
||||
void NXPutc(NXStream *stream, char out) {
|
||||
stream[NXStreamCount++] = out;
|
||||
}
|
||||
|
||||
|
||||
void encodeWavelet( sfx_t *sfx, short *packets) {
|
||||
float wksp[4097], temp;
|
||||
int i, samples, size;
|
||||
sndBuffer *newchunk, *chunk;
|
||||
byte *out;
|
||||
|
||||
if (!madeTable) {
|
||||
for (i=0;i<256;i++) {
|
||||
mulawToShort[i] = (float)MuLawDecode((byte)i);
|
||||
}
|
||||
madeTable = qtrue;
|
||||
}
|
||||
chunk = NULL;
|
||||
|
||||
samples = sfx->soundLength;
|
||||
while(samples>0) {
|
||||
size = samples;
|
||||
if (size>(SND_CHUNK_SIZE*2)) {
|
||||
size = (SND_CHUNK_SIZE*2);
|
||||
}
|
||||
|
||||
if (size<4) {
|
||||
size = 4;
|
||||
}
|
||||
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
for(i=0; i<size; i++) {
|
||||
wksp[i] = *packets;
|
||||
packets++;
|
||||
}
|
||||
wt1(wksp, size, 1);
|
||||
out = (byte *)chunk->sndChunk;
|
||||
|
||||
for(i=0;i<size;i++) {
|
||||
temp = wksp[i];
|
||||
if (temp > 32767) temp = 32767; else if (temp<-32768) temp = -32768;
|
||||
out[i] = MuLawEncode((short)temp);
|
||||
}
|
||||
|
||||
chunk->size = size;
|
||||
samples -= size;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeWavelet(sndBuffer *chunk, short *to) {
|
||||
float wksp[4097];
|
||||
int i;
|
||||
byte *out;
|
||||
|
||||
int size = chunk->size;
|
||||
|
||||
out = (byte *)chunk->sndChunk;
|
||||
for(i=0;i<size;i++) {
|
||||
wksp[i] = mulawToShort[out[i]];
|
||||
}
|
||||
|
||||
wt1(wksp, size, -1);
|
||||
|
||||
if (!to) return;
|
||||
|
||||
for(i=0; i<size; i++) {
|
||||
to[i] = wksp[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void encodeMuLaw( sfx_t *sfx, short *packets) {
|
||||
int i, samples, size, grade, poop;
|
||||
sndBuffer *newchunk, *chunk;
|
||||
byte *out;
|
||||
|
||||
if (!madeTable) {
|
||||
for (i=0;i<256;i++) {
|
||||
mulawToShort[i] = (float)MuLawDecode((byte)i);
|
||||
}
|
||||
madeTable = qtrue;
|
||||
}
|
||||
|
||||
chunk = NULL;
|
||||
samples = sfx->soundLength;
|
||||
grade = 0;
|
||||
|
||||
while(samples>0) {
|
||||
size = samples;
|
||||
if (size>(SND_CHUNK_SIZE*2)) {
|
||||
size = (SND_CHUNK_SIZE*2);
|
||||
}
|
||||
|
||||
newchunk = SND_malloc();
|
||||
if (sfx->soundData == NULL) {
|
||||
sfx->soundData = newchunk;
|
||||
} else {
|
||||
chunk->next = newchunk;
|
||||
}
|
||||
chunk = newchunk;
|
||||
out = (byte *)chunk->sndChunk;
|
||||
for(i=0; i<size; i++) {
|
||||
poop = packets[0]+grade;
|
||||
if (poop>32767) {
|
||||
poop = 32767;
|
||||
} else if (poop<-32768) {
|
||||
poop = -32768;
|
||||
}
|
||||
out[i] = MuLawEncode((short)poop);
|
||||
grade = poop - mulawToShort[out[i]];
|
||||
packets++;
|
||||
}
|
||||
chunk->size = size;
|
||||
samples -= size;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeMuLaw(sndBuffer *chunk, short *to) {
|
||||
int i;
|
||||
byte *out;
|
||||
|
||||
int size = chunk->size;
|
||||
|
||||
out = (byte *)chunk->sndChunk;
|
||||
for(i=0;i<size;i++) {
|
||||
to[i] = mulawToShort[out[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue