Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

common.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // common.c -- misc functions used in client and server
00023 
00024 #include "../game/q_shared.h"
00025 #include "qcommon.h"
00026 #include <setjmp.h>
00027 #ifdef __linux__
00028 #include <netinet/in.h>
00029 #else
00030 #if defined(MACOS_X)
00031 #include <netinet/in.h>
00032 #else
00033 #include <winsock.h>
00034 #endif
00035 #endif
00036 
00037 int demo_protocols[] =
00038 { 66, 67, 68, 0 };
00039 
00040 #define MAX_NUM_ARGVS   50
00041 
00042 #define MIN_DEDICATED_COMHUNKMEGS 1
00043 #define MIN_COMHUNKMEGS 56
00044 #ifdef MACOS_X
00045 #define DEF_COMHUNKMEGS "64"
00046 #define DEF_COMZONEMEGS "24"
00047 #else
00048 #define DEF_COMHUNKMEGS "56"
00049 #define DEF_COMZONEMEGS "16"
00050 #endif
00051 
00052 int     com_argc;
00053 char    *com_argv[MAX_NUM_ARGVS+1];
00054 
00055 jmp_buf abortframe;     // an ERR_DROP occured, exit the entire frame
00056 
00057 
00058 FILE *debuglogfile;
00059 static fileHandle_t logfile;
00060 fileHandle_t    com_journalFile;            // events are written here
00061 fileHandle_t    com_journalDataFile;        // config files are written here
00062 
00063 cvar_t  *com_viewlog;
00064 cvar_t  *com_speeds;
00065 cvar_t  *com_developer;
00066 cvar_t  *com_dedicated;
00067 cvar_t  *com_timescale;
00068 cvar_t  *com_fixedtime;
00069 cvar_t  *com_dropsim;       // 0.0 to 1.0, simulated packet drops
00070 cvar_t  *com_journal;
00071 cvar_t  *com_maxfps;
00072 cvar_t  *com_timedemo;
00073 cvar_t  *com_sv_running;
00074 cvar_t  *com_cl_running;
00075 cvar_t  *com_logfile;       // 1 = buffer log, 2 = flush after each print
00076 cvar_t  *com_showtrace;
00077 cvar_t  *com_version;
00078 cvar_t  *com_blood;
00079 cvar_t  *com_buildScript;   // for automated data building scripts
00080 cvar_t  *com_introPlayed;
00081 cvar_t  *cl_paused;
00082 cvar_t  *sv_paused;
00083 cvar_t  *com_cameraMode;
00084 #if defined(_WIN32) && defined(_DEBUG)
00085 cvar_t  *com_noErrorInterrupt;
00086 #endif
00087 
00088 // com_speeds times
00089 int     time_game;
00090 int     time_frontend;      // renderer frontend time
00091 int     time_backend;       // renderer backend time
00092 
00093 int         com_frameTime;
00094 int         com_frameMsec;
00095 int         com_frameNumber;
00096 
00097 qboolean    com_errorEntered;
00098 qboolean    com_fullyInitialized;
00099 
00100 char    com_errorMessage[MAXPRINTMSG];
00101 
00102 void Com_WriteConfig_f( void );
00103 void CIN_CloseAllVideos();
00104 
00105 //============================================================================
00106 
00107 static char *rd_buffer;
00108 static int  rd_buffersize;
00109 static void (*rd_flush)( char *buffer );
00110 
00111 void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
00112 {
00113     if (!buffer || !buffersize || !flush)
00114         return;
00115     rd_buffer = buffer;
00116     rd_buffersize = buffersize;
00117     rd_flush = flush;
00118 
00119     *rd_buffer = 0;
00120 }
00121 
00122 void Com_EndRedirect (void)
00123 {
00124     if ( rd_flush ) {
00125         rd_flush(rd_buffer);
00126     }
00127 
00128     rd_buffer = NULL;
00129     rd_buffersize = 0;
00130     rd_flush = NULL;
00131 }
00132 
00133 /*
00134 =============
00135 Com_Printf
00136 
00137 Both client and server can use this, and it will output
00138 to the apropriate place.
00139 
00140 A raw string should NEVER be passed as fmt, because of "%f" type crashers.
00141 =============
00142 */
00143 void QDECL Com_Printf( const char *fmt, ... ) {
00144     va_list     argptr;
00145     char        msg[MAXPRINTMSG];
00146   static qboolean opening_qconsole = qfalse;
00147 
00148     va_start (argptr,fmt);
00149     Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
00150     va_end (argptr);
00151 
00152     if ( rd_buffer ) {
00153         if ((strlen (msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) {
00154             rd_flush(rd_buffer);
00155             *rd_buffer = 0;
00156         }
00157         Q_strcat(rd_buffer, rd_buffersize, msg);
00158     // TTimo nooo .. that would defeat the purpose
00159         //rd_flush(rd_buffer);          
00160         //*rd_buffer = 0;
00161         return;
00162     }
00163 
00164     // echo to console if we're not a dedicated server
00165     if ( com_dedicated && !com_dedicated->integer ) {
00166         CL_ConsolePrint( msg );
00167     }
00168 
00169     // echo to dedicated console and early console
00170     Sys_Print( msg );
00171 
00172     // logfile
00173     if ( com_logfile && com_logfile->integer ) {
00174     // TTimo: only open the qconsole.log if the filesystem is in an initialized state
00175     //   also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
00176         if ( !logfile && FS_Initialized() && !opening_qconsole) {
00177             struct tm *newtime;
00178             time_t aclock;
00179 
00180       opening_qconsole = qtrue;
00181 
00182             time( &aclock );
00183             newtime = localtime( &aclock );
00184 
00185             logfile = FS_FOpenFileWrite( "qconsole.log" );
00186             Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
00187             if ( com_logfile->integer > 1 ) {
00188                 // force it to not buffer so we get valid
00189                 // data even if we are crashing
00190                 FS_ForceFlush(logfile);
00191             }
00192 
00193       opening_qconsole = qfalse;
00194         }
00195         if ( logfile && FS_Initialized()) {
00196             FS_Write(msg, strlen(msg), logfile);
00197         }
00198     }
00199 }
00200 
00201 
00202 /*
00203 ================
00204 Com_DPrintf
00205 
00206 A Com_Printf that only shows up if the "developer" cvar is set
00207 ================
00208 */
00209 void QDECL Com_DPrintf( const char *fmt, ...) {
00210     va_list     argptr;
00211     char        msg[MAXPRINTMSG];
00212         
00213     if ( !com_developer || !com_developer->integer ) {
00214         return;         // don't confuse non-developers with techie stuff...
00215     }
00216 
00217     va_start (argptr,fmt);  
00218     Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
00219     va_end (argptr);
00220     
00221     Com_Printf ("%s", msg);
00222 }
00223 
00224 /*
00225 =============
00226 Com_Error
00227 
00228 Both client and server can use this, and it will
00229 do the apropriate things.
00230 =============
00231 */
00232 void QDECL Com_Error( int code, const char *fmt, ... ) {
00233     va_list     argptr;
00234     static int  lastErrorTime;
00235     static int  errorCount;
00236     int         currentTime;
00237 
00238 #if defined(_WIN32) && defined(_DEBUG)
00239     if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
00240         if (!com_noErrorInterrupt->integer) {
00241             __asm {
00242                 int 0x03
00243             }
00244         }
00245     }
00246 #endif
00247 
00248     // when we are running automated scripts, make sure we
00249     // know if anything failed
00250     if ( com_buildScript && com_buildScript->integer ) {
00251         code = ERR_FATAL;
00252     }
00253 
00254     // make sure we can get at our local stuff
00255     FS_PureServerSetLoadedPaks( "", "" );
00256 
00257     // if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
00258     currentTime = Sys_Milliseconds();
00259     if ( currentTime - lastErrorTime < 100 ) {
00260         if ( ++errorCount > 3 ) {
00261             code = ERR_FATAL;
00262         }
00263     } else {
00264         errorCount = 0;
00265     }
00266     lastErrorTime = currentTime;
00267 
00268     if ( com_errorEntered ) {
00269         Sys_Error( "recursive error after: %s", com_errorMessage );
00270     }
00271     com_errorEntered = qtrue;
00272 
00273     va_start (argptr,fmt);
00274     vsprintf (com_errorMessage,fmt,argptr);
00275     va_end (argptr);
00276 
00277     if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
00278         Cvar_Set("com_errorMessage", com_errorMessage);
00279     }
00280 
00281     if ( code == ERR_SERVERDISCONNECT ) {
00282         CL_Disconnect( qtrue );
00283         CL_FlushMemory( );
00284         com_errorEntered = qfalse;
00285         longjmp (abortframe, -1);
00286     } else if ( code == ERR_DROP || code == ERR_DISCONNECT ) {
00287         Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage);
00288         SV_Shutdown (va("Server crashed: %s\n",  com_errorMessage));
00289         CL_Disconnect( qtrue );
00290         CL_FlushMemory( );
00291         com_errorEntered = qfalse;
00292         longjmp (abortframe, -1);
00293     } else if ( code == ERR_NEED_CD ) {
00294         SV_Shutdown( "Server didn't have CD\n" );
00295         if ( com_cl_running && com_cl_running->integer ) {
00296             CL_Disconnect( qtrue );
00297             CL_FlushMemory( );
00298             com_errorEntered = qfalse;
00299             CL_CDDialog();
00300         } else {
00301             Com_Printf("Server didn't have CD\n" );
00302         }
00303         longjmp (abortframe, -1);
00304     } else {
00305         CL_Shutdown ();
00306         SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage));
00307     }
00308 
00309     Com_Shutdown ();
00310 
00311     Sys_Error ("%s", com_errorMessage);
00312 }
00313 
00314 
00315 /*
00316 =============
00317 Com_Quit_f
00318 
00319 Both client and server can use this, and it will
00320 do the apropriate things.
00321 =============
00322 */
00323 void Com_Quit_f( void ) {
00324     // don't try to shutdown if we are in a recursive error
00325     if ( !com_errorEntered ) {
00326         SV_Shutdown ("Server quit\n");
00327         CL_Shutdown ();
00328         Com_Shutdown ();
00329         FS_Shutdown(qtrue);
00330     }
00331     Sys_Quit ();
00332 }
00333 
00334 
00335 
00336 /*
00337 ============================================================================
00338 
00339 COMMAND LINE FUNCTIONS
00340 
00341 + characters seperate the commandLine string into multiple console
00342 command lines.
00343 
00344 All of these are valid:
00345 
00346 quake3 +set test blah +map test
00347 quake3 set test blah+map test
00348 quake3 set test blah + map test
00349 
00350 ============================================================================
00351 */
00352 
00353 #define MAX_CONSOLE_LINES   32
00354 int     com_numConsoleLines;
00355 char    *com_consoleLines[MAX_CONSOLE_LINES];
00356 
00357 /*
00358 ==================
00359 Com_ParseCommandLine
00360 
00361 Break it up into multiple console lines
00362 ==================
00363 */
00364 void Com_ParseCommandLine( char *commandLine ) {
00365     int inq = 0;
00366     com_consoleLines[0] = commandLine;
00367     com_numConsoleLines = 1;
00368 
00369     while ( *commandLine ) {
00370         if (*commandLine == '"') {
00371             inq = !inq;
00372         }
00373         // look for a + seperating character
00374         // if commandLine came from a file, we might have real line seperators
00375         if ( (*commandLine == '+' && !inq) || *commandLine == '\n'  || *commandLine == '\r' ) {
00376             if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
00377                 return;
00378             }
00379             com_consoleLines[com_numConsoleLines] = commandLine + 1;
00380             com_numConsoleLines++;
00381             *commandLine = 0;
00382         }
00383         commandLine++;
00384     }
00385 }
00386 
00387 
00388 /*
00389 ===================
00390 Com_SafeMode
00391 
00392 Check for "safe" on the command line, which will
00393 skip loading of q3config.cfg
00394 ===================
00395 */
00396 qboolean Com_SafeMode( void ) {
00397     int     i;
00398 
00399     for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
00400         Cmd_TokenizeString( com_consoleLines[i] );
00401         if ( !Q_stricmp( Cmd_Argv(0), "safe" )
00402             || !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
00403             com_consoleLines[i][0] = 0;
00404             return qtrue;
00405         }
00406     }
00407     return qfalse;
00408 }
00409 
00410 
00411 /*
00412 ===============
00413 Com_StartupVariable
00414 
00415 Searches for command line parameters that are set commands.
00416 If match is not NULL, only that cvar will be looked for.
00417 That is necessary because cddir and basedir need to be set
00418 before the filesystem is started, but all other sets shouls
00419 be after execing the config and default.
00420 ===============
00421 */
00422 void Com_StartupVariable( const char *match ) {
00423     int     i;
00424     char    *s;
00425     cvar_t  *cv;
00426 
00427     for (i=0 ; i < com_numConsoleLines ; i++) {
00428         Cmd_TokenizeString( com_consoleLines[i] );
00429         if ( strcmp( Cmd_Argv(0), "set" ) ) {
00430             continue;
00431         }
00432 
00433         s = Cmd_Argv(1);
00434         if ( !match || !strcmp( s, match ) ) {
00435             Cvar_Set( s, Cmd_Argv(2) );
00436             cv = Cvar_Get( s, "", 0 );
00437             cv->flags |= CVAR_USER_CREATED;
00438 //          com_consoleLines[i] = 0;
00439         }
00440     }
00441 }
00442 
00443 
00444 /*
00445 =================
00446 Com_AddStartupCommands
00447 
00448 Adds command line parameters as script statements
00449 Commands are seperated by + signs
00450 
00451 Returns qtrue if any late commands were added, which
00452 will keep the demoloop from immediately starting
00453 =================
00454 */
00455 qboolean Com_AddStartupCommands( void ) {
00456     int     i;
00457     qboolean    added;
00458 
00459     added = qfalse;
00460     // quote every token, so args with semicolons can work
00461     for (i=0 ; i < com_numConsoleLines ; i++) {
00462         if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
00463             continue;
00464         }
00465 
00466         // set commands won't override menu startup
00467         if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
00468             added = qtrue;
00469         }
00470         Cbuf_AddText( com_consoleLines[i] );
00471         Cbuf_AddText( "\n" );
00472     }
00473 
00474     return added;
00475 }
00476 
00477 
00478 //============================================================================
00479 
00480 void Info_Print( const char *s ) {
00481     char    key[512];
00482     char    value[512];
00483     char    *o;
00484     int     l;
00485 
00486     if (*s == '\\')
00487         s++;
00488     while (*s)
00489     {
00490         o = key;
00491         while (*s && *s != '\\')
00492             *o++ = *s++;
00493 
00494         l = o - key;
00495         if (l < 20)
00496         {
00497             Com_Memset (o, ' ', 20-l);
00498             key[20] = 0;
00499         }
00500         else
00501             *o = 0;
00502         Com_Printf ("%s", key);
00503 
00504         if (!*s)
00505         {
00506             Com_Printf ("MISSING VALUE\n");
00507             return;
00508         }
00509 
00510         o = value;
00511         s++;
00512         while (*s && *s != '\\')
00513             *o++ = *s++;
00514         *o = 0;
00515 
00516         if (*s)
00517             s++;
00518         Com_Printf ("%s\n", value);
00519     }
00520 }
00521 
00522 /*
00523 ============
00524 Com_StringContains
00525 ============
00526 */
00527 char *Com_StringContains(char *str1, char *str2, int casesensitive) {
00528     int len, i, j;
00529 
00530     len = strlen(str1) - strlen(str2);
00531     for (i = 0; i <= len; i++, str1++) {
00532         for (j = 0; str2[j]; j++) {
00533             if (casesensitive) {
00534                 if (str1[j] != str2[j]) {
00535                     break;
00536                 }
00537             }
00538             else {
00539                 if (toupper(str1[j]) != toupper(str2[j])) {
00540                     break;
00541                 }
00542             }
00543         }
00544         if (!str2[j]) {
00545             return str1;
00546         }
00547     }
00548     return NULL;
00549 }
00550 
00551 /*
00552 ============
00553 Com_Filter
00554 ============
00555 */
00556 int Com_Filter(char *filter, char *name, int casesensitive)
00557 {
00558     char buf[MAX_TOKEN_CHARS];
00559     char *ptr;
00560     int i, found;
00561 
00562     while(*filter) {
00563         if (*filter == '*') {
00564             filter++;
00565             for (i = 0; *filter; i++) {
00566                 if (*filter == '*' || *filter == '?') break;
00567                 buf[i] = *filter;
00568                 filter++;
00569             }
00570             buf[i] = '\0';
00571             if (strlen(buf)) {
00572                 ptr = Com_StringContains(name, buf, casesensitive);
00573                 if (!ptr) return qfalse;
00574                 name = ptr + strlen(buf);
00575             }
00576         }
00577         else if (*filter == '?') {
00578             filter++;
00579             name++;
00580         }
00581         else if (*filter == '[' && *(filter+1) == '[') {
00582             filter++;
00583         }
00584         else if (*filter == '[') {
00585             filter++;
00586             found = qfalse;
00587             while(*filter && !found) {
00588                 if (*filter == ']' && *(filter+1) != ']') break;
00589                 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
00590                     if (casesensitive) {
00591                         if (*name >= *filter && *name <= *(filter+2)) found = qtrue;
00592                     }
00593                     else {
00594                         if (toupper(*name) >= toupper(*filter) &&
00595                             toupper(*name) <= toupper(*(filter+2))) found = qtrue;
00596                     }
00597                     filter += 3;
00598                 }
00599                 else {
00600                     if (casesensitive) {
00601                         if (*filter == *name) found = qtrue;
00602                     }
00603                     else {
00604                         if (toupper(*filter) == toupper(*name)) found = qtrue;
00605                     }
00606                     filter++;
00607                 }
00608             }
00609             if (!found) return qfalse;
00610             while(*filter) {
00611                 if (*filter == ']' && *(filter+1) != ']') break;
00612                 filter++;
00613             }
00614             filter++;
00615             name++;
00616         }
00617         else {
00618             if (casesensitive) {
00619                 if (*filter != *name) return qfalse;
00620             }
00621             else {
00622                 if (toupper(*filter) != toupper(*name)) return qfalse;
00623             }
00624             filter++;
00625             name++;
00626         }
00627     }
00628     return qtrue;
00629 }
00630 
00631 /*
00632 ============
00633 Com_FilterPath
00634 ============
00635 */
00636 int Com_FilterPath(char *filter, char *name, int casesensitive)
00637 {
00638     int i;
00639     char new_filter[MAX_QPATH];
00640     char new_name[MAX_QPATH];
00641 
00642     for (i = 0; i < MAX_QPATH-1 && filter[i]; i++) {
00643         if ( filter[i] == '\\' || filter[i] == ':' ) {
00644             new_filter[i] = '/';
00645         }
00646         else {
00647             new_filter[i] = filter[i];
00648         }
00649     }
00650     new_filter[i] = '\0';
00651     for (i = 0; i < MAX_QPATH-1 && name[i]; i++) {
00652         if ( name[i] == '\\' || name[i] == ':' ) {
00653             new_name[i] = '/';
00654         }
00655         else {
00656             new_name[i] = name[i];
00657         }
00658     }
00659     new_name[i] = '\0';
00660     return Com_Filter(new_filter, new_name, casesensitive);
00661 }
00662 
00663 /*
00664 ============
00665 Com_HashKey
00666 ============
00667 */
00668 int Com_HashKey(char *string, int maxlen) {
00669     int register hash, i;
00670 
00671     hash = 0;
00672     for (i = 0; i < maxlen && string[i] != '\0'; i++) {
00673         hash += string[i] * (119 + i);
00674     }
00675     hash = (hash ^ (hash >> 10) ^ (hash >> 20));
00676     return hash;
00677 }
00678 
00679 /*
00680 ================
00681 Com_RealTime
00682 ================
00683 */
00684 int Com_RealTime(qtime_t *qtime) {
00685     time_t t;
00686     struct tm *tms;
00687 
00688     t = time(NULL);
00689     if (!qtime)
00690         return t;
00691     tms = localtime(&t);
00692     if (tms) {
00693         qtime->tm_sec = tms->tm_sec;
00694         qtime->tm_min = tms->tm_min;
00695         qtime->tm_hour = tms->tm_hour;
00696         qtime->tm_mday = tms->tm_mday;
00697         qtime->tm_mon = tms->tm_mon;
00698         qtime->tm_year = tms->tm_year;
00699         qtime->tm_wday = tms->tm_wday;
00700         qtime->tm_yday = tms->tm_yday;
00701         qtime->tm_isdst = tms->tm_isdst;
00702     }
00703     return t;
00704 }
00705 
00706 
00707 /*
00708 ==============================================================================
00709 
00710                         ZONE MEMORY ALLOCATION
00711 
00712 There is never any space between memblocks, and there will never be two
00713 contiguous free memblocks.
00714 
00715 The rover can be left pointing at a non-empty block
00716 
00717 The zone calls are pretty much only used for small strings and structures,
00718 all big things are allocated on the hunk.
00719 ==============================================================================
00720 */
00721 
00722 #define ZONEID  0x1d4a11
00723 #define MINFRAGMENT 64
00724 
00725 typedef struct zonedebug_s {
00726     char *label;
00727     char *file;
00728     int line;
00729     int allocSize;
00730 } zonedebug_t;
00731 
00732 typedef struct memblock_s {
00733     int     size;           // including the header and possibly tiny fragments
00734     int     tag;            // a tag of 0 is a free block
00735     struct memblock_s       *next, *prev;
00736     int     id;             // should be ZONEID
00737 #ifdef ZONE_DEBUG
00738     zonedebug_t d;
00739 #endif
00740 } memblock_t;
00741 
00742 typedef struct {
00743     int     size;           // total bytes malloced, including header
00744     int     used;           // total bytes used
00745     memblock_t  blocklist;  // start / end cap for linked list
00746     memblock_t  *rover;
00747 } memzone_t;
00748 
00749 // main zone for all "dynamic" memory allocation
00750 memzone_t   *mainzone;
00751 // we also have a small zone for small allocations that would only
00752 // fragment the main zone (think of cvar and cmd strings)
00753 memzone_t   *smallzone;
00754 
00755 void Z_CheckHeap( void );
00756 
00757 /*
00758 ========================
00759 Z_ClearZone
00760 ========================
00761 */
00762 void Z_ClearZone( memzone_t *zone, int size ) {
00763     memblock_t  *block;
00764     
00765     // set the entire zone to one free block
00766 
00767     zone->blocklist.next = zone->blocklist.prev = block =
00768         (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
00769     zone->blocklist.tag = 1;    // in use block
00770     zone->blocklist.id = 0;
00771     zone->blocklist.size = 0;
00772     zone->rover = block;
00773     zone->size = size;
00774     zone->used = 0;
00775     
00776     block->prev = block->next = &zone->blocklist;
00777     block->tag = 0;         // free block
00778     block->id = ZONEID;
00779     block->size = size - sizeof(memzone_t);
00780 }
00781 
00782 /*
00783 ========================
00784 Z_AvailableZoneMemory
00785 ========================
00786 */
00787 int Z_AvailableZoneMemory( memzone_t *zone ) {
00788     return zone->size - zone->used;
00789 }
00790 
00791 /*
00792 ========================
00793 Z_AvailableMemory
00794 ========================
00795 */
00796 int Z_AvailableMemory( void ) {
00797     return Z_AvailableZoneMemory( mainzone );
00798 }
00799 
00800 /*
00801 ========================
00802 Z_Free
00803 ========================
00804 */
00805 void Z_Free( void *ptr ) {
00806     memblock_t  *block, *other;
00807     memzone_t *zone;
00808     
00809     if (!ptr) {
00810         Com_Error( ERR_DROP, "Z_Free: NULL pointer" );
00811     }
00812 
00813     block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
00814     if (block->id != ZONEID) {
00815         Com_Error( ERR_FATAL, "Z_Free: freed a pointer without ZONEID" );
00816     }
00817     if (block->tag == 0) {
00818         Com_Error( ERR_FATAL, "Z_Free: freed a freed pointer" );
00819     }
00820     // if static memory
00821     if (block->tag == TAG_STATIC) {
00822         return;
00823     }
00824 
00825     // check the memory trash tester
00826     if ( *(int *)((byte *)block + block->size - 4 ) != ZONEID ) {
00827         Com_Error( ERR_FATAL, "Z_Free: memory block wrote past end" );
00828     }
00829 
00830     if (block->tag == TAG_SMALL) {
00831         zone = smallzone;
00832     }
00833     else {
00834         zone = mainzone;
00835     }
00836 
00837     zone->used -= block->size;
00838     // set the block to something that should cause problems
00839     // if it is referenced...
00840     Com_Memset( ptr, 0xaa, block->size - sizeof( *block ) );
00841 
00842     block->tag = 0;     // mark as free
00843     
00844     other = block->prev;
00845     if (!other->tag) {
00846         // merge with previous free block
00847         other->size += block->size;
00848         other->next = block->next;
00849         other->next->prev = other;
00850         if (block == zone->rover) {
00851             zone->rover = other;
00852         }
00853         block = other;
00854     }
00855 
00856     zone->rover = block;
00857 
00858     other = block->next;
00859     if ( !other->tag ) {
00860         // merge the next free block onto the end
00861         block->size += other->size;
00862         block->next = other->next;
00863         block->next->prev = block;
00864         if (other == zone->rover) {
00865             zone->rover = block;
00866         }
00867     }
00868 }
00869 
00870 
00871 /*
00872 ================
00873 Z_FreeTags
00874 ================
00875 */
00876 void Z_FreeTags( int tag ) {
00877     int         count;
00878     memzone_t   *zone;
00879 
00880     if ( tag == TAG_SMALL ) {
00881         zone = smallzone;
00882     }
00883     else {
00884         zone = mainzone;
00885     }
00886     count = 0;
00887     // use the rover as our pointer, because
00888     // Z_Free automatically adjusts it
00889     zone->rover = zone->blocklist.next;
00890     do {
00891         if ( zone->rover->tag == tag ) {
00892             count++;
00893             Z_Free( (void *)(zone->rover + 1) );
00894             continue;
00895         }
00896         zone->rover = zone->rover->next;
00897     } while ( zone->rover != &zone->blocklist );
00898 }
00899 
00900 
00901 /*
00902 ================
00903 Z_TagMalloc
00904 ================
00905 */
00906 #ifdef ZONE_DEBUG
00907 void *Z_TagMallocDebug( int size, int tag, char *label, char *file, int line ) {
00908 #else
00909 void *Z_TagMalloc( int size, int tag ) {
00910 #endif
00911     int     extra, allocSize;
00912     memblock_t  *start, *rover, *new, *base;
00913     memzone_t *zone;
00914 
00915     if (!tag) {
00916         Com_Error( ERR_FATAL, "Z_TagMalloc: tried to use a 0 tag" );
00917     }
00918 
00919     if ( tag == TAG_SMALL ) {
00920         zone = smallzone;
00921     }
00922     else {
00923         zone = mainzone;
00924     }
00925 
00926     allocSize = size;
00927     //
00928     // scan through the block list looking for the first free block
00929     // of sufficient size
00930     //
00931     size += sizeof(memblock_t); // account for size of block header
00932     size += 4;                  // space for memory trash tester
00933     size = (size + 3) & ~3;     // align to 32 bit boundary
00934     
00935     base = rover = zone->rover;
00936     start = base->prev;
00937     
00938     do {
00939         if (rover == start) {
00940 #ifdef ZONE_DEBUG
00941             Z_LogHeap();
00942 #endif
00943             // scaned all the way around the list
00944             Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone",
00945                                 size, zone == smallzone ? "small" : "main");
00946             return NULL;
00947         }
00948         if (rover->tag) {
00949             base = rover = rover->next;
00950         } else {
00951             rover = rover->next;
00952         }
00953     } while (base->tag || base->size < size);
00954     
00955     //
00956     // found a block big enough
00957     //
00958     extra = base->size - size;
00959     if (extra > MINFRAGMENT) {
00960         // there will be a free fragment after the allocated block
00961         new = (memblock_t *) ((byte *)base + size );
00962         new->size = extra;
00963         new->tag = 0;           // free block
00964         new->prev = base;
00965         new->id = ZONEID;
00966         new->next = base->next;
00967         new->next->prev = new;
00968         base->next = new;
00969         base->size = size;
00970     }
00971     
00972     base->tag = tag;            // no longer a free block
00973     
00974     zone->rover = base->next;   // next allocation will start looking here
00975     zone->used += base->size;   //
00976     
00977     base->id = ZONEID;
00978 
00979 #ifdef ZONE_DEBUG
00980     base->d.label = label;
00981     base->d.file = file;
00982     base->d.line = line;
00983     base->d.allocSize = allocSize;
00984 #endif
00985 
00986     // marker for memory trash testing
00987     *(int *)((byte *)base + base->size - 4) = ZONEID;
00988 
00989     return (void *) ((byte *)base + sizeof(memblock_t));
00990 }
00991 
00992 /*
00993 ========================
00994 Z_Malloc
00995 ========================
00996 */
00997 #ifdef ZONE_DEBUG
00998 void *Z_MallocDebug( int size, char *label, char *file, int line ) {
00999 #else
01000 void *Z_Malloc( int size ) {
01001 #endif
01002     void    *buf;
01003     
01004   //Z_CheckHeap (); // DEBUG
01005 
01006 #ifdef ZONE_DEBUG
01007     buf = Z_TagMallocDebug( size, TAG_GENERAL, label, file, line );
01008 #else
01009     buf = Z_TagMalloc( size, TAG_GENERAL );
01010 #endif
01011     Com_Memset( buf, 0, size );
01012 
01013     return buf;
01014 }
01015 
01016 #ifdef ZONE_DEBUG
01017 void *S_MallocDebug( int size, char *label, char *file, int line ) {
01018     return Z_TagMallocDebug( size, TAG_SMALL, label, file, line );
01019 }
01020 #else
01021 void *S_Malloc( int size ) {
01022     return Z_TagMalloc( size, TAG_SMALL );
01023 }
01024 #endif
01025 
01026 /*
01027 ========================
01028 Z_CheckHeap
01029 ========================
01030 */
01031 void Z_CheckHeap( void ) {
01032     memblock_t  *block;
01033     
01034     for (block = mainzone->blocklist.next ; ; block = block->next) {
01035         if (block->next == &mainzone->blocklist) {
01036             break;          // all blocks have been hit
01037         }
01038         if ( (byte *)block + block->size != (byte *)block->next)
01039             Com_Error( ERR_FATAL, "Z_CheckHeap: block size does not touch the next block\n" );
01040         if ( block->next->prev != block) {
01041             Com_Error( ERR_FATAL, "Z_CheckHeap: next block doesn't have proper back link\n" );
01042         }
01043         if ( !block->tag && !block->next->tag ) {
01044             Com_Error( ERR_FATAL, "Z_CheckHeap: two consecutive free blocks\n" );
01045         }
01046     }
01047 }
01048 
01049 /*
01050 ========================
01051 Z_LogZoneHeap
01052 ========================
01053 */
01054 void Z_LogZoneHeap( memzone_t *zone, char *name ) {
01055 #ifdef ZONE_DEBUG
01056     char dump[32], *ptr;
01057     int  i, j;
01058 #endif
01059     memblock_t  *block;
01060     char        buf[4096];
01061     int size, allocSize, numBlocks;
01062 
01063     if (!logfile || !FS_Initialized())
01064         return;
01065     size = allocSize = numBlocks = 0;
01066     Com_sprintf(buf, sizeof(buf), "\r\n================\r\n%s log\r\n================\r\n", name);
01067     FS_Write(buf, strlen(buf), logfile);
01068     for (block = zone->blocklist.next ; block->next != &zone->blocklist; block = block->next) {
01069         if (block->tag) {
01070 #ifdef ZONE_DEBUG
01071             ptr = ((char *) block) + sizeof(memblock_t);
01072             j = 0;
01073             for (i = 0; i < 20 && i < block->d.allocSize; i++) {
01074                 if (ptr[i] >= 32 && ptr[i] < 127) {
01075                     dump[j++] = ptr[i];
01076                 }
01077                 else {
01078                     dump[j++] = '_';
01079                 }
01080             }
01081             dump[j] = '\0';
01082             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);
01083             FS_Write(buf, strlen(buf), logfile);
01084             allocSize += block->d.allocSize;
01085 #endif
01086             size += block->size;
01087             numBlocks++;
01088         }
01089     }
01090 #ifdef ZONE_DEBUG
01091     // subtract debug memory
01092     size -= numBlocks * sizeof(zonedebug_t);
01093 #else
01094     allocSize = numBlocks * sizeof(memblock_t); // + 32 bit alignment
01095 #endif
01096     Com_sprintf(buf, sizeof(buf), "%d %s memory in %d blocks\r\n", size, name, numBlocks);
01097     FS_Write(buf, strlen(buf), logfile);
01098     Com_sprintf(buf, sizeof(buf), "%d %s memory overhead\r\n", size - allocSize, name);
01099     FS_Write(buf, strlen(buf), logfile);
01100 }
01101 
01102 /*
01103 ========================
01104 Z_LogHeap
01105 ========================
01106 */
01107 void Z_LogHeap( void ) {
01108     Z_LogZoneHeap( mainzone, "MAIN" );
01109     Z_LogZoneHeap( smallzone, "SMALL" );
01110 }
01111 
01112 // static mem blocks to reduce a lot of small zone overhead
01113 typedef struct memstatic_s {
01114     memblock_t b;
01115     byte mem[2];
01116 } memstatic_t;
01117 
01118 // bk001204 - initializer brackets
01119 memstatic_t emptystring =
01120     { {(sizeof(memblock_t)+2 + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'\0', '\0'} };
01121 memstatic_t numberstring[] = {
01122     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'0', '\0'} },
01123     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'1', '\0'} },
01124     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'2', '\0'} },
01125     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'3', '\0'} },
01126     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'4', '\0'} },
01127     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'5', '\0'} },
01128     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'6', '\0'} },
01129     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'7', '\0'} },
01130     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'8', '\0'} }, 
01131     { {(sizeof(memstatic_t) + 3) & ~3, TAG_STATIC, NULL, NULL, ZONEID}, {'9', '\0'} }
01132 };
01133 
01134 /*
01135 ========================
01136 CopyString
01137 
01138  NOTE:  never write over the memory CopyString returns because
01139         memory from a memstatic_t might be returned
01140 ========================
01141 */
01142 char *CopyString( const char *in ) {
01143     char    *out;
01144 
01145     if (!in[0]) {
01146         return ((char *)&emptystring) + sizeof(memblock_t);
01147     }
01148     else if (!in[1]) {
01149         if (in[0] >= '0' && in[0] <= '9') {
01150             return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t);
01151         }
01152     }
01153     out = S_Malloc (strlen(in)+1);
01154     strcpy (out, in);
01155     return out;
01156 }
01157 
01158 /*
01159 ==============================================================================
01160 
01161 Goals:
01162     reproducable without history effects -- no out of memory errors on weird map to map changes
01163     allow restarting of the client without fragmentation
01164     minimize total pages in use at run time
01165     minimize total pages needed during load time
01166 
01167   Single block of memory with stack allocators coming from both ends towards the middle.
01168 
01169   One side is designated the temporary memory allocator.
01170 
01171   Temporary memory can be allocated and freed in any order.
01172 
01173   A highwater mark is kept of the most in use at any time.
01174 
01175   When there is no temporary memory allocated, the permanent and temp sides
01176   can be switched, allowing the already touched temp memory to be used for
01177   permanent storage.
01178 
01179   Temp memory must never be allocated on two ends at once, or fragmentation
01180   could occur.
01181 
01182   If we have any in-use temp memory, additional temp allocations must come from
01183   that side.
01184 
01185   If not, we can choose to make either side the new temp side and push future
01186   permanent allocations to the other side.  Permanent allocations should be
01187   kept on the side that has the current greatest wasted highwater mark.
01188 
01189 ==============================================================================
01190 */
01191 
01192 
01193 #define HUNK_MAGIC  0x89537892
01194 #define HUNK_FREE_MAGIC 0x89537893
01195 
01196 typedef struct {
01197     int     magic;
01198     int     size;
01199 } hunkHeader_t;
01200 
01201 typedef struct {
01202     int     mark;
01203     int     permanent;
01204     int     temp;
01205     int     tempHighwater;
01206 } hunkUsed_t;
01207 
01208 typedef struct hunkblock_s {
01209     int size;
01210     byte printed;
01211     struct hunkblock_s *next;
01212     char *label;
01213     char *file;
01214     int line;
01215 } hunkblock_t;
01216 
01217 static  hunkblock_t *hunkblocks;
01218 
01219 static  hunkUsed_t  hunk_low, hunk_high;
01220 static  hunkUsed_t  *hunk_permanent, *hunk_temp;
01221 
01222 static  byte    *s_hunkData = NULL;
01223 static  int     s_hunkTotal;
01224 
01225 static  int     s_zoneTotal;
01226 static  int     s_smallZoneTotal;
01227 
01228 
01229 /*
01230 =================
01231 Com_Meminfo_f
01232 =================
01233 */
01234 void Com_Meminfo_f( void ) {
01235     memblock_t  *block;
01236     int         zoneBytes, zoneBlocks;
01237     int         smallZoneBytes, smallZoneBlocks;
01238     int         botlibBytes, rendererBytes;
01239     int         unused;
01240 
01241     zoneBytes = 0;
01242     botlibBytes = 0;
01243     rendererBytes = 0;
01244     zoneBlocks = 0;
01245     for (block = mainzone->blocklist.next ; ; block = block->next) {
01246         if ( Cmd_Argc() != 1 ) {
01247             Com_Printf ("block:%p    size:%7i    tag:%3i\n",
01248                 block, block->size, block->tag);
01249         }
01250         if ( block->tag ) {
01251             zoneBytes += block->size;
01252             zoneBlocks++;
01253             if ( block->tag == TAG_BOTLIB ) {
01254                 botlibBytes += block->size;
01255             } else if ( block->tag == TAG_RENDERER ) {
01256                 rendererBytes += block->size;
01257             }
01258         }
01259 
01260         if (block->next == &mainzone->blocklist) {
01261             break;          // all blocks have been hit 
01262         }
01263         if ( (byte *)block + block->size != (byte *)block->next) {
01264             Com_Printf ("ERROR: block size does not touch the next block\n");
01265         }
01266         if ( block->next->prev != block) {
01267             Com_Printf ("ERROR: next block doesn't have proper back link\n");
01268         }
01269         if ( !block->tag && !block->next->tag ) {
01270             Com_Printf ("ERROR: two consecutive free blocks\n");
01271         }
01272     }
01273 
01274     smallZoneBytes = 0;
01275     smallZoneBlocks = 0;
01276     for (block = smallzone->blocklist.next ; ; block = block->next) {
01277         if ( block->tag ) {
01278             smallZoneBytes += block->size;
01279             smallZoneBlocks++;
01280         }
01281 
01282         if (block->next == &smallzone->blocklist) {
01283             break;          // all blocks have been hit 
01284         }
01285     }
01286 
01287     Com_Printf( "%8i bytes total hunk\n", s_hunkTotal );
01288     Com_Printf( "%8i bytes total zone\n", s_zoneTotal );
01289     Com_Printf( "\n" );
01290     Com_Printf( "%8i low mark\n", hunk_low.mark );
01291     Com_Printf( "%8i low permanent\n", hunk_low.permanent );
01292     if ( hunk_low.temp != hunk_low.permanent ) {
01293         Com_Printf( "%8i low temp\n", hunk_low.temp );
01294     }
01295     Com_Printf( "%8i low tempHighwater\n", hunk_low.tempHighwater );
01296     Com_Printf( "\n" );
01297     Com_Printf( "%8i high mark\n", hunk_high.mark );
01298     Com_Printf( "%8i high permanent\n", hunk_high.permanent );
01299     if ( hunk_high.temp != hunk_high.permanent ) {
01300         Com_Printf( "%8i high temp\n", hunk_high.temp );
01301     }
01302     Com_Printf( "%8i high tempHighwater\n", hunk_high.tempHighwater );
01303     Com_Printf( "\n" );
01304     Com_Printf( "%8i total hunk in use\n", hunk_low.permanent + hunk_high.permanent );
01305     unused = 0;
01306     if ( hunk_low.tempHighwater > hunk_low.permanent ) {
01307         unused += hunk_low.tempHighwater - hunk_low.permanent;
01308     }
01309     if ( hunk_high.tempHighwater > hunk_high.permanent ) {
01310         unused += hunk_high.tempHighwater - hunk_high.permanent;
01311     }
01312     Com_Printf( "%8i unused highwater\n", unused );
01313     Com_Printf( "\n" );
01314     Com_Printf( "%8i bytes in %i zone blocks\n", zoneBytes, zoneBlocks  );
01315     Com_Printf( "        %8i bytes in dynamic botlib\n", botlibBytes );
01316     Com_Printf( "        %8i bytes in dynamic renderer\n", rendererBytes );
01317     Com_Printf( "        %8i bytes in dynamic other\n", zoneBytes - ( botlibBytes + rendererBytes ) );
01318     Com_Printf( "        %8i bytes in small Zone memory\n", smallZoneBytes );
01319 }
01320 
01321 /*
01322 ===============
01323 Com_TouchMemory
01324 
01325 Touch all known used data to make sure it is paged in
01326 ===============
01327 */
01328 void Com_TouchMemory( void ) {
01329     int     start, end;
01330     int     i, j;
01331     int     sum;
01332     memblock_t  *block;
01333 
01334     Z_CheckHeap();
01335 
01336     start = Sys_Milliseconds();
01337 
01338     sum = 0;
01339 
01340     j = hunk_low.permanent >> 2;
01341     for ( i = 0 ; i < j ; i+=64 ) {         // only need to touch each page
01342         sum += ((int *)s_hunkData)[i];
01343     }
01344 
01345     i = ( s_hunkTotal - hunk_high.permanent ) >> 2;
01346     j = hunk_high.permanent >> 2;
01347     for (  ; i < j ; i+=64 ) {          // only need to touch each page
01348         sum += ((int *)s_hunkData)[i];
01349     }
01350 
01351     for (block = mainzone->blocklist.next ; ; block = block->next) {
01352         if ( block->tag ) {
01353             j = block->size >> 2;
01354             for ( i = 0 ; i < j ; i+=64 ) {             // only need to touch each page
01355                 sum += ((int *)block)[i];
01356             }
01357         }
01358         if ( block->next == &mainzone->blocklist ) {
01359             break;          // all blocks have been hit 
01360         }
01361     }
01362 
01363     end = Sys_Milliseconds();
01364 
01365     Com_Printf( "Com_TouchMemory: %i msec\n", end - start );
01366 }
01367 
01368 
01369 
01370 /*
01371 =================
01372 Com_InitZoneMemory
01373 =================
01374 */
01375 void Com_InitSmallZoneMemory( void ) {
01376     s_smallZoneTotal = 512 * 1024;
01377     // bk001205 - was malloc
01378     smallzone = calloc( s_smallZoneTotal, 1 );
01379     if ( !smallzone ) {
01380         Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal / (1024*1024) );
01381     }
01382     Z_ClearZone( smallzone, s_smallZoneTotal );
01383     
01384