~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/qcommon/common.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
7
Quake III Arena source code is free software; you can redistribute it
 
8
and/or modify it under the terms of the GNU General Public License as
 
9
published by the Free Software Foundation; either version 2 of the License,
 
10
or (at your option) any later version.
 
11
 
 
12
Quake III Arena source code is distributed in the hope that it will be
 
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with Quake III Arena source code; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
===========================================================================
 
21
*/
 
22
// common.c -- misc functions used in client and server
 
23
 
 
24
#include "q_shared.h"
 
25
#include "qcommon.h"
 
26
#include <setjmp.h>
 
27
#ifndef _WIN32
 
28
#include <netinet/in.h>
 
29
#include <sys/stat.h> // umask
 
30
#else
 
31
#include <winsock.h>
 
32
#endif
 
33
 
 
34
int demo_protocols[] =
 
35
{ 66, 67, 68, 0 };
 
36
 
 
37
#define MAX_NUM_ARGVS   50
 
38
 
 
39
#define MIN_DEDICATED_COMHUNKMEGS 1
 
40
#define MIN_COMHUNKMEGS         56
 
41
#define DEF_COMHUNKMEGS         64
 
42
#define DEF_COMZONEMEGS         24
 
43
#define XSTRING(x)                              STRING(x)
 
44
#define STRING(x)                                       #x
 
45
#define DEF_COMHUNKMEGS_S       XSTRING(DEF_COMHUNKMEGS)
 
46
#define DEF_COMZONEMEGS_S       XSTRING(DEF_COMZONEMEGS)
 
47
 
 
48
int             com_argc;
 
49
char    *com_argv[MAX_NUM_ARGVS+1];
 
50
 
 
51
jmp_buf abortframe;             // an ERR_DROP occured, exit the entire frame
 
52
 
 
53
 
 
54
FILE *debuglogfile;
 
55
static fileHandle_t logfile;
 
56
fileHandle_t    com_journalFile;                        // events are written here
 
57
fileHandle_t    com_journalDataFile;            // config files are written here
 
58
 
 
59
cvar_t  *com_viewlog;
 
60
cvar_t  *com_speeds;
 
61
cvar_t  *com_developer;
 
62
cvar_t  *com_dedicated;
 
63
cvar_t  *com_timescale;
 
64
cvar_t  *com_fixedtime;
 
65
cvar_t  *com_dropsim;           // 0.0 to 1.0, simulated packet drops
 
66
cvar_t  *com_journal;
 
67
cvar_t  *com_maxfps;
 
68
cvar_t  *com_altivec;
 
69
cvar_t  *com_timedemo;
 
70
cvar_t  *com_sv_running;
 
71
cvar_t  *com_cl_running;
 
72
cvar_t  *com_logfile;           // 1 = buffer log, 2 = flush after each print
 
73
cvar_t  *com_showtrace;
 
74
cvar_t  *com_version;
 
75
cvar_t  *com_blood;
 
76
cvar_t  *com_buildScript;       // for automated data building scripts
 
77
cvar_t  *com_introPlayed;
 
78
cvar_t  *cl_paused;
 
79
cvar_t  *sv_paused;
 
80
cvar_t  *cl_packetdelay;
 
81
cvar_t  *sv_packetdelay;
 
82
cvar_t  *com_cameraMode;
 
83
#if defined(_WIN32) && defined(_DEBUG)
 
84
cvar_t  *com_noErrorInterrupt;
 
85
#endif
 
86
 
 
87
// com_speeds times
 
88
int             time_game;
 
89
int             time_frontend;          // renderer frontend time
 
90
int             time_backend;           // renderer backend time
 
91
 
 
92
int                     com_frameTime;
 
93
int                     com_frameMsec;
 
94
int                     com_frameNumber;
 
95
 
 
96
qboolean        com_errorEntered;
 
97
qboolean        com_fullyInitialized;
 
98
 
 
99
char    com_errorMessage[MAXPRINTMSG];
 
100
 
 
101
void Com_WriteConfig_f( void );
 
102
void CIN_CloseAllVideos( void );
 
103
 
 
104
//============================================================================
 
105
 
 
106
static char     *rd_buffer;
 
107
static int      rd_buffersize;
 
108
static void     (*rd_flush)( char *buffer );
 
109
 
 
110
void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
 
111
{
 
112
        if (!buffer || !buffersize || !flush)
 
113
                return;
 
114
        rd_buffer = buffer;
 
115
        rd_buffersize = buffersize;
 
116
        rd_flush = flush;
 
117
 
 
118
        *rd_buffer = 0;
 
119
}
 
120
 
 
121
void Com_EndRedirect (void)
 
122
{
 
123
        if ( rd_flush ) {
 
124
                rd_flush(rd_buffer);
 
125
        }
 
126
 
 
127
        rd_buffer = NULL;
 
128
        rd_buffersize = 0;
 
129
        rd_flush = NULL;
 
130
}
 
131
 
 
132
/*
 
133
=============
 
134
Com_Printf
 
135
 
 
136
Both client and server can use this, and it will output
 
137
to the apropriate place.
 
138
 
 
139
A raw string should NEVER be passed as fmt, because of "%f" type crashers.
 
140
=============
 
141
*/
 
142
void QDECL Com_Printf( const char *fmt, ... ) {
 
143
        va_list         argptr;
 
144
        char            msg[MAXPRINTMSG];
 
145
  static qboolean opening_qconsole = qfalse;
 
146
 
 
147
 
 
148
        va_start (argptr,fmt);
 
149
        Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
 
150
        va_end (argptr);
 
151
 
 
152
        if ( rd_buffer ) {
 
153
                if ((strlen (msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) {
 
154
                        rd_flush(rd_buffer);
 
155
                        *rd_buffer = 0;
 
156
                }
 
157
                Q_strcat(rd_buffer, rd_buffersize, msg);
 
158
    // TTimo nooo .. that would defeat the purpose
 
159
                //rd_flush(rd_buffer);                  
 
160
                //*rd_buffer = 0;
 
161
                return;
 
162
        }
 
163
 
 
164
        // echo to console if we're not a dedicated server
 
165
        if ( com_dedicated && !com_dedicated->integer ) {
 
166
                CL_ConsolePrint( msg );
 
167
        }
 
168
 
 
169
        // echo to dedicated console and early console
 
170
        Sys_Print( msg );
 
171
 
 
172
        // logfile
 
173
        if ( com_logfile && com_logfile->integer ) {
 
174
    // TTimo: only open the qconsole.log if the filesystem is in an initialized state
 
175
    //   also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
 
176
                if ( !logfile && FS_Initialized() && !opening_qconsole) {
 
177
                        struct tm *newtime;
 
178
                        time_t aclock;
 
179
 
 
180
      opening_qconsole = qtrue;
 
181
 
 
182
                        time( &aclock );
 
183
                        newtime = localtime( &aclock );
 
184
 
 
185
                        logfile = FS_FOpenFileWrite( "qconsole.log" );
 
186
                        
 
187
                        if(logfile)
 
188
                        {
 
189
                                Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
 
190
                        
 
191
                                if ( com_logfile->integer > 1 )
 
192
                                {
 
193
                                        // force it to not buffer so we get valid
 
194
                                        // data even if we are crashing
 
195
                                        FS_ForceFlush(logfile);
 
196
                                }
 
197
                        }
 
198
                        else
 
199
                        {
 
200
                                Com_Printf("Opening qconsole.log failed!\n");
 
201
                                Cvar_SetValue("logfile", 0);
 
202
                        }
 
203
 
 
204
      opening_qconsole = qfalse;
 
205
                }
 
206
                if ( logfile && FS_Initialized()) {
 
207
                        FS_Write(msg, strlen(msg), logfile);
 
208
                }
 
209
        }
 
210
}
 
211
 
 
212
 
 
213
/*
 
214
================
 
215
Com_DPrintf
 
216
 
 
217
A Com_Printf that only shows up if the "developer" cvar is set
 
218
================
 
219
*/
 
220
void QDECL Com_DPrintf( const char *fmt, ...) {
 
221
        va_list         argptr;
 
222
        char            msg[MAXPRINTMSG];
 
223
                
 
224
        if ( !com_developer || !com_developer->integer ) {
 
225
                return;                 // don't confuse non-developers with techie stuff...
 
226
        }
 
227
 
 
228
        va_start (argptr,fmt);  
 
229
        Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
 
230
        va_end (argptr);
 
231
        
 
232
        Com_Printf ("%s", msg);
 
233
}
 
234
 
 
235
/*
 
236
=============
 
237
Com_Error
 
238
 
 
239
Both client and server can use this, and it will
 
240
do the apropriate things.
 
241
=============
 
242
*/
 
243
void QDECL Com_Error( int code, const char *fmt, ... ) {
 
244
        va_list         argptr;
 
245
        static int      lastErrorTime;
 
246
        static int      errorCount;
 
247
        int                     currentTime;
 
248
 
 
249
#if defined(_WIN32) && defined(_DEBUG)
 
250
        if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
 
251
                if (!com_noErrorInterrupt->integer) {
 
252
                        __asm {
 
253
                                int 0x03
 
254
                        }
 
255
                }
 
256
        }
 
257
#endif
 
258
 
 
259
        // when we are running automated scripts, make sure we
 
260
        // know if anything failed
 
261
        if ( com_buildScript && com_buildScript->integer ) {
 
262
                code = ERR_FATAL;
 
263
        }
 
264
 
 
265
        // if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
 
266
        currentTime = Sys_Milliseconds();
 
267
        if ( currentTime - lastErrorTime < 100 ) {
 
268
                if ( ++errorCount > 3 ) {
 
269
                        code = ERR_FATAL;
 
270
                }
 
271
        } else {
 
272
                errorCount = 0;
 
273
        }
 
274
        lastErrorTime = currentTime;
 
275
 
 
276
        if ( com_errorEntered ) {
 
277
                Sys_Error( "recursive error after: %s", com_errorMessage );
 
278
        }
 
279
        com_errorEntered = qtrue;
 
280
 
 
281
        va_start (argptr,fmt);
 
282
        vsprintf (com_errorMessage,fmt,argptr);
 
283
        va_end (argptr);
 
284
 
 
285
        if (code != ERR_DISCONNECT && code != ERR_NEED_CD)
 
286
                Cvar_Set("com_errorMessage", com_errorMessage);
 
287
 
 
288
        if (code == ERR_DISCONNECT || code == ERR_SERVERDISCONNECT) {
 
289
                CL_Disconnect( qtrue );
 
290
                CL_FlushMemory( );
 
291
                // make sure we can get at our local stuff
 
292
                FS_PureServerSetLoadedPaks("", "");
 
293
                com_errorEntered = qfalse;
 
294
                longjmp (abortframe, -1);
 
295
        } else if (code == ERR_DROP) {
 
296
                Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage);
 
297
                SV_Shutdown (va("Server crashed: %s",  com_errorMessage));
 
298
                CL_Disconnect( qtrue );
 
299
                CL_FlushMemory( );
 
300
                FS_PureServerSetLoadedPaks("", "");
 
301
                com_errorEntered = qfalse;
 
302
                longjmp (abortframe, -1);
 
303
        } else if ( code == ERR_NEED_CD ) {
 
304
                SV_Shutdown( "Server didn't have CD" );
 
305
                if ( com_cl_running && com_cl_running->integer ) {
 
306
                        CL_Disconnect( qtrue );
 
307
                        CL_FlushMemory( );
 
308
                        com_errorEntered = qfalse;
 
309
                        CL_CDDialog();
 
310
                } else {
 
311
                        Com_Printf("Server didn't have CD\n" );
 
312
                }
 
313
                FS_PureServerSetLoadedPaks("", "");
 
314
                longjmp (abortframe, -1);
 
315
        } else {
 
316
                CL_Shutdown ();
 
317
                SV_Shutdown (va("Server fatal crashed: %s", com_errorMessage));
 
318
        }
 
319
 
 
320
        Com_Shutdown ();
 
321
 
 
322
        Sys_Error ("%s", com_errorMessage);
 
323
}
 
324
 
 
325
 
 
326
/*
 
327
=============
 
328
Com_Quit_f
 
329
 
 
330
Both client and server can use this, and it will
 
331
do the apropriate things.
 
332
=============
 
333
*/
 
334
void Com_Quit_f( void ) {
 
335
        // don't try to shutdown if we are in a recursive error
 
336
        if ( !com_errorEntered ) {
 
337
                SV_Shutdown ("Server quit");
 
338
                CL_Shutdown ();
 
339
                Com_Shutdown ();
 
340
                FS_Shutdown(qtrue);
 
341
        }
 
342
        Sys_Quit ();
 
343
}
 
344
 
 
345
 
 
346
 
 
347
/*
 
348
============================================================================
 
349
 
 
350
COMMAND LINE FUNCTIONS
 
351
 
 
352
+ characters seperate the commandLine string into multiple console
 
353
command lines.
 
354
 
 
355
All of these are valid:
 
356
 
 
357
quake3 +set test blah +map test
 
358
quake3 set test blah+map test
 
359
quake3 set test blah + map test
 
360
 
 
361
============================================================================
 
362
*/
 
363
 
 
364
#define MAX_CONSOLE_LINES       32
 
365
int             com_numConsoleLines;
 
366
char    *com_consoleLines[MAX_CONSOLE_LINES];
 
367
 
 
368
/*
 
369
==================
 
370
Com_ParseCommandLine
 
371
 
 
372
Break it up into multiple console lines
 
373
==================
 
374
*/
 
375
void Com_ParseCommandLine( char *commandLine ) {
 
376
    int inq = 0;
 
377
    com_consoleLines[0] = commandLine;
 
378
    com_numConsoleLines = 1;
 
379
 
 
380
    while ( *commandLine ) {
 
381
        if (*commandLine == '"') {
 
382
            inq = !inq;
 
383
        }
 
384
        // look for a + seperating character
 
385
        // if commandLine came from a file, we might have real line seperators
 
386
        if ( (*commandLine == '+' && !inq) || *commandLine == '\n'  || *commandLine == '\r' ) {
 
387
            if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
 
388
                return;
 
389
            }
 
390
            com_consoleLines[com_numConsoleLines] = commandLine + 1;
 
391
            com_numConsoleLines++;
 
392
            *commandLine = 0;
 
393
        }
 
394
        commandLine++;
 
395
    }
 
396
}
 
397
 
 
398
 
 
399
/*
 
400
===================
 
401
Com_SafeMode
 
402
 
 
403
Check for "safe" on the command line, which will
 
404
skip loading of q3config.cfg
 
405
===================
 
406
*/
 
407
qboolean Com_SafeMode( void ) {
 
408
        int             i;
 
409
 
 
410
        for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
 
411
                Cmd_TokenizeString( com_consoleLines[i] );
 
412
                if ( !Q_stricmp( Cmd_Argv(0), "safe" )
 
413
                        || !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
 
414
                        com_consoleLines[i][0] = 0;
 
415
                        return qtrue;
 
416
                }
 
417
        }
 
418
        return qfalse;
 
419
}
 
420
 
 
421
 
 
422
/*
 
423
===============
 
424
Com_StartupVariable
 
425
 
 
426
Searches for command line parameters that are set commands.
 
427
If match is not NULL, only that cvar will be looked for.
 
428
That is necessary because cddir and basedir need to be set
 
429
before the filesystem is started, but all other sets should
 
430
be after execing the config and default.
 
431
===============
 
432
*/
 
433
void Com_StartupVariable( const char *match ) {
 
434
        int             i;
 
435
        char    *s;
 
436
        cvar_t  *cv;
 
437
 
 
438
        for (i=0 ; i < com_numConsoleLines ; i++) {
 
439
                Cmd_TokenizeString( com_consoleLines[i] );
 
440
                if ( strcmp( Cmd_Argv(0), "set" ) ) {
 
441
                        continue;
 
442
                }
 
443
 
 
444
                s = Cmd_Argv(1);
 
445
                if ( !match || !strcmp( s, match ) ) {
 
446
                        Cvar_Set( s, Cmd_Argv(2) );
 
447
                        cv = Cvar_Get( s, "", 0 );
 
448
                        cv->flags |= CVAR_USER_CREATED;
 
449
//                      com_consoleLines[i] = 0;
 
450
                }
 
451
        }
 
452
}
 
453
 
 
454
 
 
455
/*
 
456
=================
 
457
Com_AddStartupCommands
 
458
 
 
459
Adds command line parameters as script statements
 
460
Commands are seperated by + signs
 
461
 
 
462
Returns qtrue if any late commands were added, which
 
463
will keep the demoloop from immediately starting
 
464
=================
 
465
*/
 
466
qboolean Com_AddStartupCommands( void ) {
 
467
        int             i;
 
468
        qboolean        added;
 
469
 
 
470
        added = qfalse;
 
471
        // quote every token, so args with semicolons can work
 
472
        for (i=0 ; i < com_numConsoleLines ; i++) {
 
473
                if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
 
474
                        continue;
 
475
                }
 
476
 
 
477
                // set commands won't override menu startup
 
478
                if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
 
479
                        added = qtrue;
 
480
                }
 
481
                Cbuf_AddText( com_consoleLines[i] );
 
482
                Cbuf_AddText( "\n" );
 
483
        }
 
484
 
 
485
        return added;
 
486
}
 
487
 
 
488
 
 
489
//============================================================================
 
490
 
 
491
void Info_Print( const char *s ) {
 
492
        char    key[512];
 
493
        char    value[512];
 
494
        char    *o;
 
495
        int             l;
 
496
 
 
497
        if (*s == '\\')
 
498
                s++;
 
499
        while (*s)
 
500
        {
 
501
                o = key;
 
502
                while (*s && *s != '\\')
 
503
                        *o++ = *s++;
 
504
 
 
505
                l = o - key;
 
506
                if (l < 20)
 
507
                {
 
508
                        Com_Memset (o, ' ', 20-l);
 
509
                        key[20] = 0;
 
510
                }
 
511
                else
 
512
                        *o = 0;
 
513
                Com_Printf ("%s", key);
 
514
 
 
515
                if (!*s)
 
516
                {
 
517
                        Com_Printf ("MISSING VALUE\n");
 
518
                        return;
 
519
                }
 
520
 
 
521
                o = value;
 
522
                s++;
 
523
                while (*s && *s != '\\')
 
524
                        *o++ = *s++;
 
525
                *o = 0;
 
526
 
 
527
                if (*s)
 
528
                        s++;
 
529
                Com_Printf ("%s\n", value);
 
530
        }
 
531
}
 
532
 
 
533
/*
 
534
============
 
535
Com_StringContains
 
536
============
 
537
*/
 
538
char *Com_StringContains(char *str1, char *str2, int casesensitive) {
 
539
        int len, i, j;
 
540
 
 
541
        len = strlen(str1) - strlen(str2);
 
542
        for (i = 0; i <= len; i++, str1++) {
 
543
                for (j = 0; str2[j]; j++) {
 
544
                        if (casesensitive) {
 
545
                                if (str1[j] != str2[j]) {
 
546
                                        break;
 
547
                                }
 
548
                        }
 
549
                        else {
 
550
                                if (toupper(str1[j]) != toupper(str2[j])) {
 
551
                                        break;
 
552
                                }
 
553
                        }
 
554
                }
 
555
                if (!str2[j]) {
 
556
                        return str1;
 
557
                }
 
558
        }
 
559
        return NULL;
 
560
}
 
561
 
 
562
/*
 
563
============
 
564
Com_Filter
 
565
============
 
566
*/
 
567
int Com_Filter(char *filter, char *name, int casesensitive)
 
568
{
 
569
        char buf[MAX_TOKEN_CHARS];
 
570
        char *ptr;
 
571
        int i, found;
 
572
 
 
573
        while(*filter) {
 
574
                if (*filter == '*') {
 
575
                        filter++;
 
576
                        for (i = 0; *filter; i++) {
 
577
                                if (*filter == '*' || *filter == '?') break;
 
578
                                buf[i] = *filter;
 
579
                                filter++;
 
580
                        }
 
581
                        buf[i] = '\0';
 
582
                        if (strlen(buf)) {
 
583
                                ptr = Com_StringContains(name, buf, casesensitive);
 
584
                                if (!ptr) return qfalse;
 
585
                                name = ptr + strlen(buf);
 
586
                        }
 
587
                }
 
588
                else if (*filter == '?') {
 
589
                        filter++;
 
590
                        name++;
 
591
                }
 
592
                else if (*filter == '[' && *(filter+1) == '[') {
 
593
                        filter++;
 
594
                }
 
595
                else if (*filter == '[') {
 
596
                        filter++;
 
597
                        found = qfalse;
 
598
                        while(*filter && !found) {
 
599
                                if (*filter == ']' && *(filter+1) != ']') break;
 
600
                                if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
 
601
                                        if (casesensitive) {
 
602
                                                if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
 
603
                                        }
 
604
                                        else {
 
605
                                                if (toupper(*name) >= toupper(*filter) &&
 
606
                                                        toupper(*name) <= toupper(*(filter+2))) found = qtrue;
 
607
                                        }
 
608
                                        filter += 3;
 
609
                                }
 
610
                                else {
 
611
                                        if (casesensitive) {
 
612
                                                if (*filter == *name) found = qtrue;
 
613
                                        }
 
614
                                        else {
 
615
                                                if (toupper(*filter) == toupper(*name)) found = qtrue;
 
616
                                        }
 
617
                                        filter++;
 
618
                                }
 
619
                        }
 
620
                        if (!found) return qfalse;
 
621
                        while(*filter) {
 
622
                                if (*filter == ']' && *(filter+1) != ']') break;
 
623
                                filter++;
 
624
                        }
 
625
                        filter++;
 
626
                        name++;
 
627
                }
 
628
                else {
 
629
                        if (casesensitive) {
 
630
                                if (*filter != *name) return qfalse;
 
631
                        }
 
632
                        else {
 
633
                                if (toupper(*filter) != toupper(*name)) return qfalse;
 
634
                        }
 
635
                        filter++;
 
636
                        name++;
 
637
                }
 
638
        }
 
639
        return qtrue;
 
640
}
 
641
 
 
642
/*
 
643
============
 
644
Com_FilterPath
 
645
============
 
646
*/
 
647
int Com_FilterPath(char *filter, char *name, int casesensitive)
 
648
{
 
649
        int i;
 
650
        char new_filter[MAX_QPATH];
 
651
        char new_name[MAX_QPATH];
 
652
 
 
653
        for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) {
 
654
                if ( filter[i] == '\\' || filter[i] == ':' ) {
 
655
                        new_filter[i] = '/';
 
656
                }
 
657
                else {
 
658
                        new_filter[i] = filter[i];
 
659
                }
 
660
        }
 
661
        new_filter[i] = '\0';
 
662
        for (i = 0; i < MAX_QPATH-1 && name[i]; i++) {
 
663
                if ( name[i] == '\\' || name[i] == ':' ) {
 
664
                        new_name[i] = '/';
 
665
                }
 
666
                else {
 
667
                        new_name[i] = name[i];
 
668
                }
 
669
        }
 
670
        new_name[i] = '\0';
 
671
        return Com_Filter(new_filter, new_name, casesensitive);
 
672
}
 
673
 
 
674
/*
 
675
============
 
676
Com_HashKey
 
677
============
 
678
*/
 
679
int Com_HashKey(char *string, int maxlen) {
 
680
        int register hash, i;
 
681
 
 
682
        hash = 0;
 
683
        for (i = 0; i < maxlen && string[i] != '\0'; i++) {
 
684
                hash += string[i] * (119 + i);
 
685
        }
 
686
        hash = (hash ^ (hash >> 10) ^ (hash >> 20));
 
687
        return hash;
 
688
}
 
689
 
 
690
/*
 
691
================
 
692
Com_RealTime
 
693
================
 
694
*/
 
695
int Com_RealTime(qtime_t *qtime) {
 
696
        time_t t;
 
697
        struct tm *tms;
 
698
 
 
699
        t = time(NULL);
 
700
        if (!qtime)
 
701
                return t;
 
702
        tms = localtime(&t);
 
703
        if (tms) {
 
704
                qtime->tm_sec = tms->tm_sec;
 
705
                qtime->tm_min = tms->tm_min;
 
706
                qtime->tm_hour = tms->tm_hour;
 
707
                qtime->tm_mday = tms->tm_mday;
 
708
                qtime->tm_mon = tms->tm_mon;
 
709
                qtime->tm_year = tms->tm_year;
 
710
                qtime->tm_wday = tms->tm_wday;
 
711
                qtime->tm_yday = tms->tm_yday;
 
712
                qtime->tm_isdst = tms->tm_isdst;
 
713
        }
 
714
        return t;
 
715
}
 
716
 
 
717
 
 
718
/*
 
719
==============================================================================
 
720
 
 
721
                                                ZONE MEMORY ALLOCATION
 
722
 
 
723
There is never any space between memblocks, and there will never be two
 
724
contiguous free memblocks.
 
725
 
 
726
The rover can be left pointing at a non-empty block
 
727
 
 
728
The zone calls are pretty much only used for small strings and structures,
 
729
all big things are allocated on the hunk.
 
730
==============================================================================
 
731
*/
 
732
 
 
733
#define ZONEID  0x1d4a11
 
734
#define MINFRAGMENT     64
 
735
 
 
736
typedef struct zonedebug_s {
 
737
        char *label;
 
738
        char *file;
 
739
        int line;
 
740
        int allocSize;
 
741
} zonedebug_t;
 
742
 
 
743
typedef struct memblock_s {
 
744
        int             size;           // including the header and possibly tiny fragments
 
745
        int     tag;            // a tag of 0 is a free block
 
746
        struct memblock_s       *next, *prev;
 
747
        int     id;                     // should be ZONEID
 
748
#ifdef ZONE_DEBUG
 
749
        zonedebug_t d;
 
750
#endif
 
751
} memblock_t;
 
752
 
 
753
typedef struct {
 
754
        int             size;                   // total bytes malloced, including header
 
755
        int             used;                   // total bytes used
 
756
        memblock_t      blocklist;      // start / end cap for linked list
 
757
        memblock_t      *rover;
 
758
} memzone_t;
 
759
 
 
760
// main zone for all "dynamic" memory allocation
 
761
memzone_t       *mainzone;
 
762
// we also have a small zone for small allocations that would only
 
763
// fragment the main zone (think of cvar and cmd strings)
 
764
memzone_t       *smallzone;
 
765
 
 
766
void Z_CheckHeap( void );
 
767
 
 
768
/*
 
769
========================
 
770
Z_ClearZone
 
771
========================
 
772
*/
 
773
void Z_ClearZone( memzone_t *zone, int size ) {
 
774
        memblock_t      *block;
 
775
        
 
776
        // set the entire zone to one free block
 
777
 
 
778
        zone->blocklist.next = zone->blocklist.prev = block =
 
779
                (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
 
780
        zone->blocklist.tag = 1;        // in use block
 
781
        zone->blocklist.id = 0;
 
782
        zone->blocklist.size = 0;
 
783
        zone->rover = block;
 
784
        zone->size = size;
 
785
        zone->used = 0;
 
786
        
 
787
        block->prev = block->next = &zone->blocklist;
 
788
        block->tag = 0;                 // free block
 
789
        block->id = ZONEID;
 
790
        block->size = size - sizeof(memzone_t);
 
791
}
 
792
 
 
793
/*
 
794
========================
 
795
Z_AvailableZoneMemory
 
796
========================
 
797
*/
 
798
int Z_AvailableZoneMemory( memzone_t *zone ) {
 
799
        return zone->size - zone->used;
 
800
}
 
801
 
 
802
/*
 
803
========================
 
804
Z_AvailableMemory
 
805
========================
 
806
*/
 
807
int Z_AvailableMemory( void ) {
 
808
        return Z_AvailableZoneMemory( mainzone );
 
809
}
 
810
 
 
811
/*
 
812
========================
 
813
Z_Free
 
814
========================
 
815
*/
 
816
void Z_Free( void *ptr ) {
 
817
        memblock_t      *block, *other;
 
818
        memzone_t *zone;
 
819
        
 
820
        if (!ptr) {
 
821
                Com_Error( ERR_DROP, "Z_Free: NULL pointer" );
 
822
        }
 
823
 
 
824
        block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
 
825
        if (block->id != ZONEID) {
 
826
                Com_Error( ERR_FATAL, "Z_Free: freed a pointer without ZONEID" );
 
827
        }
 
828
        if (block->tag == 0) {
 
829
                Com_Error( ERR_FATAL, "Z_Free: freed a freed pointer" );
 
830
        }
 
831
        // if static memory
 
832
        if (block->tag == TAG_STATIC) {
 
833
                return;
 
834
        }
 
835
 
 
836
        // check the memory trash tester
 
837
        if ( *(int *)((byte *)block + block->size - 4 ) != ZONEID ) {
 
838
                Com_Error( ERR_FATAL, "Z_Free: memory block wrote past end" );
 
839
        }
 
840
 
 
841
        if (block->tag == TAG_SMALL) {
 
842
                zone = smallzone;
 
843
        }
 
844
        else {
 
845
                zone = mainzone;
 
846
        }
 
847
 
 
848
        zone->used -= block->size;
 
849
        // set the block to something that should cause problems
 
850
        // if it is referenced...
 
851
        Com_Memset( ptr, 0xaa, block->size - sizeof( *block ) );
 
852
 
 
853
        block->tag = 0;         // mark as free
 
854
        
 
855
        other = block->prev;
 
856
        if (!other->tag) {
 
857
                // merge with previous free block
 
858
                other->size += block->size;
 
859
                other->next = block->next;
 
860
                other->next->prev = other;
 
861
                if (block == zone->rover) {
 
862
                        zone->rover = other;
 
863
                }
 
864
                block = other;
 
865
        }
 
866
 
 
867
        zone->rover = block;
 
868
 
 
869
        other = block->next;
 
870
        if ( !other->tag ) {
 
871
                // merge the next free block onto the end
 
872
                block->size += other->size;
 
873
                block->next = other->next;
 
874
                block->next->prev = block;
 
875
                if (other == zone->rover) {
 
876
                        zone->rover = block;
 
877
                }
 
878
        }
 
879
}
 
880
 
 
881
 
 
882
/*
 
883
================
 
884
Z_FreeTags
 
885
================
 
886
*/
 
887
void Z_FreeTags( int tag ) {
 
888
        int                     count;
 
889
        memzone_t       *zone;
 
890
 
 
891
        if ( tag == TAG_SMALL ) {
 
892
                zone = smallzone;
 
893
        }
 
894
        else {
 
895
                zone = mainzone;
 
896
        }
 
897
        count = 0;
 
898
        // use the rover as our pointer, because
 
899
        // Z_Free automatically adjusts it
 
900
        zone->rover = zone->blocklist.next;
 
901
        do {
 
902
                if ( zone->rover->tag == tag ) {
 
903
                        count++;
 
904
                        Z_Free( (void *)(zone->rover + 1) );
 
905
                        continue;
 
906
                }
 
907
                zone->rover = zone->rover->next;
 
908
        } while ( zone->rover != &zone->blocklist );
 
909
}
 
910
 
 
911
 
 
912
/*
 
913
================
 
914
Z_TagMalloc
 
915
================
 
916
*/
 
917
#ifdef ZONE_DEBUG
 
918
void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ) {
 
919
#else
 
920
void *Z_TagMalloc( int size, int tag ) {
 
921
#endif
 
922
        int             extra, allocSize;
 
923
        memblock_t      *start, *rover, *new, *base;
 
924
        memzone_t *zone;
 
925
 
 
926
        if (!tag) {
 
927
                Com_Error( ERR_FATAL, "Z_TagMalloc: tried to use a 0 tag" );
 
928
        }
 
929
 
 
930
        if ( tag == TAG_SMALL ) {
 
931
                zone = smallzone;
 
932
        }
 
933
        else {
 
934
                zone = mainzone;
 
935
        }
 
936
 
 
937
        allocSize = size;
 
938
        //
 
939
        // scan through the block list looking for the first free block
 
940
        // of sufficient size
 
941
        //
 
942
        size += sizeof(memblock_t);     // account for size of block header
 
943
        size += 4;                                      // space for memory trash tester
 
944
        size = PAD(size, sizeof(intptr_t));             // align to 32/64 bit boundary
 
945
        
 
946
        base = rover = zone->rover;
 
947
        start = base->prev;
 
948
        
 
949
        do {
 
950
                if (rover == start)     {
 
951
#ifdef ZONE_DEBUG
 
952
                        Z_LogHeap();
 
953
#endif
 
954
                        // scaned all the way around the list
 
955
                        Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone",
 
956
                                                                size, zone == smallzone ? "small" : "main");
 
957
                        return NULL;
 
958
                }
 
959
                if (rover->tag) {
 
960
                        base = rover = rover->next;
 
961
                } else {
 
962
                        rover = rover->next;
 
963
                }
 
964
        } while (base->tag || base->size < size);
 
965
        
 
966
        //
 
967
        // found a block big enough
 
968
        //
 
969
        extra = base->size - size;
 
970
        if (extra > MINFRAGMENT) {
 
971
                // there will be a free fragment after the allocated block
 
972
                new = (memblock_t *) ((byte *)base + size );
 
973
                new->size = extra;
 
974
                new->tag = 0;                   // free block
 
975
                new->prev = base;
 
976
                new->id = ZONEID;
 
977
                new->next = base->next;
 
978
                new->next->prev = new;
 
979
                base->next = new;
 
980
                base->size = size;
 
981
        }
 
982
        
 
983
        base->tag = tag;                        // no longer a free block
 
984
        
 
985
        zone->rover = base->next;       // next allocation will start looking here
 
986
        zone->used += base->size;       //
 
987
        
 
988
        base->id = ZONEID;
 
989
 
 
990
#ifdef ZONE_DEBUG
 
991
        base->d.label = label;
 
992
        base->d.file = file;
 
993
        base->d.line = line;
 
994
        base->d.allocSize = allocSize;
 
995
#endif
 
996
 
 
997
        // marker for memory trash testing
 
998
        *(int *)((byte *)base + base->size - 4) = ZONEID;
 
999
 
 
1000
        return (void *) ((byte *)base + sizeof(memblock_t));
 
1001
}
 
1002
 
 
1003
/*
 
1004
========================
 
1005
Z_Malloc
 
1006
========================
 
1007
*/
 
1008
#ifdef ZONE_DEBUG
 
1009
void *Z_MallocDebug( int size, char *label, char *file, int line ) {
 
1010
#else
 
1011
void *Z_Malloc( int size ) {
 
1012
#endif
 
1013
        void    *buf;
 
1014
        
 
1015
  //Z_CheckHeap ();     // DEBUG
 
1016
 
 
1017
#ifdef ZONE_DEBUG
 
1018
        buf = Z_TagMallocDebug( size, TAG_GENERAL, label, file, line );
 
1019
#else
 
1020
        buf = Z_TagMalloc( size, TAG_GENERAL );
 
1021
#endif
 
1022
        Com_Memset( buf, 0, size );
 
1023
 
 
1024
        return buf;
 
1025
}
 
1026
 
 
1027
#ifdef ZONE_DEBUG
 
1028
void *S_MallocDebug( int size, char *label, char *file, int line ) {
 
1029
        return Z_TagMallocDebug( size, TAG_SMALL, label, file, line );
 
1030
}
 
1031
#else
 
1032
void *S_Malloc( int size ) {
 
1033
        return Z_TagMalloc( size, TAG_SMALL );
 
1034
}
 
1035
#endif
 
1036
 
 
1037
/*
 
1038
========================
 
1039
Z_CheckHeap
 
1040
========================
 
1041
*/
 
1042
void Z_CheckHeap( void ) {
 
1043
        memblock_t      *block;
 
1044
        
 
1045
        for (block = mainzone->blocklist.next ; ; block = block->next) {
 
1046
                if (block->next == &mainzone->blocklist) {
 
1047
                        break;                  // all blocks have been hit
 
1048
                }
 
1049
                if ( (byte *)block + block->size != (byte *)block->next)
 
1050
                        Com_Error( ERR_FATAL, "Z_CheckHeap: block size does not touch the next block\n" );
 
1051
                if ( block->next->prev != block) {
 
1052
                        Com_Error( ERR_FATAL, "Z_CheckHeap: next block doesn't have proper back link\n" );
 
1053
                }
 
1054
                if ( !block->tag && !block->next->tag ) {
 
1055
                        Com_Error( ERR_FATAL, "Z_CheckHeap: two consecutive free blocks\n" );
 
1056
                }
 
1057
        }
 
1058
}
 
1059
 
 
1060
/*
 
1061
========================
 
1062
Z_LogZoneHeap
 
1063
========================
 
1064
*/
 
1065
void Z_LogZoneHeap( memzone_t *zone, char *name ) {
 
1066
#ifdef ZONE_DEBUG
 
1067
        char dump[32], *ptr;
 
1068
        int  i, j;
 
1069
#endif
 
1070
        memblock_t      *block;
 
1071
        char            buf[4096];
 
1072
        int size, allocSize, numBlocks;
 
1073
 
 
1074
        if (!logfile || !FS_Initialized())
 
1075
                return;
 
1076
        size = allocSize = numBlocks = 0;
 
1077
        Com_sprintf(buf, sizeof(buf), "\r\n================\r\n%s log\r\n================\r\n", name);
 
1078
        FS_Write(buf, strlen(buf), logfile);
 
1079
        for (block = zone->blocklist.next ; block->next != &zone->blocklist; block = block->next) {
 
1080
                if (block->tag) {
 
1081
#ifdef ZONE_DEBUG
 
1082
                        ptr = ((char *) block) + sizeof(memblock_t);
 
1083
                        j = 0;
 
1084
                        for (i = 0; i < 20 && i < block->d.allocSize; i++) {
 
1085
                                if (ptr[i] >= 32 && ptr[i] < 127) {
 
1086
                                        dump[j++] = ptr[i];
 
1087
                                }
 
1088
                                else {
 
1089
                                        dump[j++] = '_';
 
1090
                                }
 
1091
                        }
 
1092
                        dump[j] = '\0';
 
1093
                        Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s) [%s]\r\n", block->d.allocSize, block->d.file, block->d.line, block->d.label, dump);
 
1094
                        FS_Write(buf, strlen(buf), logfile);
 
1095
                        allocSize += block->d.allocSize;
 
1096
#endif
 
1097
                        size += block->size;
 
1098
                        numBlocks++;
 
1099
                }
 
1100
        }
 
1101
#ifdef ZONE_DEBUG
 
1102
        // subtract debug memory
 
1103
        size -= numBlocks * sizeof(zonedebug_t);
 
1104
#else
 
1105
        allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment
 
1106
#endif
 
1107
        Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks);
 
1108
        FS_Write(buf, strlen(buf), logfile);
 
1109
        Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name);
 
1110
        FS_Write(buf, strlen(buf), logfile);
 
1111
}
 
1112
 
 
1113
/*
 
1114
========================
 
1115
Z_LogHeap
 
1116
========================
 
1117
*/
 
1118
void Z_LogHeap( void ) {
 
1119
        Z_LogZoneHeap( mainzone, "MAIN" );
 
1120
        Z_LogZoneHeap( smallzone, "SMALL" );
 
1121
}
 
1122
 
 
1123
// static mem blocks to reduce a lot of small zone overhead
 
1124
typedef struct memstatic_s {
 
1125
        memblock_t b;
 
1126
        byte mem[2];
 
1127
} memstatic_t;
 
1128
 
 
1129
// bk001204 - initializer brackets
 
1130
memstatic_t emptystring =
 
1131
        { {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} };
 
1132
memstatic_t numberstring[] = {
 
1133
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} },
 
1134
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} },
 
1135
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} },
 
1136
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} },
 
1137
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} },
 
1138
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} },
 
1139
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} },
 
1140
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} },
 
1141
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} }, 
 
1142
        { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} }
 
1143
};
 
1144
 
 
1145
/*
 
1146
========================
 
1147
CopyString
 
1148
 
 
1149
 NOTE:  never write over the memory CopyString returns because
 
1150
                memory from a memstatic_t might be returned
 
1151
========================
 
1152
*/
 
1153
char *CopyString( const char *in ) {
 
1154
        char    *out;
 
1155
 
 
1156
        if (!in[0]) {
 
1157
                return ((char *)&emptystring) + sizeof(memblock_t);
 
1158
        }
 
1159
        else if (!in[1]) {
 
1160
                if (in[0] >= '0' && in[0] <= '9') {
 
1161
                        return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t);
 
1162
                }
 
1163
        }
 
1164
        out = S_Malloc (strlen(in)+1);
 
1165
        strcpy (out, in);
 
1166
        return out;
 
1167
}
 
1168
 
 
1169
/*
 
1170
==============================================================================
 
1171
 
 
1172
Goals:
 
1173
        reproducable without history effects -- no out of memory errors on weird map to map changes
 
1174
        allow restarting of the client without fragmentation
 
1175
        minimize total pages in use at run time
 
1176
        minimize total pages needed during load time
 
1177
 
 
1178
  Single block of memory with stack allocators coming from both ends towards the middle.
 
1179
 
 
1180
  One side is designated the temporary memory allocator.
 
1181
 
 
1182
  Temporary memory can be allocated and freed in any order.
 
1183
 
 
1184
  A highwater mark is kept of the most in use at any time.
 
1185
 
 
1186
  When there is no temporary memory allocated, the permanent and temp sides
 
1187
  can be switched, allowing the already touched temp memory to be used for
 
1188
  permanent storage.
 
1189
 
 
1190
  Temp memory must never be allocated on two ends at once, or fragmentation
 
1191
  could occur.
 
1192
 
 
1193
  If we have any in-use temp memory, additional temp allocations must come from
 
1194
  that side.
 
1195
 
 
1196
  If not, we can choose to make either side the new temp side and push future
 
1197
  permanent allocations to the other side.  Permanent allocations should be
 
1198
  kept on the side that has the current greatest wasted highwater mark.
 
1199
 
 
1200
==============================================================================
 
1201
*/
 
1202
 
 
1203
 
 
1204
#define HUNK_MAGIC      0x89537892
 
1205
#define HUNK_FREE_MAGIC 0x89537893
 
1206
 
 
1207
typedef struct {
 
1208
        int             magic;
 
1209
        int             size;
 
1210
} hunkHeader_t;
 
1211
 
 
1212
typedef struct {
 
1213
        int             mark;
 
1214
        int             permanent;
 
1215
        int             temp;
 
1216
        int             tempHighwater;
 
1217
} hunkUsed_t;
 
1218
 
 
1219
typedef struct hunkblock_s {
 
1220
        int size;
 
1221
        byte printed;
 
1222
        struct hunkblock_s *next;
 
1223
        char *label;
 
1224
        char *file;
 
1225
        int line;
 
1226
} hunkblock_t;
 
1227
 
 
1228
static  hunkblock_t *hunkblocks;
 
1229
 
 
1230
static  hunkUsed_t      hunk_low, hunk_high;
 
1231
static  hunkUsed_t      *hunk_permanent, *hunk_temp;
 
1232
 
 
1233
static  byte    *s_hunkData = NULL;
 
1234
static  int             s_hunkTotal;
 
1235
 
 
1236
static  int             s_zoneTotal;
 
1237
static  int             s_smallZoneTotal;
 
1238
 
 
1239
 
 
1240
/*
 
1241
=================
 
1242
Com_Meminfo_f
 
1243
=================
 
1244
*/
 
1245
void Com_Meminfo_f( void ) {
 
1246
        memblock_t      *block;
 
1247
        int                     zoneBytes, zoneBlocks;
 
1248
        int                     smallZoneBytes, smallZoneBlocks;
 
1249
        int                     botlibBytes, rendererBytes;
 
1250
        int                     unused;
 
1251
 
 
1252
        zoneBytes = 0;
 
1253
        botlibBytes = 0;
 
1254
        rendererBytes = 0;
 
1255
        zoneBlocks = 0;
 
1256
        for (block = mainzone->blocklist.next ; ; block = block->next) {
 
1257
                if ( Cmd_Argc() != 1 ) {
 
1258
                        Com_Printf ("block:%p    size:%7i    tag:%3i\n",
 
1259
                                block, block->size, block->tag);
 
1260
                }
 
1261
                if ( block->tag ) {
 
1262
                        zoneBytes += block->size;
 
1263
                        zoneBlocks++;
 
1264
                        if ( block->tag == TAG_BOTLIB ) {
 
1265
                                botlibBytes += block->size;
 
1266
                        } else if ( block->tag == TAG_RENDERER ) {
 
1267
                                rendererBytes += block->size;
 
1268
                        }
 
1269
                }
 
1270
 
 
1271
                if (block->next == &mainzone->blocklist) {
 
1272
                        break;                  // all blocks have been hit     
 
1273
                }
 
1274
                if ( (byte *)block + block->size != (byte *)block->next) {
 
1275
                        Com_Printf ("ERROR: block size does not touch the next block\n");
 
1276
                }
 
1277
                if ( block->next->prev != block) {
 
1278
                        Com_Printf ("ERROR: next block doesn't have proper back link\n");
 
1279
                }
 
1280
                if ( !block->tag && !block->next->tag ) {
 
1281
                        Com_Printf ("ERROR: two consecutive free blocks\n");
 
1282
                }
 
1283
        }
 
1284
 
 
1285
        smallZoneBytes = 0;
 
1286
        smallZoneBlocks = 0;
 
1287
        for (block = smallzone->blocklist.next ; ; block = block->next) {
 
1288
                if ( block->tag ) {
 
1289
                        smallZoneBytes += block->size;
 
1290
                        smallZoneBlocks++;
 
1291
                }
 
1292
 
 
1293
                if (block->next == &smallzone->blocklist) {
 
1294
                        break;                  // all blocks have been hit     
 
1295
                }
 
1296
        }
 
1297
 
 
1298
        Com_Printf( "%8i bytes total hunk\n", s_hunkTotal );
 
1299
        Com_Printf( "%8i bytes total zone\n", s_zoneTotal );
 
1300
        Com_Printf( "\n" );
 
1301
        Com_Printf( "%8i low mark\n", hunk_low.mark );
 
1302
        Com_Printf( "%8i low permanent\n", hunk_low.permanent );
 
1303
        if ( hunk_low.temp != hunk_low.permanent ) {
 
1304
                Com_Printf( "%8i low temp\n", hunk_low.temp );
 
1305
        }
 
1306
        Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater );
 
1307
        Com_Printf( "\n" );
 
1308
        Com_Printf( "%8i high mark\n", hunk_high.mark );
 
1309
        Com_Printf( "%8i high permanent\n", hunk_high.permanent );
 
1310
        if ( hunk_high.temp != hunk_high.permanent ) {
 
1311
                Com_Printf( "%8i high temp\n", hunk_high.temp );
 
1312
        }
 
1313
        Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater );
 
1314
        Com_Printf( "\n" );
 
1315
        Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent );
 
1316
        unused = 0;
 
1317
        if ( hunk_low.tempHighwater > hunk_low.permanent ) {
 
1318
                unused += hunk_low.tempHighwater - hunk_low.permanent;
 
1319
        }
 
1320
        if ( hunk_high.tempHighwater > hunk_high.permanent ) {
 
1321
                unused += hunk_high.tempHighwater - hunk_high.permanent;
 
1322
        }
 
1323
        Com_Printf( "%8i unused highwater\n", unused );
 
1324
        Com_Printf( "\n" );
 
1325
        Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks      );
 
1326
        Com_Printf( "        %8i bytes in dynamic botlib\n", botlibBytes );
 
1327
        Com_Printf( "        %8i bytes in dynamic renderer\n", rendererBytes );
 
1328
        Com_Printf( "        %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) );
 
1329
        Com_Printf( "        %8i bytes in small Zone memory\n", smallZoneBytes );
 
1330
}
 
1331
 
 
1332
/*
 
1333
===============
 
1334
Com_TouchMemory
 
1335
 
 
1336
Touch all known used data to make sure it is paged in
 
1337
===============
 
1338
*/
 
1339
void Com_TouchMemory( void ) {
 
1340
        int             start, end;
 
1341
        int             i, j;
 
1342
        int             sum;
 
1343
        memblock_t      *block;
 
1344
 
 
1345
        Z_CheckHeap();
 
1346
 
 
1347
        start = Sys_Milliseconds();
 
1348
 
 
1349
        sum = 0;
 
1350
 
 
1351
        j = hunk_low.permanent >> 2;
 
1352
        for ( i = 0 ; i < j ; i+=64 ) {                 // only need to touch each page
 
1353
                sum += ((int *)s_hunkData)[i];
 
1354
        }
 
1355
 
 
1356
        i = ( s_hunkTotal - hunk_high.permanent ) >> 2;
 
1357
        j = hunk_high.permanent >> 2;
 
1358
        for (  ; i < j ; i+=64 ) {                      // only need to touch each page
 
1359
                sum += ((int *)s_hunkData)[i];
 
1360
        }
 
1361
 
 
1362
        for (block = mainzone->blocklist.next ; ; block = block->next) {
 
1363
                if ( block->tag ) {
 
1364
                        j = block->size >> 2;
 
1365
                        for ( i = 0 ; i < j ; i+=64 ) {                         // only need to touch each page
 
1366
                                sum += ((int *)block)[i];
 
1367
                        }
 
1368
                }
 
1369
                if ( block->next == &mainzone->blocklist ) {
 
1370
                        break;                  // all blocks have been hit     
 
1371
                }
 
1372
        }
 
1373
 
 
1374
        end = Sys_Milliseconds();
 
1375
 
 
1376
        Com_Printf( "Com_TouchMemory: %i msec\n", end - start );
 
1377
}
 
1378
 
 
1379
 
 
1380
 
 
1381
/*
 
1382
=================
 
1383
Com_InitZoneMemory
 
1384
=================
 
1385
*/
 
1386
void Com_InitSmallZoneMemory( void ) {
 
1387
        s_smallZoneTotal = 512 * 1024;
 
1388
        // bk001205 - was malloc
 
1389
        smallzone = calloc( s_smallZoneTotal, 1 );
 
1390
        if ( !smallzone ) {
 
1391
                Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) );
 
1392
        }
 
1393
        Z_ClearZone( smallzone, s_smallZoneTotal );
 
1394
        
 
1395
        return;
 
1396
}
 
1397
 
 
1398
void Com_InitZoneMemory( void ) {
 
1399
        cvar_t  *cv;
 
1400
 
 
1401
        //FIXME: 05/01/06 com_zoneMegs is useless right now as neither q3config.cfg nor
 
1402
        // Com_StartupVariable have been executed by this point. The net result is that
 
1403
        // s_zoneTotal will always be set to the default value.
 
1404
 
 
1405
        // allocate the random block zone
 
1406
        cv = Cvar_Get( "com_zoneMegs", DEF_COMZONEMEGS_S, CVAR_LATCH | CVAR_ARCHIVE );
 
1407
 
 
1408
        if ( cv->integer < DEF_COMZONEMEGS ) {
 
1409
                s_zoneTotal = 1024 * 1024 * DEF_COMZONEMEGS;
 
1410
        } else {
 
1411
                s_zoneTotal = cv->integer * 1024 * 1024;
 
1412
        }
 
1413
 
 
1414
        // bk001205 - was malloc
 
1415
        mainzone = calloc( s_zoneTotal, 1 );
 
1416
        if ( !mainzone ) {
 
1417
                Com_Error( ERR_FATAL, "Zone data failed to allocate %i megs", s_zoneTotal / (1024*1024) );
 
1418
        }
 
1419
        Z_ClearZone( mainzone, s_zoneTotal );
 
1420
 
 
1421
}
 
1422
 
 
1423
/*
 
1424
=================
 
1425
Hunk_Log
 
1426
=================
 
1427
*/
 
1428
void Hunk_Log( void) {
 
1429
        hunkblock_t     *block;
 
1430
        char            buf[4096];
 
1431
        int size, numBlocks;
 
1432
 
 
1433
        if (!logfile || !FS_Initialized())
 
1434
                return;
 
1435
        size = 0;
 
1436
        numBlocks = 0;
 
1437
        Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk log\r\n================\r\n");
 
1438
        FS_Write(buf, strlen(buf), logfile);
 
1439
        for (block = hunkblocks ; block; block = block->next) {
 
1440
#ifdef HUNK_DEBUG
 
1441
                Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", block->size, block->file, block->line, block->label);
 
1442
                FS_Write(buf, strlen(buf), logfile);
 
1443
#endif
 
1444
                size += block->size;
 
1445
                numBlocks++;
 
1446
        }
 
1447
        Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
 
1448
        FS_Write(buf, strlen(buf), logfile);
 
1449
        Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
 
1450
        FS_Write(buf, strlen(buf), logfile);
 
1451
}
 
1452
 
 
1453
/*
 
1454
=================
 
1455
Hunk_SmallLog
 
1456
=================
 
1457
*/
 
1458
void Hunk_SmallLog( void) {
 
1459
        hunkblock_t     *block, *block2;
 
1460
        char            buf[4096];
 
1461
        int size, locsize, numBlocks;
 
1462
 
 
1463
        if (!logfile || !FS_Initialized())
 
1464
                return;
 
1465
        for (block = hunkblocks ; block; block = block->next) {
 
1466
                block->printed = qfalse;
 
1467
        }
 
1468
        size = 0;
 
1469
        numBlocks = 0;
 
1470
        Com_sprintf(buf, sizeof(buf), "\r\n================\r\nHunk Small log\r\n================\r\n");
 
1471
        FS_Write(buf, strlen(buf), logfile);
 
1472
        for (block = hunkblocks; block; block = block->next) {
 
1473
                if (block->printed) {
 
1474
                        continue;
 
1475
                }
 
1476
                locsize = block->size;
 
1477
                for (block2 = block->next; block2; block2 = block2->next) {
 
1478
                        if (block->line != block2->line) {
 
1479
                                continue;
 
1480
                        }
 
1481
                        if (Q_stricmp(block->file, block2->file)) {
 
1482
                                continue;
 
1483
                        }
 
1484
                        size += block2->size;
 
1485
                        locsize += block2->size;
 
1486
                        block2->printed = qtrue;
 
1487
                }
 
1488
#ifdef HUNK_DEBUG
 
1489
                Com_sprintf(buf, sizeof(buf), "size = %8d: %s, line: %d (%s)\r\n", locsize, block->file, block->line, block->label);
 
1490
                FS_Write(buf, strlen(buf), logfile);
 
1491
#endif
 
1492
                size += block->size;
 
1493
                numBlocks++;
 
1494
        }
 
1495
        Com_sprintf(buf, sizeof(buf), "%d Hunk memory\r\n", size);
 
1496
        FS_Write(buf, strlen(buf), logfile);
 
1497
        Com_sprintf(buf, sizeof(buf), "%d hunk blocks\r\n", numBlocks);
 
1498
        FS_Write(buf, strlen(buf), logfile);
 
1499
}
 
1500
 
 
1501
/*
 
1502
=================
 
1503
Com_InitZoneMemory
 
1504
=================
 
1505
*/
 
1506
void Com_InitHunkMemory( void ) {
 
1507
        cvar_t  *cv;
 
1508
        int nMinAlloc;
 
1509
        char *pMsg = NULL;
 
1510
 
 
1511
        // make sure the file system has allocated and "not" freed any temp blocks
 
1512
        // this allows the config and product id files ( journal files too ) to be loaded
 
1513
        // by the file system without redunant routines in the file system utilizing different 
 
1514
        // memory systems
 
1515
        if (FS_LoadStack() != 0) {
 
1516
                Com_Error( ERR_FATAL, "Hunk initialization failed. File system load stack not zero");
 
1517
        }
 
1518
 
 
1519
        // allocate the stack based hunk allocator
 
1520
        cv = Cvar_Get( "com_hunkMegs", DEF_COMHUNKMEGS_S, CVAR_LATCH | CVAR_ARCHIVE );
 
1521
 
 
1522
        // if we are not dedicated min allocation is 56, otherwise min is 1
 
1523
        if (com_dedicated && com_dedicated->integer) {
 
1524
                nMinAlloc = MIN_DEDICATED_COMHUNKMEGS;
 
1525
                pMsg = "Minimum com_hunkMegs for a dedicated server is %i, allocating %i megs.\n";
 
1526
        }
 
1527
        else {
 
1528
                nMinAlloc = MIN_COMHUNKMEGS;
 
1529
                pMsg = "Minimum com_hunkMegs is %i, allocating %i megs.\n";
 
1530
        }
 
1531
 
 
1532
        if ( cv->integer < nMinAlloc ) {
 
1533
                s_hunkTotal = 1024 * 1024 * nMinAlloc;
 
1534
            Com_Printf(pMsg, nMinAlloc, s_hunkTotal / (1024 * 1024));
 
1535
        } else {
 
1536
                s_hunkTotal = cv->integer * 1024 * 1024;
 
1537
        }
 
1538
 
 
1539
 
 
1540
        // bk001205 - was malloc
 
1541
        s_hunkData = calloc( s_hunkTotal + 31, 1 );
 
1542
        if ( !s_hunkData ) {
 
1543
                Com_Error( ERR_FATAL, "Hunk data failed to allocate %i megs", s_hunkTotal / (1024*1024) );
 
1544
        }
 
1545
        // cacheline align
 
1546
        s_hunkData = (byte *) ( ( (intptr_t)s_hunkData + 31 ) & ~31 );
 
1547
        Hunk_Clear();
 
1548
 
 
1549
        Cmd_AddCommand( "meminfo", Com_Meminfo_f );
 
1550
#ifdef ZONE_DEBUG
 
1551
        Cmd_AddCommand( "zonelog", Z_LogHeap );
 
1552
#endif
 
1553
#ifdef HUNK_DEBUG
 
1554
        Cmd_AddCommand( "hunklog", Hunk_Log );
 
1555
        Cmd_AddCommand( "hunksmalllog", Hunk_SmallLog );
 
1556
#endif
 
1557
}
 
1558
 
 
1559
/*
 
1560
====================
 
1561
Hunk_MemoryRemaining
 
1562
====================
 
1563
*/
 
1564
int     Hunk_MemoryRemaining( void ) {
 
1565
        int             low, high;
 
1566
 
 
1567
        low = hunk_low.permanent > hunk_low.temp ? hunk_low.permanent : hunk_low.temp;
 
1568
        high = hunk_high.permanent > hunk_high.temp ? hunk_high.permanent : hunk_high.temp;
 
1569
 
 
1570
        return s_hunkTotal - ( low + high );
 
1571
}
 
1572
 
 
1573
/*
 
1574
===================
 
1575
Hunk_SetMark
 
1576
 
 
1577
The server calls this after the level and game VM have been loaded
 
1578
===================
 
1579
*/
 
1580
void Hunk_SetMark( void ) {
 
1581
        hunk_low.mark = hunk_low.permanent;
 
1582
        hunk_high.mark = hunk_high.permanent;
 
1583
}
 
1584
 
 
1585
/*
 
1586
=================
 
1587
Hunk_ClearToMark
 
1588
 
 
1589
The client calls this before starting a vid_restart or snd_restart
 
1590
=================
 
1591
*/
 
1592
void Hunk_ClearToMark( void ) {
 
1593
        hunk_low.permanent = hunk_low.temp = hunk_low.mark;
 
1594
        hunk_high.permanent = hunk_high.temp = hunk_high.mark;
 
1595
}
 
1596
 
 
1597
/*
 
1598
=================
 
1599
Hunk_CheckMark
 
1600
=================
 
1601
*/
 
1602
qboolean Hunk_CheckMark( void ) {
 
1603
        if( hunk_low.mark || hunk_high.mark ) {
 
1604
                return qtrue;
 
1605
        }
 
1606
        return qfalse;
 
1607
}
 
1608
 
 
1609
void CL_ShutdownCGame( void );
 
1610
void CL_ShutdownUI( void );
 
1611
void SV_ShutdownGameProgs( void );
 
1612
 
 
1613
/*
 
1614
=================
 
1615
Hunk_Clear
 
1616
 
 
1617
The server calls this before shutting down or loading a new map
 
1618
=================
 
1619
*/
 
1620
void Hunk_Clear( void ) {
 
1621
 
 
1622
#ifndef DEDICATED
 
1623
        CL_ShutdownCGame();
 
1624
        CL_ShutdownUI();
 
1625
#endif
 
1626
        SV_ShutdownGameProgs();
 
1627
#ifndef DEDICATED
 
1628
        CIN_CloseAllVideos();
 
1629
#endif
 
1630
        hunk_low.mark = 0;
 
1631
        hunk_low.permanent = 0;
 
1632
        hunk_low.temp = 0;
 
1633
        hunk_low.tempHighwater = 0;
 
1634
 
 
1635
        hunk_high.mark = 0;
 
1636
        hunk_high.permanent = 0;
 
1637
        hunk_high.temp = 0;
 
1638
        hunk_high.tempHighwater = 0;
 
1639
 
 
1640
        hunk_permanent = &hunk_low;
 
1641
        hunk_temp = &hunk_high;
 
1642
 
 
1643
        Com_Printf( "Hunk_Clear: reset the hunk ok\n" );
 
1644
        VM_Clear();
 
1645
#ifdef HUNK_DEBUG
 
1646
        hunkblocks = NULL;
 
1647
#endif
 
1648
}
 
1649
 
 
1650
static void Hunk_SwapBanks( void ) {
 
1651
        hunkUsed_t      *swap;
 
1652
 
 
1653
        // can't swap banks if there is any temp already allocated
 
1654
        if ( hunk_temp->temp != hunk_temp->permanent ) {
 
1655
                return;
 
1656
        }
 
1657
 
 
1658
        // if we have a larger highwater mark on this side, start making
 
1659
        // our permanent allocations here and use the other side for temp
 
1660
        if ( hunk_temp->tempHighwater - hunk_temp->permanent >
 
1661
                hunk_permanent->tempHighwater - hunk_permanent->permanent ) {
 
1662
                swap = hunk_temp;
 
1663
                hunk_temp = hunk_permanent;
 
1664
                hunk_permanent = swap;
 
1665
        }
 
1666
}
 
1667
 
 
1668
/*
 
1669
=================
 
1670
Hunk_Alloc
 
1671
 
 
1672
Allocate permanent (until the hunk is cleared) memory
 
1673
=================
 
1674
*/
 
1675
#ifdef HUNK_DEBUG
 
1676
void *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line ) {
 
1677
#else
 
1678
void *Hunk_Alloc( int size, ha_pref preference ) {
 
1679
#endif
 
1680
        void    *buf;
 
1681
 
 
1682
        if ( s_hunkData == NULL)
 
1683
        {
 
1684
                Com_Error( ERR_FATAL, "Hunk_Alloc: Hunk memory system not initialized" );
 
1685
        }
 
1686
 
 
1687
        // can't do preference if there is any temp allocated
 
1688
        if (preference == h_dontcare || hunk_temp->temp != hunk_temp->permanent) {
 
1689
                Hunk_SwapBanks();
 
1690
        } else {
 
1691
                if (preference == h_low && hunk_permanent != &hunk_low) {
 
1692
                        Hunk_SwapBanks();
 
1693
                } else if (preference == h_high && hunk_permanent != &hunk_high) {
 
1694
                        Hunk_SwapBanks();
 
1695
                }
 
1696
        }
 
1697
 
 
1698
#ifdef HUNK_DEBUG
 
1699
        size += sizeof(hunkblock_t);
 
1700
#endif
 
1701
 
 
1702
        // round to cacheline
 
1703
        size = (size+31)&~31;
 
1704
 
 
1705
        if ( hunk_low.temp + hunk_high.temp + size > s_hunkTotal ) {
 
1706
#ifdef HUNK_DEBUG
 
1707
                Hunk_Log();
 
1708
                Hunk_SmallLog();
 
1709
#endif
 
1710
                Com_Error( ERR_DROP, "Hunk_Alloc failed on %i", size );
 
1711
        }
 
1712
 
 
1713
        if ( hunk_permanent == &hunk_low ) {
 
1714
                buf = (void *)(s_hunkData + hunk_permanent->permanent);
 
1715
                hunk_permanent->permanent += size;
 
1716
        } else {
 
1717
                hunk_permanent->permanent += size;
 
1718
                buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent );
 
1719
        }
 
1720
 
 
1721
        hunk_permanent->temp = hunk_permanent->permanent;
 
1722
 
 
1723
        Com_Memset( buf, 0, size );
 
1724
 
 
1725
#ifdef HUNK_DEBUG
 
1726
        {
 
1727
                hunkblock_t *block;
 
1728
 
 
1729
                block = (hunkblock_t *) buf;
 
1730
                block->size = size - sizeof(hunkblock_t);
 
1731
                block->file = file;
 
1732
                block->label = label;
 
1733
                block->line = line;
 
1734
                block->next = hunkblocks;
 
1735
                hunkblocks = block;
 
1736
                buf = ((byte *) buf) + sizeof(hunkblock_t);
 
1737
        }
 
1738
#endif
 
1739
        return buf;
 
1740
}
 
1741
 
 
1742
/*
 
1743
=================
 
1744
Hunk_AllocateTempMemory
 
1745
 
 
1746
This is used by the file loading system.
 
1747
Multiple files can be loaded in temporary memory.
 
1748
When the files-in-use count reaches zero, all temp memory will be deleted
 
1749
=================
 
1750
*/
 
1751
void *Hunk_AllocateTempMemory( int size ) {
 
1752
        void            *buf;
 
1753
        hunkHeader_t    *hdr;
 
1754
 
 
1755
        // return a Z_Malloc'd block if the hunk has not been initialized
 
1756
        // this allows the config and product id files ( journal files too ) to be loaded
 
1757
        // by the file system without redunant routines in the file system utilizing different 
 
1758
        // memory systems
 
1759
        if ( s_hunkData == NULL )
 
1760
        {
 
1761
                return Z_Malloc(size);
 
1762
        }
 
1763
 
 
1764
        Hunk_SwapBanks();
 
1765
 
 
1766
        size = PAD(size, sizeof(intptr_t)) + sizeof( hunkHeader_t );
 
1767
 
 
1768
        if ( hunk_temp->temp + hunk_permanent->permanent + size > s_hunkTotal ) {
 
1769
                Com_Error( ERR_DROP, "Hunk_AllocateTempMemory: failed on %i", size );
 
1770
        }
 
1771
 
 
1772
        if ( hunk_temp == &hunk_low ) {
 
1773
                buf = (void *)(s_hunkData + hunk_temp->temp);
 
1774
                hunk_temp->temp += size;
 
1775
        } else {
 
1776
                hunk_temp->temp += size;
 
1777
                buf = (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp );
 
1778
        }
 
1779
 
 
1780
        if ( hunk_temp->temp > hunk_temp->tempHighwater ) {
 
1781
                hunk_temp->tempHighwater = hunk_temp->temp;
 
1782
        }
 
1783
 
 
1784
        hdr = (hunkHeader_t *)buf;
 
1785
        buf = (void *)(hdr+1);
 
1786
 
 
1787
        hdr->magic = HUNK_MAGIC;
 
1788
        hdr->size = size;
 
1789
 
 
1790
        // don't bother clearing, because we are going to load a file over it
 
1791
        return buf;
 
1792
}
 
1793
 
 
1794
 
 
1795
/*
 
1796
==================
 
1797
Hunk_FreeTempMemory
 
1798
==================
 
1799
*/
 
1800
void Hunk_FreeTempMemory( void *buf ) {
 
1801
        hunkHeader_t    *hdr;
 
1802
 
 
1803
          // free with Z_Free if the hunk has not been initialized
 
1804
          // this allows the config and product id files ( journal files too ) to be loaded
 
1805
          // by the file system without redunant routines in the file system utilizing different 
 
1806
          // memory systems
 
1807
        if ( s_hunkData == NULL )
 
1808
        {
 
1809
                Z_Free(buf);
 
1810
                return;
 
1811
        }
 
1812
 
 
1813
 
 
1814
        hdr = ( (hunkHeader_t *)buf ) - 1;
 
1815
        if ( hdr->magic != HUNK_MAGIC ) {
 
1816
                Com_Error( ERR_FATAL, "Hunk_FreeTempMemory: bad magic" );
 
1817
        }
 
1818
 
 
1819
        hdr->magic = HUNK_FREE_MAGIC;
 
1820
 
 
1821
        // this only works if the files are freed in stack order,
 
1822
        // otherwise the memory will stay around until Hunk_ClearTempMemory
 
1823
        if ( hunk_temp == &hunk_low ) {
 
1824
                if ( hdr == (void *)(s_hunkData + hunk_temp->temp - hdr->size ) ) {
 
1825
                        hunk_temp->temp -= hdr->size;
 
1826
                } else {
 
1827
                        Com_Printf( "Hunk_FreeTempMemory: not the final block\n" );
 
1828
                }
 
1829
        } else {
 
1830
                if ( hdr == (void *)(s_hunkData + s_hunkTotal - hunk_temp->temp ) ) {
 
1831
                        hunk_temp->temp -= hdr->size;
 
1832
                } else {
 
1833
                        Com_Printf( "Hunk_FreeTempMemory: not the final block\n" );
 
1834
                }
 
1835
        }
 
1836
}
 
1837
 
 
1838
 
 
1839
/*
 
1840
=================
 
1841
Hunk_ClearTempMemory
 
1842
 
 
1843
The temp space is no longer needed.  If we have left more
 
1844
touched but unused memory on this side, have future
 
1845
permanent allocs use this side.
 
1846
=================
 
1847
*/
 
1848
void Hunk_ClearTempMemory( void ) {
 
1849
        if ( s_hunkData != NULL ) {
 
1850
                hunk_temp->temp = hunk_temp->permanent;
 
1851
        }
 
1852
}
 
1853
 
 
1854
/*
 
1855
=================
 
1856
Hunk_Trash
 
1857
=================
 
1858
*/
 
1859
void Hunk_Trash( void ) {
 
1860
        int length, i, rnd;
 
1861
        char *buf, value;
 
1862
 
 
1863
        return;
 
1864
 
 
1865
        if ( s_hunkData == NULL )
 
1866
                return;
 
1867
 
 
1868
#ifdef _DEBUG
 
1869
        Com_Error(ERR_DROP, "hunk trashed\n");
 
1870
        return;
 
1871
#endif
 
1872
 
 
1873
        Cvar_Set("com_jp", "1");
 
1874
        Hunk_SwapBanks();
 
1875
 
 
1876
        if ( hunk_permanent == &hunk_low ) {
 
1877
                buf = (void *)(s_hunkData + hunk_permanent->permanent);
 
1878
        } else {
 
1879
                buf = (void *)(s_hunkData + s_hunkTotal - hunk_permanent->permanent );
 
1880
        }
 
1881
        length = hunk_permanent->permanent;
 
1882
 
 
1883
        if (length > 0x7FFFF) {
 
1884
                //randomly trash data within buf
 
1885
                rnd = random() * (length - 0x7FFFF);
 
1886
                value = 31;
 
1887
                for (i = 0; i < 0x7FFFF; i++) {
 
1888
                        value *= 109;
 
1889
                        buf[rnd+i] ^= value;
 
1890
                }
 
1891
        }
 
1892
}
 
1893
 
 
1894
/*
 
1895
===================================================================
 
1896
 
 
1897
EVENTS AND JOURNALING
 
1898
 
 
1899
In addition to these events, .cfg files are also copied to the
 
1900
journaled file
 
1901
===================================================================
 
1902
*/
 
1903
 
 
1904
// bk001129 - here we go again: upped from 64
 
1905
// FIXME TTimo blunt upping from 256 to 1024
 
1906
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=5
 
1907
#define MAX_PUSHED_EVENTS                   1024
 
1908
// bk001129 - init, also static
 
1909
static int com_pushedEventsHead = 0;
 
1910
static int com_pushedEventsTail = 0;
 
1911
// bk001129 - static
 
1912
static sysEvent_t       com_pushedEvents[MAX_PUSHED_EVENTS];
 
1913
 
 
1914
/*
 
1915
=================
 
1916
Com_InitJournaling
 
1917
=================
 
1918
*/
 
1919
void Com_InitJournaling( void ) {
 
1920
        Com_StartupVariable( "journal" );
 
1921
        com_journal = Cvar_Get ("journal", "0", CVAR_INIT);
 
1922
        if ( !com_journal->integer ) {
 
1923
                return;
 
1924
        }
 
1925
 
 
1926
        if ( com_journal->integer == 1 ) {
 
1927
                Com_Printf( "Journaling events\n");
 
1928
                com_journalFile = FS_FOpenFileWrite( "journal.dat" );
 
1929
                com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" );
 
1930
        } else if ( com_journal->integer == 2 ) {
 
1931
                Com_Printf( "Replaying journaled events\n");
 
1932
                FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue );
 
1933
                FS_FOpenFileRead( "journaldata.dat", &com_journalDataFile, qtrue );
 
1934
        }
 
1935
 
 
1936
        if ( !com_journalFile || !com_journalDataFile ) {
 
1937
                Cvar_Set( "com_journal", "0" );
 
1938
                com_journalFile = 0;
 
1939
                com_journalDataFile = 0;
 
1940
                Com_Printf( "Couldn't open journal files\n" );
 
1941
        }
 
1942
}
 
1943
 
 
1944
/*
 
1945
=================
 
1946
Com_GetRealEvent
 
1947
=================
 
1948
*/
 
1949
sysEvent_t      Com_GetRealEvent( void ) {
 
1950
        int                     r;
 
1951
        sysEvent_t      ev;
 
1952
 
 
1953
        // either get an event from the system or the journal file
 
1954
        if ( com_journal->integer == 2 ) {
 
1955
                r = FS_Read( &ev, sizeof(ev), com_journalFile );
 
1956
                if ( r != sizeof(ev) ) {
 
1957
                        Com_Error( ERR_FATAL, "Error reading from journal file" );
 
1958
                }
 
1959
                if ( ev.evPtrLength ) {
 
1960
                        ev.evPtr = Z_Malloc( ev.evPtrLength );
 
1961
                        r = FS_Read( ev.evPtr, ev.evPtrLength, com_journalFile );
 
1962
                        if ( r != ev.evPtrLength ) {
 
1963
                                Com_Error( ERR_FATAL, "Error reading from journal file" );
 
1964
                        }
 
1965
                }
 
1966
        } else {
 
1967
                ev = Sys_GetEvent();
 
1968
 
 
1969
                // write the journal value out if needed
 
1970
                if ( com_journal->integer == 1 ) {
 
1971
                        r = FS_Write( &ev, sizeof(ev), com_journalFile );
 
1972
                        if ( r != sizeof(ev) ) {
 
1973
                                Com_Error( ERR_FATAL, "Error writing to journal file" );
 
1974
                        }
 
1975
                        if ( ev.evPtrLength ) {
 
1976
                                r = FS_Write( ev.evPtr, ev.evPtrLength, com_journalFile );
 
1977
                                if ( r != ev.evPtrLength ) {
 
1978
                                        Com_Error( ERR_FATAL, "Error writing to journal file" );
 
1979
                                }
 
1980
                        }
 
1981
                }
 
1982
        }
 
1983
 
 
1984
        return ev;
 
1985
}
 
1986
 
 
1987
 
 
1988
/*
 
1989
=================
 
1990
Com_InitPushEvent
 
1991
=================
 
1992
*/
 
1993
// bk001129 - added
 
1994
void Com_InitPushEvent( void ) {
 
1995
  // clear the static buffer array
 
1996
  // this requires SE_NONE to be accepted as a valid but NOP event
 
1997
  memset( com_pushedEvents, 0, sizeof(com_pushedEvents) );
 
1998
  // reset counters while we are at it
 
1999
  // beware: GetEvent might still return an SE_NONE from the buffer
 
2000
  com_pushedEventsHead = 0;
 
2001
  com_pushedEventsTail = 0;
 
2002
}
 
2003
 
 
2004
 
 
2005
/*
 
2006
=================
 
2007
Com_PushEvent
 
2008
=================
 
2009
*/
 
2010
void Com_PushEvent( sysEvent_t *event ) {
 
2011
        sysEvent_t              *ev;
 
2012
        static int printedWarning = 0; // bk001129 - init, bk001204 - explicit int
 
2013
 
 
2014
        ev = &com_pushedEvents[ com_pushedEventsHead & (MAX_PUSHED_EVENTS-1) ];
 
2015
 
 
2016
        if ( com_pushedEventsHead - com_pushedEventsTail >= MAX_PUSHED_EVENTS ) {
 
2017
 
 
2018
                // don't print the warning constantly, or it can give time for more...
 
2019
                if ( !printedWarning ) {
 
2020
                        printedWarning = qtrue;
 
2021
                        Com_Printf( "WARNING: Com_PushEvent overflow\n" );
 
2022
                }
 
2023
 
 
2024
                if ( ev->evPtr ) {
 
2025
                        Z_Free( ev->evPtr );
 
2026
                }
 
2027
                com_pushedEventsTail++;
 
2028
        } else {
 
2029
                printedWarning = qfalse;
 
2030
        }
 
2031
 
 
2032
        *ev = *event;
 
2033
        com_pushedEventsHead++;
 
2034
}
 
2035
 
 
2036
/*
 
2037
=================
 
2038
Com_GetEvent
 
2039
=================
 
2040
*/
 
2041
sysEvent_t      Com_GetEvent( void ) {
 
2042
        if ( com_pushedEventsHead > com_pushedEventsTail ) {
 
2043
                com_pushedEventsTail++;
 
2044
                return com_pushedEvents[ (com_pushedEventsTail-1) & (MAX_PUSHED_EVENTS-1) ];
 
2045
        }
 
2046
        return Com_GetRealEvent();
 
2047
}
 
2048
 
 
2049
/*
 
2050
=================
 
2051
Com_RunAndTimeServerPacket
 
2052
=================
 
2053
*/
 
2054
void Com_RunAndTimeServerPacket( netadr_t *evFrom, msg_t *buf ) {
 
2055
        int             t1, t2, msec;
 
2056
 
 
2057
        t1 = 0;
 
2058
 
 
2059
        if ( com_speeds->integer ) {
 
2060
                t1 = Sys_Milliseconds ();
 
2061
        }
 
2062
 
 
2063
        SV_PacketEvent( *evFrom, buf );
 
2064
 
 
2065
        if ( com_speeds->integer ) {
 
2066
                t2 = Sys_Milliseconds ();
 
2067
                msec = t2 - t1;
 
2068
                if ( com_speeds->integer == 3 ) {
 
2069
                        Com_Printf( "SV_PacketEvent time: %i\n", msec );
 
2070
                }
 
2071
        }
 
2072
}
 
2073
 
 
2074
/*
 
2075
=================
 
2076
Com_EventLoop
 
2077
 
 
2078
Returns last event time
 
2079
=================
 
2080
*/
 
2081
int Com_EventLoop( void ) {
 
2082
        sysEvent_t      ev;
 
2083
        netadr_t        evFrom;
 
2084
        byte            bufData[MAX_MSGLEN];
 
2085
        msg_t           buf;
 
2086
 
 
2087
        MSG_Init( &buf, bufData, sizeof( bufData ) );
 
2088
 
 
2089
        while ( 1 ) {
 
2090
                NET_FlushPacketQueue();
 
2091
                ev = Com_GetEvent();
 
2092
 
 
2093
                // if no more events are available
 
2094
                if ( ev.evType == SE_NONE ) {
 
2095
                        // manually send packet events for the loopback channel
 
2096
                        while ( NET_GetLoopPacket( NS_CLIENT, &evFrom, &buf ) ) {
 
2097
                                CL_PacketEvent( evFrom, &buf );
 
2098
                        }
 
2099
 
 
2100
                        while ( NET_GetLoopPacket( NS_SERVER, &evFrom, &buf ) ) {
 
2101
                                // if the server just shut down, flush the events
 
2102
                                if ( com_sv_running->integer ) {
 
2103
                                        Com_RunAndTimeServerPacket( &evFrom, &buf );
 
2104
                                }
 
2105
                        }
 
2106
 
 
2107
                        return ev.evTime;
 
2108
                }
 
2109
 
 
2110
 
 
2111
                switch ( ev.evType ) {
 
2112
                default:
 
2113
                  // bk001129 - was ev.evTime
 
2114
                        Com_Error( ERR_FATAL, "Com_EventLoop: bad event type %i", ev.evType );
 
2115
                        break;
 
2116
        case SE_NONE:
 
2117
            break;
 
2118
                case SE_KEY:
 
2119
                        CL_KeyEvent( ev.evValue, ev.evValue2, ev.evTime );
 
2120
                        break;
 
2121
                case SE_CHAR:
 
2122
                        CL_CharEvent( ev.evValue );
 
2123
                        break;
 
2124
                case SE_MOUSE:
 
2125
                        CL_MouseEvent( ev.evValue, ev.evValue2, ev.evTime );
 
2126
                        break;
 
2127
                case SE_JOYSTICK_AXIS:
 
2128
                        CL_JoystickEvent( ev.evValue, ev.evValue2, ev.evTime );
 
2129
                        break;
 
2130
                case SE_CONSOLE:
 
2131
                        Cbuf_AddText( (char *)ev.evPtr );
 
2132
                        Cbuf_AddText( "\n" );
 
2133
                        break;
 
2134
                case SE_PACKET:
 
2135
                        // this cvar allows simulation of connections that
 
2136
                        // drop a lot of packets.  Note that loopback connections
 
2137
                        // don't go through here at all.
 
2138
                        if ( com_dropsim->value > 0 ) {
 
2139
                                static int seed;
 
2140
 
 
2141
                                if ( Q_random( &seed ) < com_dropsim->value ) {
 
2142
                                        break;          // drop this packet
 
2143
                                }
 
2144
                        }
 
2145
 
 
2146
                        evFrom = *(netadr_t *)ev.evPtr;
 
2147
                        buf.cursize = ev.evPtrLength - sizeof( evFrom );
 
2148
 
 
2149
                        // we must copy the contents of the message out, because
 
2150
                        // the event buffers are only large enough to hold the
 
2151
                        // exact payload, but channel messages need to be large
 
2152
                        // enough to hold fragment reassembly
 
2153
                        if ( (unsigned)buf.cursize > buf.maxsize ) {
 
2154
                                Com_Printf("Com_EventLoop: oversize packet\n");
 
2155
                                continue;
 
2156
                        }
 
2157
                        Com_Memcpy( buf.data, (byte *)((netadr_t *)ev.evPtr + 1), buf.cursize );
 
2158
                        if ( com_sv_running->integer ) {
 
2159
                                Com_RunAndTimeServerPacket( &evFrom, &buf );
 
2160
                        } else {
 
2161
                                CL_PacketEvent( evFrom, &buf );
 
2162
                        }
 
2163
                        break;
 
2164
                }
 
2165
 
 
2166
                // free any block data
 
2167
                if ( ev.evPtr ) {
 
2168
                        Z_Free( ev.evPtr );
 
2169
                }
 
2170
        }
 
2171
 
 
2172
        return 0;       // never reached
 
2173
}
 
2174
 
 
2175
/*
 
2176
================
 
2177
Com_Milliseconds
 
2178
 
 
2179
Can be used for profiling, but will be journaled accurately
 
2180
================
 
2181
*/
 
2182
int Com_Milliseconds (void) {
 
2183
        sysEvent_t      ev;
 
2184
 
 
2185
        // get events and push them until we get a null event with the current time
 
2186
        do {
 
2187
 
 
2188
                ev = Com_GetRealEvent();
 
2189
                if ( ev.evType != SE_NONE ) {
 
2190
                        Com_PushEvent( &ev );
 
2191
                }
 
2192
        } while ( ev.evType != SE_NONE );
 
2193
        
 
2194
        return ev.evTime;
 
2195
}
 
2196
 
 
2197
//============================================================================
 
2198
 
 
2199
/*
 
2200
=============
 
2201
Com_Error_f
 
2202
 
 
2203
Just throw a fatal error to
 
2204
test error shutdown procedures
 
2205
=============
 
2206
*/
 
2207
static void Com_Error_f (void) {
 
2208
        if ( Cmd_Argc() > 1 ) {
 
2209
                Com_Error( ERR_DROP, "Testing drop error" );
 
2210
        } else {
 
2211
                Com_Error( ERR_FATAL, "Testing fatal error" );
 
2212
        }
 
2213
}
 
2214
 
 
2215
 
 
2216
/*
 
2217
=============
 
2218
Com_Freeze_f
 
2219
 
 
2220
Just freeze in place for a given number of seconds to test
 
2221
error recovery
 
2222
=============
 
2223
*/
 
2224
static void Com_Freeze_f (void) {
 
2225
        float   s;
 
2226
        int             start, now;
 
2227
 
 
2228
        if ( Cmd_Argc() != 2 ) {
 
2229
                Com_Printf( "freeze <seconds>\n" );
 
2230
                return;
 
2231
        }
 
2232
        s = atof( Cmd_Argv(1) );
 
2233
 
 
2234
        start = Com_Milliseconds();
 
2235
 
 
2236
        while ( 1 ) {
 
2237
                now = Com_Milliseconds();
 
2238
                if ( ( now - start ) * 0.001 > s ) {
 
2239
                        break;
 
2240
                }
 
2241
        }
 
2242
}
 
2243
 
 
2244
/*
 
2245
=================
 
2246
Com_Crash_f
 
2247
 
 
2248
A way to force a bus error for development reasons
 
2249
=================
 
2250
*/
 
2251
static void Com_Crash_f( void ) {
 
2252
        * ( int * ) 0 = 0x12345678;
 
2253
}
 
2254
 
 
2255
// TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version
 
2256
//   not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it
 
2257
//   https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470
 
2258
#ifndef DEDICATED
 
2259
char    cl_cdkey[34] = "                                ";
 
2260
#else
 
2261
char    cl_cdkey[34] = "123456789";
 
2262
#endif
 
2263
 
 
2264
/*
 
2265
=================
 
2266
Com_ReadCDKey
 
2267
=================
 
2268
*/
 
2269
qboolean CL_CDKeyValidate( const char *key, const char *checksum );
 
2270
void Com_ReadCDKey( const char *filename ) {
 
2271
        fileHandle_t    f;
 
2272
        char                    buffer[33];
 
2273
        char                    fbuffer[MAX_OSPATH];
 
2274
 
 
2275
        sprintf(fbuffer, "%s/q3key", filename);
 
2276
 
 
2277
        FS_SV_FOpenFileRead( fbuffer, &f );
 
2278
        if ( !f ) {
 
2279
                Q_strncpyz( cl_cdkey, "                ", 17 );
 
2280
                return;
 
2281
        }
 
2282
 
 
2283
        Com_Memset( buffer, 0, sizeof(buffer) );
 
2284
 
 
2285
        FS_Read( buffer, 16, f );
 
2286
        FS_FCloseFile( f );
 
2287
 
 
2288
        if (CL_CDKeyValidate(buffer, NULL)) {
 
2289
                Q_strncpyz( cl_cdkey, buffer, 17 );
 
2290
        } else {
 
2291
                Q_strncpyz( cl_cdkey, "                ", 17 );
 
2292
        }
 
2293
}
 
2294
 
 
2295
/*
 
2296
=================
 
2297
Com_AppendCDKey
 
2298
=================
 
2299
*/
 
2300
void Com_AppendCDKey( const char *filename ) {
 
2301
        fileHandle_t    f;
 
2302
        char                    buffer[33];
 
2303
        char                    fbuffer[MAX_OSPATH];
 
2304
 
 
2305
        sprintf(fbuffer, "%s/q3key", filename);
 
2306
 
 
2307
        FS_SV_FOpenFileRead( fbuffer, &f );
 
2308
        if (!f) {
 
2309
                Q_strncpyz( &cl_cdkey[16], "                ", 17 );
 
2310
                return;
 
2311
        }
 
2312
 
 
2313
        Com_Memset( buffer, 0, sizeof(buffer) );
 
2314
 
 
2315
        FS_Read( buffer, 16, f );
 
2316
        FS_FCloseFile( f );
 
2317
 
 
2318
        if (CL_CDKeyValidate(buffer, NULL)) {
 
2319
                strcat( &cl_cdkey[16], buffer );
 
2320
        } else {
 
2321
                Q_strncpyz( &cl_cdkey[16], "                ", 17 );
 
2322
        }
 
2323
}
 
2324
 
 
2325
#ifndef DEDICATED // bk001204
 
2326
/*
 
2327
=================
 
2328
Com_WriteCDKey
 
2329
=================
 
2330
*/
 
2331
static void Com_WriteCDKey( const char *filename, const char *ikey ) {
 
2332
        fileHandle_t    f;
 
2333
        char                    fbuffer[MAX_OSPATH];
 
2334
        char                    key[17];
 
2335
#ifndef _WIN32
 
2336
        mode_t                  savedumask;
 
2337
#endif
 
2338
 
 
2339
 
 
2340
        sprintf(fbuffer, "%s/q3key", filename);
 
2341
 
 
2342
 
 
2343
        Q_strncpyz( key, ikey, 17 );
 
2344
 
 
2345
        if(!CL_CDKeyValidate(key, NULL) ) {
 
2346
                return;
 
2347
        }
 
2348
 
 
2349
#ifndef _WIN32
 
2350
        savedumask = umask(0077);
 
2351
#endif
 
2352
        f = FS_SV_FOpenFileWrite( fbuffer );
 
2353
        if ( !f ) {
 
2354
                Com_Printf ("Couldn't write CD key to %s.\n", fbuffer );
 
2355
                goto out;
 
2356
        }
 
2357
 
 
2358
        FS_Write( key, 16, f );
 
2359
 
 
2360
        FS_Printf( f, "\n// generated by quake, do not modify\r\n" );
 
2361
        FS_Printf( f, "// Do not give this file to ANYONE.\r\n" );
 
2362
        FS_Printf( f, "// id Software and Activision will NOT ask you to send this file to them.\r\n");
 
2363
 
 
2364
        FS_FCloseFile( f );
 
2365
out:
 
2366
#ifndef _WIN32
 
2367
        umask(savedumask);
 
2368
#endif
 
2369
        return;
 
2370
}
 
2371
#endif
 
2372
 
 
2373
 
 
2374
static void Com_DetectAltivec(void)
 
2375
{
 
2376
        // Only detect if user hasn't forcibly disabled it.
 
2377
        if (com_altivec->integer) {
 
2378
                static qboolean altivec = qfalse;
 
2379
                static qboolean detected = qfalse;
 
2380
                if (!detected) {
 
2381
                        altivec = Sys_DetectAltivec();
 
2382
                        detected = qtrue;
 
2383
                }
 
2384
 
 
2385
                if (!altivec) {
 
2386
                        Cvar_Set( "com_altivec", "0" );  // we don't have it! Disable support!
 
2387
                }
 
2388
        }
 
2389
}
 
2390
 
 
2391
 
 
2392
/*
 
2393
=================
 
2394
Com_Init
 
2395
=================
 
2396
*/
 
2397
void Com_Init( char *commandLine ) {
 
2398
        char    *s;
 
2399
 
 
2400
        Com_Printf( "%s %s %s\n", SVN_VERSION, PLATFORM_STRING, __DATE__ );
 
2401
 
 
2402
        if ( setjmp (abortframe) ) {
 
2403
                Sys_Error ("Error during initialization");
 
2404
        }
 
2405
 
 
2406
  // bk001129 - do this before anything else decides to push events
 
2407
  Com_InitPushEvent();
 
2408
 
 
2409
        Com_InitSmallZoneMemory();
 
2410
        Cvar_Init ();
 
2411
 
 
2412
        // prepare enough of the subsystems to handle
 
2413
        // cvar and command buffer management
 
2414
        Com_ParseCommandLine( commandLine );
 
2415
 
 
2416
//      Swap_Init ();
 
2417
        Cbuf_Init ();
 
2418
 
 
2419
        Com_InitZoneMemory();
 
2420
        Cmd_Init ();
 
2421
 
 
2422
        // override anything from the config files with command line args
 
2423
        Com_StartupVariable( NULL );
 
2424
 
 
2425
        // get the developer cvar set as early as possible
 
2426
        Com_StartupVariable( "developer" );
 
2427
 
 
2428
        // done early so bind command exists
 
2429
        CL_InitKeyCommands();
 
2430
 
 
2431
        FS_InitFilesystem ();
 
2432
 
 
2433
        Com_InitJournaling();
 
2434
 
 
2435
        Cbuf_AddText ("exec default.cfg\n");
 
2436
 
 
2437
        // skip the q3config.cfg if "safe" is on the command line
 
2438
        if ( !Com_SafeMode() ) {
 
2439
                Cbuf_AddText ("exec q3config.cfg\n");
 
2440
        }
 
2441
 
 
2442
        Cbuf_AddText ("exec autoexec.cfg\n");
 
2443
 
 
2444
        Cbuf_Execute ();
 
2445
 
 
2446
        // override anything from the config files with command line args
 
2447
        Com_StartupVariable( NULL );
 
2448
 
 
2449
  // get dedicated here for proper hunk megs initialization
 
2450
#ifdef DEDICATED
 
2451
        com_dedicated = Cvar_Get ("dedicated", "1", CVAR_ROM);
 
2452
#else
 
2453
        com_dedicated = Cvar_Get ("dedicated", "0", CVAR_LATCH);
 
2454
#endif
 
2455
        // allocate the stack based hunk allocator
 
2456
        Com_InitHunkMemory();
 
2457
 
 
2458
        // if any archived cvars are modified after this, we will trigger a writing
 
2459
        // of the config file
 
2460
        cvar_modifiedFlags &= ~CVAR_ARCHIVE;
 
2461
 
 
2462
        //
 
2463
        // init commands and vars
 
2464
        //
 
2465
        com_altivec = Cvar_Get ("com_altivec", "1", CVAR_ARCHIVE);
 
2466
        com_maxfps = Cvar_Get ("com_maxfps", "85", CVAR_ARCHIVE);
 
2467
        com_blood = Cvar_Get ("com_blood", "1", CVAR_ARCHIVE);
 
2468
 
 
2469
        com_developer = Cvar_Get ("developer", "0", CVAR_TEMP );
 
2470
        com_logfile = Cvar_Get ("logfile", "0", CVAR_TEMP );
 
2471
 
 
2472
        com_timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT | CVAR_SYSTEMINFO );
 
2473
        com_fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT);
 
2474
        com_showtrace = Cvar_Get ("com_showtrace", "0", CVAR_CHEAT);
 
2475
        com_dropsim = Cvar_Get ("com_dropsim", "0", CVAR_CHEAT);
 
2476
        com_viewlog = Cvar_Get( "viewlog", "0", CVAR_CHEAT );
 
2477
        com_speeds = Cvar_Get ("com_speeds", "0", 0);
 
2478
        com_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT);
 
2479
        com_cameraMode = Cvar_Get ("com_cameraMode", "0", CVAR_CHEAT);
 
2480
 
 
2481
        cl_paused = Cvar_Get ("cl_paused", "0", CVAR_ROM);
 
2482
        sv_paused = Cvar_Get ("sv_paused", "0", CVAR_ROM);
 
2483
        cl_packetdelay = Cvar_Get ("cl_packetdelay", "0", CVAR_CHEAT);
 
2484
        sv_packetdelay = Cvar_Get ("sv_packetdelay", "0", CVAR_CHEAT);
 
2485
        com_sv_running = Cvar_Get ("sv_running", "0", CVAR_ROM);
 
2486
        com_cl_running = Cvar_Get ("cl_running", "0", CVAR_ROM);
 
2487
        com_buildScript = Cvar_Get( "com_buildScript", "0", 0 );
 
2488
 
 
2489
        com_introPlayed = Cvar_Get( "com_introplayed", "0", CVAR_ARCHIVE);
 
2490
 
 
2491
#if defined(_WIN32) && defined(_DEBUG)
 
2492
        com_noErrorInterrupt = Cvar_Get( "com_noErrorInterrupt", "0", 0 );
 
2493
#endif
 
2494
 
 
2495
        if ( com_dedicated->integer ) {
 
2496
                if ( !com_viewlog->integer ) {
 
2497
                        Cvar_Set( "viewlog", "1" );
 
2498
                }
 
2499
        }
 
2500
 
 
2501
        if ( com_developer && com_developer->integer ) {
 
2502
                Cmd_AddCommand ("error", Com_Error_f);
 
2503
                Cmd_AddCommand ("crash", Com_Crash_f );
 
2504
                Cmd_AddCommand ("freeze", Com_Freeze_f);
 
2505
        }
 
2506
        Cmd_AddCommand ("quit", Com_Quit_f);
 
2507
        Cmd_AddCommand ("changeVectors", MSG_ReportChangeVectors_f );
 
2508
        Cmd_AddCommand ("writeconfig", Com_WriteConfig_f );
 
2509
 
 
2510
        s = va("%s %s %s", Q3_VERSION, PLATFORM_STRING, __DATE__ );
 
2511
        com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO );
 
2512
 
 
2513
        Sys_Init();
 
2514
        Netchan_Init( Com_Milliseconds() & 0xffff );    // pick a port value that should be nice and random
 
2515
        VM_Init();
 
2516
        SV_Init();
 
2517
 
 
2518
        com_dedicated->modified = qfalse;
 
2519
        if ( !com_dedicated->integer ) {
 
2520
                CL_Init();
 
2521
                Sys_ShowConsole( com_viewlog->integer, qfalse );
 
2522
        }
 
2523
 
 
2524
        // set com_frameTime so that if a map is started on the
 
2525
        // command line it will still be able to count on com_frameTime
 
2526
        // being random enough for a serverid
 
2527
        com_frameTime = Com_Milliseconds();
 
2528
 
 
2529
        // add + commands from command line
 
2530
        if ( !Com_AddStartupCommands() ) {
 
2531
                // if the user didn't give any commands, run default action
 
2532
                if ( !com_dedicated->integer ) {
 
2533
                        Cbuf_AddText ("cinematic idlogo.RoQ\n");
 
2534
                        if( !com_introPlayed->integer ) {
 
2535
                                Cvar_Set( com_introPlayed->name, "1" );
 
2536
                                Cvar_Set( "nextmap", "cinematic intro.RoQ" );
 
2537
                        }
 
2538
                }
 
2539
        }
 
2540
 
 
2541
        // start in full screen ui mode
 
2542
        Cvar_Set("r_uiFullScreen", "1");
 
2543
 
 
2544
        CL_StartHunkUsers();
 
2545
 
 
2546
        // make sure single player is off by default
 
2547
        Cvar_Set("ui_singlePlayerActive", "0");
 
2548
 
 
2549
        com_fullyInitialized = qtrue;
 
2550
 
 
2551
        // always set the cvar, but only print the info if it makes sense.
 
2552
        Com_DetectAltivec();
 
2553
        #if idppc
 
2554
        Com_Printf ("Altivec support is %s\n", com_altivec->integer ? "enabled" : "disabled");
 
2555
        #endif
 
2556
 
 
2557
        Com_Printf ("--- Common Initialization Complete ---\n");
 
2558
}
 
2559
 
 
2560
//==================================================================
 
2561
 
 
2562
void Com_WriteConfigToFile( const char *filename ) {
 
2563
        fileHandle_t    f;
 
2564
 
 
2565
        f = FS_FOpenFileWrite( filename );
 
2566
        if ( !f ) {
 
2567
                Com_Printf ("Couldn't write %s.\n", filename );
 
2568
                return;
 
2569
        }
 
2570
 
 
2571
        FS_Printf (f, "// generated by quake, do not modify\n");
 
2572
        Key_WriteBindings (f);
 
2573
        Cvar_WriteVariables (f);
 
2574
        FS_FCloseFile( f );
 
2575
}
 
2576
 
 
2577
 
 
2578
/*
 
2579
===============
 
2580
Com_WriteConfiguration
 
2581
 
 
2582
Writes key bindings and archived cvars to config file if modified
 
2583
===============
 
2584
*/
 
2585
void Com_WriteConfiguration( void ) {
 
2586
#ifndef DEDICATED // bk001204
 
2587
        cvar_t  *fs;
 
2588
#endif
 
2589
        // if we are quiting without fully initializing, make sure
 
2590
        // we don't write out anything
 
2591
        if ( !com_fullyInitialized ) {
 
2592
                return;
 
2593
        }
 
2594
 
 
2595
        if ( !(cvar_modifiedFlags & CVAR_ARCHIVE ) ) {
 
2596
                return;
 
2597
        }
 
2598
        cvar_modifiedFlags &= ~CVAR_ARCHIVE;
 
2599
 
 
2600
        Com_WriteConfigToFile( "q3config.cfg" );
 
2601
 
 
2602
        // bk001119 - tentative "not needed for dedicated"
 
2603
#ifndef DEDICATED
 
2604
        fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
 
2605
        if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) {
 
2606
                Com_WriteCDKey( fs->string, &cl_cdkey[16] );
 
2607
        } else {
 
2608
                Com_WriteCDKey( BASEGAME, cl_cdkey );
 
2609
        }
 
2610
#endif
 
2611
}
 
2612
 
 
2613
 
 
2614
/*
 
2615
===============
 
2616
Com_WriteConfig_f
 
2617
 
 
2618
Write the config file to a specific name
 
2619
===============
 
2620
*/
 
2621
void Com_WriteConfig_f( void ) {
 
2622
        char    filename[MAX_QPATH];
 
2623
 
 
2624
        if ( Cmd_Argc() != 2 ) {
 
2625
                Com_Printf( "Usage: writeconfig <filename>\n" );
 
2626
                return;
 
2627
        }
 
2628
 
 
2629
        Q_strncpyz( filename, Cmd_Argv(1), sizeof( filename ) );
 
2630
        COM_DefaultExtension( filename, sizeof( filename ), ".cfg" );
 
2631
        Com_Printf( "Writing %s.\n", filename );
 
2632
        Com_WriteConfigToFile( filename );
 
2633
}
 
2634
 
 
2635
/*
 
2636
================
 
2637
Com_ModifyMsec
 
2638
================
 
2639
*/
 
2640
int Com_ModifyMsec( int msec ) {
 
2641
        int             clampTime;
 
2642
 
 
2643
        //
 
2644
        // modify time for debugging values
 
2645
        //
 
2646
        if ( com_fixedtime->integer ) {
 
2647
                msec = com_fixedtime->integer;
 
2648
        } else if ( com_timescale->value ) {
 
2649
                msec *= com_timescale->value;
 
2650
        } else if (com_cameraMode->integer) {
 
2651
                msec *= com_timescale->value;
 
2652
        }
 
2653
        
 
2654
        // don't let it scale below 1 msec
 
2655
        if ( msec < 1 && com_timescale->value) {
 
2656
                msec = 1;
 
2657
        }
 
2658
 
 
2659
        if ( com_dedicated->integer ) {
 
2660
                // dedicated servers don't want to clamp for a much longer
 
2661
                // period, because it would mess up all the client's views
 
2662
                // of time.
 
2663
                if (com_sv_running->integer && msec > 500)
 
2664
                        Com_Printf( "Hitch warning: %i msec frame time\n", msec );
 
2665
 
 
2666
                clampTime = 5000;
 
2667
        } else 
 
2668
        if ( !com_sv_running->integer ) {
 
2669
                // clients of remote servers do not want to clamp time, because
 
2670
                // it would skew their view of the server's time temporarily
 
2671
                clampTime = 5000;
 
2672
        } else {
 
2673
                // for local single player gaming
 
2674
                // we may want to clamp the time to prevent players from
 
2675
                // flying off edges when something hitches.
 
2676
                clampTime = 200;
 
2677
        }
 
2678
 
 
2679
        if ( msec > clampTime ) {
 
2680
                msec = clampTime;
 
2681
        }
 
2682
 
 
2683
        return msec;
 
2684
}
 
2685
 
 
2686
/*
 
2687
=================
 
2688
Com_Frame
 
2689
=================
 
2690
*/
 
2691
void Com_Frame( void ) {
 
2692
 
 
2693
        int             msec, minMsec;
 
2694
        static int      lastTime;
 
2695
        int key;
 
2696
 
 
2697
        int             timeBeforeFirstEvents;
 
2698
        int           timeBeforeServer;
 
2699
        int           timeBeforeEvents;
 
2700
        int           timeBeforeClient;
 
2701
        int           timeAfter;
 
2702
  
 
2703
 
 
2704
 
 
2705
 
 
2706
 
 
2707
        if ( setjmp (abortframe) ) {
 
2708
                return;                 // an ERR_DROP was thrown
 
2709
        }
 
2710
 
 
2711
        // bk001204 - init to zero.
 
2712
        //  also:  might be clobbered by `longjmp' or `vfork'
 
2713
        timeBeforeFirstEvents =0;
 
2714
        timeBeforeServer =0;
 
2715
        timeBeforeEvents =0;
 
2716
        timeBeforeClient = 0;
 
2717
        timeAfter = 0;
 
2718
 
 
2719
 
 
2720
        // old net chan encryption key
 
2721
        key = 0x87243987;
 
2722
 
 
2723
        // write config file if anything changed
 
2724
        Com_WriteConfiguration(); 
 
2725
 
 
2726
        // if "viewlog" has been modified, show or hide the log console
 
2727
        if ( com_viewlog->modified ) {
 
2728
                if ( !com_dedicated->value ) {
 
2729
                        Sys_ShowConsole( com_viewlog->integer, qfalse );
 
2730
                }
 
2731
                com_viewlog->modified = qfalse;
 
2732
        }
 
2733
 
 
2734
        //
 
2735
        // main event loop
 
2736
        //
 
2737
        if ( com_speeds->integer ) {
 
2738
                timeBeforeFirstEvents = Sys_Milliseconds ();
 
2739
        }
 
2740
 
 
2741
        // we may want to spin here if things are going too fast
 
2742
        if ( !com_dedicated->integer && com_maxfps->integer > 0 && !com_timedemo->integer ) {
 
2743
                minMsec = 1000 / com_maxfps->integer;
 
2744
        } else {
 
2745
                minMsec = 1;
 
2746
        }
 
2747
        do {
 
2748
                com_frameTime = Com_EventLoop();
 
2749
                if ( lastTime > com_frameTime ) {
 
2750
                        lastTime = com_frameTime;               // possible on first frame
 
2751
                }
 
2752
                msec = com_frameTime - lastTime;
 
2753
        } while ( msec < minMsec );
 
2754
        Cbuf_Execute ();
 
2755
 
 
2756
        if (com_altivec->modified)
 
2757
        {
 
2758
                Com_DetectAltivec();
 
2759
                com_altivec->modified = qfalse;
 
2760
        }
 
2761
 
 
2762
        lastTime = com_frameTime;
 
2763
 
 
2764
        // mess with msec if needed
 
2765
        com_frameMsec = msec;
 
2766
        msec = Com_ModifyMsec( msec );
 
2767
 
 
2768
        //
 
2769
        // server side
 
2770
        //
 
2771
        if ( com_speeds->integer ) {
 
2772
                timeBeforeServer = Sys_Milliseconds ();
 
2773
        }
 
2774
 
 
2775
        SV_Frame( msec );
 
2776
 
 
2777
        // if "dedicated" has been modified, start up
 
2778
        // or shut down the client system.
 
2779
        // Do this after the server may have started,
 
2780
        // but before the client tries to auto-connect
 
2781
        if ( com_dedicated->modified ) {
 
2782
                // get the latched value
 
2783
                Cvar_Get( "dedicated", "0", 0 );
 
2784
                com_dedicated->modified = qfalse;
 
2785
                if ( !com_dedicated->integer ) {
 
2786
                        CL_Init();
 
2787
                        Sys_ShowConsole( com_viewlog->integer, qfalse );
 
2788
                } else {
 
2789
                        CL_Shutdown();
 
2790
                        Sys_ShowConsole( 1, qtrue );
 
2791
                }
 
2792
        }
 
2793
 
 
2794
        //
 
2795
        // client system
 
2796
        //
 
2797
        if ( !com_dedicated->integer ) {
 
2798
                //
 
2799
                // run event loop a second time to get server to client packets
 
2800
                // without a frame of latency
 
2801
                //
 
2802
                if ( com_speeds->integer ) {
 
2803
                        timeBeforeEvents = Sys_Milliseconds ();
 
2804
                }
 
2805
                Com_EventLoop();
 
2806
                Cbuf_Execute ();
 
2807
 
 
2808
 
 
2809
                //
 
2810
                // client side
 
2811
                //
 
2812
                if ( com_speeds->integer ) {
 
2813
                        timeBeforeClient = Sys_Milliseconds ();
 
2814
                }
 
2815
 
 
2816
                CL_Frame( msec );
 
2817
 
 
2818
                if ( com_speeds->integer ) {
 
2819
                        timeAfter = Sys_Milliseconds ();
 
2820
                }
 
2821
        }
 
2822
 
 
2823
        //
 
2824
        // report timing information
 
2825
        //
 
2826
        if ( com_speeds->integer ) {
 
2827
                int                     all, sv, ev, cl;
 
2828
 
 
2829
                all = timeAfter - timeBeforeServer;
 
2830
                sv = timeBeforeEvents - timeBeforeServer;
 
2831
                ev = timeBeforeServer - timeBeforeFirstEvents + timeBeforeClient - timeBeforeEvents;
 
2832
                cl = timeAfter - timeBeforeClient;
 
2833
                sv -= time_game;
 
2834
                cl -= time_frontend + time_backend;
 
2835
 
 
2836
                Com_Printf ("frame:%i all:%3i sv:%3i ev:%3i cl:%3i gm:%3i rf:%3i bk:%3i\n", 
 
2837
                                         com_frameNumber, all, sv, ev, cl, time_game, time_frontend, time_backend );
 
2838
        }       
 
2839
 
 
2840
        //
 
2841
        // trace optimization tracking
 
2842
        //
 
2843
        if ( com_showtrace->integer ) {
 
2844
        
 
2845
                extern  int c_traces, c_brush_traces, c_patch_traces;
 
2846
                extern  int     c_pointcontents;
 
2847
 
 
2848
                Com_Printf ("%4i traces  (%ib %ip) %4i points\n", c_traces,
 
2849
                        c_brush_traces, c_patch_traces, c_pointcontents);
 
2850
                c_traces = 0;
 
2851
                c_brush_traces = 0;
 
2852
                c_patch_traces = 0;
 
2853
                c_pointcontents = 0;
 
2854
        }
 
2855
 
 
2856
        // old net chan encryption key
 
2857
        key = lastTime * 0x87243987;
 
2858
 
 
2859
        com_frameNumber++;
 
2860
}
 
2861
 
 
2862
/*
 
2863
=================
 
2864
Com_Shutdown
 
2865
=================
 
2866
*/
 
2867
void Com_Shutdown (void) {
 
2868
        if (logfile) {
 
2869
                FS_FCloseFile (logfile);
 
2870
                logfile = 0;
 
2871
        }
 
2872
 
 
2873
        if ( com_journalFile ) {
 
2874
                FS_FCloseFile( com_journalFile );
 
2875
                com_journalFile = 0;
 
2876
        }
 
2877
 
 
2878
}
 
2879
 
 
2880
//------------------------------------------------------------------------
 
2881
 
 
2882
 
 
2883
/*
 
2884
=====================
 
2885
Q_acos
 
2886
 
 
2887
the msvc acos doesn't always return a value between -PI and PI:
 
2888
 
 
2889
int i;
 
2890
i = 1065353246;
 
2891
acos(*(float*) &i) == -1.#IND0
 
2892
 
 
2893
        This should go in q_math but it is too late to add new traps
 
2894
        to game and ui
 
2895
=====================
 
2896
*/
 
2897
float Q_acos(float c) {
 
2898
        float angle;
 
2899
 
 
2900
        angle = acos(c);
 
2901
 
 
2902
        if (angle > M_PI) {
 
2903
                return (float)M_PI;
 
2904
        }
 
2905
        if (angle < -M_PI) {
 
2906
                return (float)M_PI;
 
2907
        }
 
2908
        return angle;
 
2909
}
 
2910
 
 
2911
/*
 
2912
===========================================
 
2913
command line completion
 
2914
===========================================
 
2915
*/
 
2916
 
 
2917
/*
 
2918
==================
 
2919
Field_Clear
 
2920
==================
 
2921
*/
 
2922
void Field_Clear( field_t *edit ) {
 
2923
  memset(edit->buffer, 0, MAX_EDIT_LINE);
 
2924
        edit->cursor = 0;
 
2925
        edit->scroll = 0;
 
2926
}
 
2927
 
 
2928
static const char *completionString;
 
2929
static char shortestMatch[MAX_TOKEN_CHARS];
 
2930
static int      matchCount;
 
2931
// field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
 
2932
static field_t *completionField;
 
2933
 
 
2934
/*
 
2935
===============
 
2936
FindMatches
 
2937
 
 
2938
===============
 
2939
*/
 
2940
static void FindMatches( const char *s ) {
 
2941
        int             i;
 
2942
 
 
2943
        if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) {
 
2944
                return;
 
2945
        }
 
2946
        matchCount++;
 
2947
        if ( matchCount == 1 ) {
 
2948
                Q_strncpyz( shortestMatch, s, sizeof( shortestMatch ) );
 
2949
                return;
 
2950
        }
 
2951
 
 
2952
        // cut shortestMatch to the amount common with s
 
2953
        for ( i = 0 ; shortestMatch[i] ; i++ ) {
 
2954
                if ( i >= strlen( s ) ) {
 
2955
                        shortestMatch[i] = 0;
 
2956
                        break;
 
2957
                }
 
2958
 
 
2959
                if ( tolower(shortestMatch[i]) != tolower(s[i]) ) {
 
2960
                        shortestMatch[i] = 0;
 
2961
                }
 
2962
        }
 
2963
}
 
2964
 
 
2965
/*
 
2966
===============
 
2967
PrintMatches
 
2968
 
 
2969
===============
 
2970
*/
 
2971
static void PrintMatches( const char *s ) {
 
2972
        if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
 
2973
                Com_Printf( "    %s\n", s );
 
2974
        }
 
2975
}
 
2976
 
 
2977
/*
 
2978
===============
 
2979
PrintCvarMatches
 
2980
 
 
2981
===============
 
2982
*/
 
2983
static void PrintCvarMatches( const char *s ) {
 
2984
        char value[ TRUNCATE_LENGTH ];
 
2985
 
 
2986
        if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
 
2987
                Com_TruncateLongString( value, Cvar_VariableString( s ) );
 
2988
                Com_Printf( "    %s = \"%s\"\n", s, value );
 
2989
        }
 
2990
}
 
2991
 
 
2992
/*
 
2993
===============
 
2994
Field_FindFirstSeparator
 
2995
===============
 
2996
*/
 
2997
static char *Field_FindFirstSeparator( char *s )
 
2998
{
 
2999
        int i;
 
3000
 
 
3001
        for( i = 0; i < strlen( s ); i++ )
 
3002
        {
 
3003
                if( s[ i ] == ';' )
 
3004
                        return &s[ i ];
 
3005
        }
 
3006
 
 
3007
        return NULL;
 
3008
}
 
3009
 
 
3010
/*
 
3011
===============
 
3012
Field_CompleteFilename
 
3013
===============
 
3014
*/
 
3015
static void Field_CompleteFilename( const char *dir,
 
3016
                const char *ext, qboolean stripExt )
 
3017
{
 
3018
        matchCount = 0;
 
3019
        shortestMatch[ 0 ] = 0;
 
3020
 
 
3021
        FS_FilenameCompletion( dir, ext, stripExt, FindMatches );
 
3022
 
 
3023
        if( matchCount == 0 )
 
3024
                return;
 
3025
 
 
3026
        Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
 
3027
                        shortestMatch + strlen( completionString ) );
 
3028
        completionField->cursor = strlen( completionField->buffer );
 
3029
 
 
3030
        if( matchCount == 1 )
 
3031
        {
 
3032
                Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
 
3033
                completionField->cursor++;
 
3034
                return;
 
3035
        }
 
3036
 
 
3037
        Com_Printf( "]%s\n", completionField->buffer );
 
3038
        
 
3039
        FS_FilenameCompletion( dir, ext, stripExt, PrintMatches );
 
3040
}
 
3041
 
 
3042
/*
 
3043
===============
 
3044
Field_CompleteCommand
 
3045
===============
 
3046
*/
 
3047
static void Field_CompleteCommand( char *cmd,
 
3048
                qboolean doCommands, qboolean doCvars )
 
3049
{
 
3050
        int             completionArgument = 0;
 
3051
        char    *p;
 
3052
 
 
3053
        // Skip leading whitespace and quotes
 
3054
        cmd = Com_SkipCharset( cmd, " \"" );
 
3055
 
 
3056
        Cmd_TokenizeStringIgnoreQuotes( cmd );
 
3057
        completionArgument = Cmd_Argc( );
 
3058
 
 
3059
        // If there is trailing whitespace on the cmd
 
3060
        if( *( cmd + strlen( cmd ) - 1 ) == ' ' )
 
3061
        {
 
3062
                completionString = "";
 
3063
                completionArgument++;
 
3064
        }
 
3065
        else
 
3066
                completionString = Cmd_Argv( completionArgument - 1 );
 
3067
 
 
3068
        if( completionArgument > 1 )
 
3069
        {
 
3070
                const char *baseCmd = Cmd_Argv( 0 );
 
3071
 
 
3072
#ifndef DEDICATED
 
3073
                // If the very first token does not have a leading \ or /,
 
3074
                // refuse to autocomplete
 
3075
                if( cmd == completionField->buffer )
 
3076
                {
 
3077
                        if( baseCmd[ 0 ] != '\\' && baseCmd[ 0 ] != '/' )
 
3078
                                return;
 
3079
 
 
3080
                        baseCmd++;
 
3081
                }
 
3082
#endif
 
3083
 
 
3084
                if( ( p = Field_FindFirstSeparator( cmd ) ) )
 
3085
                {
 
3086
                        // Compound command
 
3087
                        Field_CompleteCommand( p + 1, qtrue, qtrue );
 
3088
                }
 
3089
                else
 
3090
                {
 
3091
                        // FIXME: all this junk should really be associated with the respective
 
3092
                        // commands, instead of being hard coded here
 
3093
                        if( ( !Q_stricmp( baseCmd, "map" ) ||
 
3094
                                                !Q_stricmp( baseCmd, "devmap" ) ||
 
3095
                                                !Q_stricmp( baseCmd, "spmap" ) ||
 
3096
                                                !Q_stricmp( baseCmd, "spdevmap" ) ) &&
 
3097
                                        completionArgument == 2 )
 
3098
                        {
 
3099
                                Field_CompleteFilename( "maps", "bsp", qtrue );
 
3100
                        }
 
3101
                        else if( ( !Q_stricmp( baseCmd, "exec" ) ||
 
3102
                                                !Q_stricmp( baseCmd, "writeconfig" ) ) &&
 
3103
                                        completionArgument == 2 )
 
3104
                        {
 
3105
                                Field_CompleteFilename( "", "cfg", qfalse );
 
3106
                        }
 
3107
                        else if( !Q_stricmp( baseCmd, "condump" ) &&
 
3108
                                        completionArgument == 2 )
 
3109
                        {
 
3110
                                Field_CompleteFilename( "", "txt", qfalse );
 
3111
                        }
 
3112
                        else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 )
 
3113
                        {
 
3114
                                char demoExt[ 16 ];
 
3115
 
 
3116
                                Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION );
 
3117
                                Field_CompleteFilename( "demos", demoExt, qtrue );
 
3118
                        }
 
3119
                        else if( ( !Q_stricmp( baseCmd, "toggle" ) ||
 
3120
                                                !Q_stricmp( baseCmd, "vstr" ) ||
 
3121
                                                !Q_stricmp( baseCmd, "set" ) ||
 
3122
                                                !Q_stricmp( baseCmd, "seta" ) ||
 
3123
                                                !Q_stricmp( baseCmd, "setu" ) ||
 
3124
                                                !Q_stricmp( baseCmd, "sets" ) ) &&
 
3125
                                        completionArgument == 2 )
 
3126
                        {
 
3127
                                // Skip "<cmd> "
 
3128
                                p = Com_SkipTokens( cmd, 1, " " );
 
3129
 
 
3130
                                if( p > cmd )
 
3131
                                        Field_CompleteCommand( p, qfalse, qtrue );
 
3132
                        }
 
3133
                        else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 )
 
3134
                        {
 
3135
                                // Skip "rcon "
 
3136
                                p = Com_SkipTokens( cmd, 1, " " );
 
3137
 
 
3138
                                if( p > cmd )
 
3139
                                        Field_CompleteCommand( p, qtrue, qtrue );
 
3140
                        }
 
3141
                        else if( !Q_stricmp( baseCmd, "bind" ) && completionArgument >= 3 )
 
3142
                        {
 
3143
                                // Skip "bind <key> "
 
3144
                                p = Com_SkipTokens( cmd, 2, " " );
 
3145
 
 
3146
                                if( p > cmd )
 
3147
                                        Field_CompleteCommand( p, qtrue, qtrue );
 
3148
                        }
 
3149
                }
 
3150
        }
 
3151
        else
 
3152
        {
 
3153
                if( completionString[0] == '\\' || completionString[0] == '/' )
 
3154
                        completionString++;
 
3155
 
 
3156
                matchCount = 0;
 
3157
                shortestMatch[ 0 ] = 0;
 
3158
 
 
3159
                if( strlen( completionString ) == 0 )
 
3160
                        return;
 
3161
 
 
3162
                if( doCommands )
 
3163
                        Cmd_CommandCompletion( FindMatches );
 
3164
 
 
3165
                if( doCvars )
 
3166
                        Cvar_CommandCompletion( FindMatches );
 
3167
 
 
3168
                if( matchCount == 0 )
 
3169
                        return; // no matches
 
3170
 
 
3171
                if( cmd == completionField->buffer )
 
3172
                {
 
3173
#ifndef DEDICATED
 
3174
                        Com_sprintf( completionField->buffer,
 
3175
                                        sizeof( completionField->buffer ), "\\%s", shortestMatch );
 
3176
#else
 
3177
                        Com_sprintf( completionField->buffer,
 
3178
                                        sizeof( completionField->buffer ), "%s", shortestMatch );
 
3179
#endif
 
3180
                }
 
3181
                else
 
3182
                {
 
3183
                        Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
 
3184
                                        shortestMatch + strlen( completionString ) );
 
3185
                }
 
3186
 
 
3187
                completionField->cursor = strlen( completionField->buffer );
 
3188
 
 
3189
                if( matchCount == 1 )
 
3190
                {
 
3191
                        Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
 
3192
                        completionField->cursor++;
 
3193
                        return;
 
3194
                }
 
3195
 
 
3196
                Com_Printf( "]%s\n", completionField->buffer );
 
3197
 
 
3198
                // run through again, printing matches
 
3199
                if( doCommands )
 
3200
                        Cmd_CommandCompletion( PrintMatches );
 
3201
 
 
3202
                if( doCvars )
 
3203
                        Cvar_CommandCompletion( PrintCvarMatches );
 
3204
        }
 
3205
}
 
3206
 
 
3207
/*
 
3208
===============
 
3209
Field_AutoComplete
 
3210
 
 
3211
Perform Tab expansion
 
3212
===============
 
3213
*/
 
3214
void Field_AutoComplete( field_t *field )
 
3215
{
 
3216
        completionField = field;
 
3217
 
 
3218
        Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
 
3219
}