/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
IceTech Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with IceTech Source Code. If not, see .
In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "../renderer/Image.h"
#include "Session_local.h"
#define MAX_PRINT_MSG_SIZE 4096
#define MAX_WARNING_LIST 256
typedef enum {
ERP_NONE,
ERP_FATAL, // exit the entire game with a popup window
ERP_DROP, // print to console and disconnect from game
ERP_DISCONNECT // don't kill server
} errorParm_t;
#if defined( _DEBUG )
#define BUILD_DEBUG "-debug"
#else
#define BUILD_DEBUG ""
#endif
struct version_s {
version_s( void ) { sprintf( string, "%s.%d%s %s %s %s", ENGINE_VERSION, BUILD_NUMBER, BUILD_DEBUG, BUILD_STRING, __DATE__, __TIME__ ); }
char string[256];
} version;
idCVar com_version( "si_version", version.string, CVAR_SYSTEM|CVAR_ROM|CVAR_SERVERINFO, "engine version" );
idCVar com_skipRenderer( "com_skipRenderer", "0", CVAR_BOOL|CVAR_SYSTEM, "skip the renderer completely" );
idCVar com_machineSpec( "com_machineSpec", "-1", CVAR_INTEGER | CVAR_ARCHIVE | CVAR_SYSTEM, "hardware classification, -1 = not detected, 0 = low quality, 1 = medium quality, 2 = high quality, 3 = ultra quality" );
idCVar com_purgeAll( "com_purgeAll", "0", CVAR_BOOL | CVAR_ARCHIVE | CVAR_SYSTEM, "purge everything between level loads" );
idCVar com_memoryMarker( "com_memoryMarker", "-1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_INIT, "used as a marker for memory stats" );
idCVar com_preciseTic( "com_preciseTic", "1", CVAR_BOOL|CVAR_SYSTEM, "run one game tick every async thread update" );
idCVar com_asyncInput( "com_asyncInput", "0", CVAR_BOOL|CVAR_SYSTEM, "sample input from the async thread" );
#define ASYNCSOUND_INFO "0: mix sound inline, 1: memory mapped async mix, 2: callback mixing, 3: write async mix"
#if defined( MACOS_X )
idCVar com_asyncSound( "com_asyncSound", "2", CVAR_INTEGER|CVAR_SYSTEM|CVAR_ROM, ASYNCSOUND_INFO );
#elif defined( __linux__ )
idCVar com_asyncSound( "com_asyncSound", "3", CVAR_INTEGER|CVAR_SYSTEM|CVAR_ROM, ASYNCSOUND_INFO );
#else
idCVar com_asyncSound( "com_asyncSound", "1", CVAR_INTEGER|CVAR_SYSTEM, ASYNCSOUND_INFO, 0, 1 );
#endif
idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "force generic platform independent SIMD" );
idCVar com_developer( "developer", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "developer mode" );
idCVar com_allowConsole( "com_allowConsole", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "allow toggling console with the tilde key" );
idCVar com_speeds( "com_speeds", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show engine timings" );
idCVar com_showFPS( "com_showFPS", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_ARCHIVE|CVAR_NOCHEAT, "show frames rendered per second" );
idCVar com_showMemoryUsage( "com_showMemoryUsage", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show total and per frame memory usage" );
idCVar com_showAsyncStats( "com_showAsyncStats", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show async network stats" );
idCVar com_showSoundDecoders( "com_showSoundDecoders", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show sound decoders" );
idCVar com_timestampPrints( "com_timestampPrints", "0", CVAR_SYSTEM, "print time with each console print, 1 = msec, 2 = sec", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar com_timescale( "timescale", "1", CVAR_SYSTEM | CVAR_FLOAT, "scales the time", 0.1f, 10.0f );
idCVar com_logFile( "logFile", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "1 = buffer log, 2 = flush after each print", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar com_logFileName( "logFileName", "qconsole.log", CVAR_SYSTEM | CVAR_NOCHEAT, "name of log file, if empty, qconsole.log will be used" );
idCVar com_makingBuild( "com_makingBuild", "0", CVAR_BOOL | CVAR_SYSTEM, "1 when making a build" );
idCVar com_updateLoadSize( "com_updateLoadSize", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "update the load size after loading a map" );
idCVar com_videoRam( "com_videoRam", "64", CVAR_INTEGER | CVAR_SYSTEM | CVAR_NOCHEAT | CVAR_ARCHIVE, "holds the last amount of detected video ram" );
#ifdef QUAKE4
idCVar com_skipUltraQuality( "com_skipUltraQuality", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_ARCHIVE, "skip ultra quality as an auto-detect target" );
idCVar com_Bundler( "com_Bundler", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_INIT, "run in bundle creation mode" );
#ifndef _XENON
idCVar com_MakeLoadScreens( "com_MakeLoadScreens", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_INIT, "generate load screen data" );
#endif
idCVar com_QuickLoad( "com_QuickLoad", "0", CVAR_BOOL | CVAR_SYSTEM, "allow quick load/save behavior" );
idCVar com_Limits( "com_Limits", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "show engine limit information" );
idCVar com_BinaryWrite( "com_BinaryWrite", "0", CVAR_BOOL | CVAR_SYSTEM, "write binary lexer data" );
idCVar com_BinaryRead( "com_BinaryRead", "0", CVAR_BOOL | CVAR_SYSTEM, "read binary lexer data" );
#ifdef RV_BINARYDECLS
idCVar com_BinaryDeclRead( "com_BinaryDeclRead", "0", CVAR_BOOL | CVAR_SYSTEM, "read binary decl data" );
#endif
#ifdef RV_SINGLE_DECL_FILE
idCVar com_SingleDeclFile( "com_SingleDeclFile", "0", CVAR_BOOL | CVAR_SYSTEM, "load decls from a single file" );
idCVar com_WriteSingleDeclFIle( "com_WriteSingleDeclFile", "0", CVAR_BOOL | CVAR_SYSTEM, "write decls to a single file" );
#endif
idCVar com_uniqueWarnings( "com_uniqueWarnings", "1", CVAR_BOOL | CVAR_SYSTEM | CVAR_ARCHIVE, "only print a unique warning once" );
idCVar com_showMemAllocTags( "com_showMemAllocTags", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "show Mem_Alloc tag statistics" );
#if defined(_XENON)
idCVar com_showXenTexCacheStats( "com_showXenTexCacheStats", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "show Xenon texture cache stats" );
idCVar com_showXenHardwareTimers( "com_showXenHardwareTimers", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "show Xenon hardware timers" );
idCVar com_showXenThreadUsage( "com_showXenThreadUsage", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "show Xenon thread usage" );
#endif
#endif
idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." );
idCVar com_engineHz("com_engineHz", "90", CVAR_FLOAT | CVAR_ARCHIVE, "Frames per second the engine runs at", 10.0f, 1024.0f);
idCVar com_deltaTimeClamp("com_deltaTimeClamp", "50", CVAR_INTEGER, "don't process more than this time in a single frame");
float com_engineHz_latched = 90.0f; // Latched version of cvar, updated between map loads
int64_t com_engineHz_numerator = 100LL * 1000LL;
int64_t com_engineHz_denominator = 100LL * 60LL;
// com_speeds times
int time_gameFrame;
int time_gameDraw;
int time_frontend; // renderSystem frontend time
int time_backend; // renderSystem backend time
#ifdef QUAKE4
int time_waiting; // wait time, surfaced by Quake 4's timing/debug HUD path
bool com_debugHudActive; // true when the game debug HUD is active
#endif
int com_frameTime; // time for the current frame in milliseconds
int com_frameNumber; // variable frame number
volatile int com_ticNumber; // 60 hz tics
int com_editors; // currently opened editor(s)
bool com_editorActive; // true if an editor has focus
#ifdef _WIN32
HWND com_hwndMsg = NULL;
bool com_outputMsg = false;
unsigned int com_msgID = -1;
#endif
#ifdef __DOOM_DLL__
idGame * game = NULL;
idGameEdit * gameEdit = NULL;
#endif
// writes si_version to the config file - in a kinda obfuscated way
//#define ID_WRITE_VERSION
#ifdef QUAKE4
/*
==================
Com_GetVersion
==================
*/
const char *Com_GetVersion( void ) {
return cvarSystem->GetCVarString( "si_version" );
}
/*
==================
Com_GetErrorMessage
==================
*/
const char *Com_GetErrorMessage( void ) {
return common ? common->GetErrorMessage() : "";
}
void GSSStartData( void ) {
}
void GSSEndData( void ) {
}
const char *Com_GetStringValue( void *var ) {
return var ? *( reinterpret_cast( var ) + 1 ) : "";
}
class rvLanguage {
public:
rvLanguage( void ) { hasVO = false; }
void SetLanguage( const idStr &lang ) { language = lang; }
const char * GetLanguage( void ) const { return language.c_str(); }
void SetHasVO( bool in ) { hasVO = in; }
bool HasVO( void ) const { return hasVO; }
bool Load( const char *fileName, bool clear = false ) { return strings.Load( fileName, clear ); }
const idLangKeyValue *GetString( int i ) const { return strings.GetKeyVal( i ); }
const char * GetString( const char *token ) const { return strings.GetString( token ); }
int GetNumStrings( void ) const { return strings.GetNumKeyVals(); }
const idLangDict *GetDict( void ) const { return &strings; }
idStr language;
idLangDict strings;
bool hasVO;
};
/*
==================
MemInfo::MemInfo
==================
*/
MemInfo::MemInfo( void ) {
total = 0;
assetTotals = 0;
imageAssetsTotal = 0;
modelAssetsTotal = 0;
soundAssetsTotal = 0;
collAssetsTotal = 0;
animsAssetsTotal = 0;
aasAssetsTotal = 0;
imageAssetsCount = 0;
modelAssetsCount = 0;
soundAssetsCount = 0;
collAssetsCount = 0;
animsAssetsCount = 0;
aasAssetsCount = 0;
}
#endif
#include "Common_local.h"
idCommonLocal commonLocal;
idCommon * common = &commonLocal;
/*
==================
idCommonLocal::idCommonLocal
==================
*/
idCommonLocal::idCommonLocal( void ) {
com_fullyInitialized = false;
com_refreshOnPrint = false;
com_errorEntered = 0;
com_shuttingDown = false;
gameFrame = 0;
gameTimeResidual = 0;
logFile = NULL;
strcpy( errorMessage, "" );
rd_buffer = NULL;
rd_buffersize = 0;
rd_flush = NULL;
#ifdef QUAKE4
rd_rcon = false;
languageDictIndex = 0;
desiredMachineSpec = -1;
com_renderableGameFrame = true;
memset( warningCount, 0, sizeof( warningCount ) );
#endif
gameDLL = 0;
#ifdef ID_WRITE_VERSION
config_compressor = NULL;
#endif
}
/*
==================
idCommonLocal::BeginRedirect
==================
*/
#ifdef QUAKE4
void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *), bool rcon ) {
#else
void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *) ) {
#endif
if ( !buffer || !buffersize || !flush ) {
return;
}
rd_buffer = buffer;
rd_buffersize = buffersize;
rd_flush = flush;
#ifdef QUAKE4
rd_rcon = rcon;
#endif
*rd_buffer = 0;
}
/*
==================
idCommonLocal::EndRedirect
==================
*/
void idCommonLocal::EndRedirect( void ) {
if ( rd_flush && rd_buffer[ 0 ] ) {
rd_flush( rd_buffer );
}
rd_buffer = NULL;
rd_buffersize = 0;
rd_flush = NULL;
#ifdef QUAKE4
rd_rcon = false;
#endif
}
#ifdef _WIN32
/*
==================
EnumWindowsProc
==================
*/
BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ) {
char buff[1024];
::GetWindowText( hwnd, buff, sizeof( buff ) );
if ( idStr::Icmpn( buff, EDITOR_WINDOWTEXT, strlen( EDITOR_WINDOWTEXT ) ) == 0 ) {
com_hwndMsg = hwnd;
return FALSE;
}
return TRUE;
}
/*
==================
FindEditor
==================
*/
bool FindEditor( void ) {
com_hwndMsg = NULL;
EnumWindows( EnumWindowsProc, 0 );
return !( com_hwndMsg == NULL );
}
#endif
/*
==================
idCommonLocal::CloseLogFile
==================
*/
void idCommonLocal::CloseLogFile( void ) {
if ( logFile ) {
com_logFile.SetBool( false ); // make sure no further VPrintf attempts to open the log file again
fileSystem->CloseFile( logFile );
logFile = NULL;
}
}
/*
==================
idCommonLocal::SetRefreshOnPrint
==================
*/
void idCommonLocal::SetRefreshOnPrint( bool set ) {
com_refreshOnPrint = set;
}
/*
==================
idCommonLocal::VPrintf
A raw string should NEVER be passed as fmt, because of "%f" type crashes.
==================
*/
void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
char msg[MAX_PRINT_MSG_SIZE];
int timeLength;
static bool logFileFailed = false;
// if the cvar system is not initialized
if ( !cvarSystem->IsInitialized() ) {
return;
}
// optionally put a timestamp at the beginning of each print,
// so we can see how long different init sections are taking
if ( com_timestampPrints.GetInteger() ) {
int t = Sys_Milliseconds();
if ( com_timestampPrints.GetInteger() == 1 ) {
t /= 1000;
}
sprintf( msg, "[%i]", t );
timeLength = strlen( msg );
} else {
timeLength = 0;
}
// don't overflow
if ( idStr::vsnPrintf( msg+timeLength, MAX_PRINT_MSG_SIZE-timeLength-1, fmt, args ) < 0 ) {
msg[sizeof(msg)-2] = '\n'; msg[sizeof(msg)-1] = '\0'; // avoid output garbling
Sys_Printf( "idCommon::VPrintf: truncated to %d characters\n", strlen(msg)-1 );
}
if ( rd_buffer ) {
if ( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) {
rd_flush( rd_buffer );
*rd_buffer = 0;
}
strcat( rd_buffer, msg );
return;
}
// echo to console buffer
console->Print( msg );
// remove any color codes
idStr::RemoveColors( msg );
// echo to dedicated console and early console
Sys_Printf( "%s", msg );
// print to script debugger server
// DebuggerServerPrint( msg );
#if 0 // !@#
#if defined(_DEBUG) && defined(WIN32)
if ( strlen( msg ) < 512 ) {
TRACE( msg );
}
#endif
#endif
// logFile
if ( com_logFile.GetInteger() && !logFileFailed && fileSystem->IsInitialized() ) {
static bool recursing;
if ( !logFile && !recursing ) {
struct tm *newtime;
ID_TIME_T aclock;
const char *fileName = com_logFileName.GetString()[0] ? com_logFileName.GetString() : "qconsole.log";
// fileSystem->OpenFileWrite can cause recursive prints into here
recursing = true;
logFile = fileSystem->OpenFileWrite( fileName );
if ( !logFile ) {
logFileFailed = true;
FatalError( "failed to open log file '%s'\n", fileName );
}
recursing = false;
if ( com_logFile.GetInteger() > 1 ) {
// force it to not buffer so we get valid
// data even if we are crashing
logFile->ForceFlush();
}
time( &aclock );
newtime = localtime( &aclock );
Printf( "log file '%s' opened on %s\n", fileName, asctime( newtime ) );
}
if ( logFile ) {
logFile->Write( msg, strlen( msg ) );
logFile->Flush(); // ForceFlush doesn't help a whole lot
}
}
// don't trigger any updates if we are in the process of doing a fatal error
if ( com_errorEntered != ERP_FATAL ) {
// update the console if we are in a long-running command, like dmap
if ( com_refreshOnPrint ) {
session->UpdateScreen();
}
// let session redraw the animated loading screen if necessary
session->PacifierUpdate();
}
#ifdef _WIN32
if ( com_outputMsg ) {
if ( com_msgID == -1 ) {
com_msgID = ::RegisterWindowMessage( DMAP_MSGID );
if ( !FindEditor() ) {
com_outputMsg = false;
} else {
Sys_ShowWindow( false );
}
}
if ( com_hwndMsg ) {
ATOM atom = ::GlobalAddAtom( msg );
::PostMessage( com_hwndMsg, com_msgID, 0, static_cast(atom) );
}
}
#endif
}
/*
==================
idCommonLocal::Printf
Both client and server can use this, and it will output to the appropriate place.
A raw string should NEVER be passed as fmt, because of "%f" type crashers.
==================
*/
void idCommonLocal::Printf( const char *fmt, ... ) {
va_list argptr;
va_start( argptr, fmt );
VPrintf( fmt, argptr );
va_end( argptr );
}
/*
==================
idCommonLocal::DPrintf
prints message that only shows up if the "developer" cvar is set
==================
*/
void idCommonLocal::DPrintf( const char *fmt, ... ) {
va_list argptr;
char msg[MAX_PRINT_MSG_SIZE];
if ( !cvarSystem->IsInitialized() || !com_developer.GetBool() ) {
return; // don't confuse non-developers with techie stuff...
}
va_start( argptr, fmt );
idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
va_end( argptr );
msg[sizeof(msg)-1] = '\0';
// never refresh the screen, which could cause reentrency problems
bool temp = com_refreshOnPrint;
com_refreshOnPrint = false;
Printf( S_COLOR_RED"%s", msg );
com_refreshOnPrint = temp;
}
/*
==================
idCommonLocal::DWarning
prints warning message in yellow that only shows up if the "developer" cvar is set
==================
*/
void idCommonLocal::DWarning( const char *fmt, ... ) {
va_list argptr;
char msg[MAX_PRINT_MSG_SIZE];
if ( !com_developer.GetBool() ) {
return; // don't confuse non-developers with techie stuff...
}
va_start( argptr, fmt );
idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
va_end( argptr );
msg[sizeof(msg)-1] = '\0';
Printf( S_COLOR_YELLOW"WARNING: %s\n", msg );
}
/*
==================
idCommonLocal::Warning
prints WARNING %s and adds the warning message to a queue to be printed later on
==================
*/
void idCommonLocal::Warning( const char *fmt, ... ) {
va_list argptr;
char msg[MAX_PRINT_MSG_SIZE];
va_start( argptr, fmt );
idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
va_end( argptr );
msg[sizeof(msg)-1] = 0;
#ifdef QUAKE4
int warningIndex = -1;
for ( int i = 0; i < warningList.Num(); i++ ) {
if ( idStr::Cmp( warningList[i].c_str(), msg ) == 0 ) {
warningIndex = i;
break;
}
}
if ( warningIndex < 0 && warningList.Num() < MAX_WARNING_LIST ) {
warningIndex = warningList.Append( msg );
warningCount[warningIndex] = 0;
}
bool printWarning = true;
if ( warningIndex >= 0 ) {
if ( com_uniqueWarnings.GetBool() && warningCount[warningIndex] > 0 ) {
printWarning = false;
}
if ( warningCount[warningIndex] < 1000 ) {
warningCount[warningIndex]++;
}
}
if ( printWarning ) {
Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
}
#else
Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
if ( warningList.Num() < MAX_WARNING_LIST ) {
warningList.AddUnique( msg );
}
#endif
}
/*
==================
idCommonLocal::PrintWarnings
==================
*/
void idCommonLocal::PrintWarnings( void ) {
int i;
if ( !warningList.Num() ) {
return;
}
#ifndef QUAKE4
warningList.Sort();
#endif
Printf( "------------- Warnings ---------------\n" );
Printf( "during %s...\n", warningCaption.c_str() );
for ( i = 0; i < warningList.Num(); i++ ) {
Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", warningList[i].c_str() );
}
if ( warningList.Num() ) {
if ( warningList.Num() >= MAX_WARNING_LIST ) {
Printf( "more than %d warnings\n", MAX_WARNING_LIST );
} else {
Printf( "%d warnings\n", warningList.Num() );
}
}
}
/*
==================
idCommonLocal::ClearWarnings
==================
*/
void idCommonLocal::ClearWarnings( const char *reason ) {
warningCaption = reason;
warningList.Clear();
#ifdef QUAKE4
memset( warningCount, 0, sizeof( warningCount ) );
#endif
}
/*
==================
idCommonLocal::DumpWarnings
==================
*/
void idCommonLocal::DumpWarnings( void ) {
int i;
idFile *warningFile;
if ( !warningList.Num() ) {
return;
}
warningFile = fileSystem->OpenFileWrite( "warnings.txt", "fs_savepath" );
if ( warningFile ) {
warningFile->Printf( "------------- Warnings ---------------\n\n" );
warningFile->Printf( "during %s...\n", warningCaption.c_str() );
#ifndef QUAKE4
warningList.Sort();
#endif
for ( i = 0; i < warningList.Num(); i++ ) {
warningList[i].RemoveColors();
#ifdef QUAKE4
warningFile->Printf( "WARNING: %s (%d times)\n", warningList[i].c_str(), warningCount[i] );
#else
warningFile->Printf( "WARNING: %s\n", warningList[i].c_str() );
#endif
}
if ( warningList.Num() >= MAX_WARNING_LIST ) {
warningFile->Printf( "\nmore than %d warnings!\n", MAX_WARNING_LIST );
} else {
warningFile->Printf( "\n%d warnings.\n", warningList.Num() );
}
warningFile->Printf( "\n\n-------------- Errors ---------------\n\n" );
errorList.Sort();
for ( i = 0; i < errorList.Num(); i++ ) {
errorList[i].RemoveColors();
warningFile->Printf( "ERROR: %s", errorList[i].c_str() );
}
warningFile->ForceFlush();
fileSystem->CloseFile( warningFile );
#if defined(_WIN32) && !defined(_DEBUG)
#ifdef QUAKE4
if ( !com_makingBuild.GetBool() && !com_updateLoadSize.GetBool() ) {
#endif
idStr osPath;
osPath = fileSystem->RelativePathToOSPath( "warnings.txt", "fs_savepath" );
WinExec( va( "Notepad.exe %s", osPath.c_str() ), SW_SHOW );
#ifdef QUAKE4
}
#endif
#endif
}
}
/*
==================
idCommonLocal::Error
==================
*/
void idCommonLocal::Error( const char *fmt, ... ) {
va_list argptr;
static int lastErrorTime;
static int errorCount;
int currentTime;
int code = ERP_DROP;
// always turn this off after an error
com_refreshOnPrint = false;
// when we are running automated scripts, make sure we
// know if anything failed
if ( cvarSystem->GetCVarInteger( "fs_copyfiles" ) ) {
code = ERP_FATAL;
}
// if we don't have GL running, make it a fatal error
if ( !renderSystem->IsOpenGLRunning() ) {
code = ERP_FATAL;
}
// if we got a recursive error, make it fatal
if ( com_errorEntered ) {
// if we are recursively erroring while exiting
// from a fatal error, just kill the entire
// process immediately, which will prevent a
// full screen rendering window covering the
// error dialog
if ( com_errorEntered == ERP_FATAL ) {
Sys_Quit();
}
code = ERP_FATAL;
}
// if we are getting a solid stream of ERP_DROP, do an ERP_FATAL
currentTime = Sys_Milliseconds();
if ( currentTime - lastErrorTime < 100 ) {
if ( ++errorCount > 3 ) {
code = ERP_FATAL;
}
} else {
errorCount = 0;
}
lastErrorTime = currentTime;
com_errorEntered = code;
va_start (argptr,fmt);
idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
va_end (argptr);
errorMessage[sizeof(errorMessage)-1] = '\0';
// copy the error message to the clip board
Sys_SetClipboardData( errorMessage );
// add the message to the error list
errorList.AddUnique( errorMessage );
// Dont shut down the session for gui editor or debugger
if ( !( com_editors & ( EDITOR_GUI | EDITOR_DEBUGGER ) ) ) {
session->Stop();
}
if ( code == ERP_DISCONNECT ) {
com_errorEntered = 0;
throw idException( errorMessage );
// The gui editor doesnt want thing to com_error so it handles exceptions instead
} else if( com_editors & ( EDITOR_GUI | EDITOR_DEBUGGER ) ) {
com_errorEntered = 0;
throw idException( errorMessage );
} else if ( code == ERP_DROP ) {
Printf( "********************\nERROR: %s\n********************\n", errorMessage );
com_errorEntered = 0;
throw idException( errorMessage );
} else {
Printf( "********************\nERROR: %s\n********************\n", errorMessage );
}
if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
}
Shutdown();
Sys_Error( "%s", errorMessage );
}
/*
==================
idCommonLocal::FatalError
Dump out of the game to a system dialog
==================
*/
void idCommonLocal::FatalError( const char *fmt, ... ) {
va_list argptr;
// if we got a recursive error, make it fatal
if ( com_errorEntered ) {
// if we are recursively erroring while exiting
// from a fatal error, just kill the entire
// process immediately, which will prevent a
// full screen rendering window covering the
// error dialog
Sys_Printf( "FATAL: recursed fatal error:\n%s\n", errorMessage );
va_start( argptr, fmt );
idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
va_end( argptr );
errorMessage[sizeof(errorMessage)-1] = '\0';
Sys_Printf( "%s\n", errorMessage );
// write the console to a log file?
Sys_Quit();
}
com_errorEntered = ERP_FATAL;
va_start( argptr, fmt );
idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
va_end( argptr );
errorMessage[sizeof(errorMessage)-1] = '\0';
if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
}
Sys_SetFatalError( errorMessage );
Shutdown();
Sys_Error( "%s", errorMessage );
}
/*
==================
idCommonLocal::Quit
==================
*/
void idCommonLocal::Quit( void ) {
#ifdef ID_ALLOW_TOOLS
if ( com_editors & EDITOR_RADIANT ) {
RadiantInit();
return;
}
#endif
// don't try to shutdown if we are in a recursive error
if ( !com_errorEntered ) {
Shutdown();
}
Sys_Quit();
}
/*
============================================================================
COMMAND LINE FUNCTIONS
+ characters separate the commandLine string into multiple console
command lines.
All of these are valid:
doom +set test blah +map test
doom set test blah+map test
doom set test blah + map test
============================================================================
*/
#define MAX_CONSOLE_LINES 32
int com_numConsoleLines;
idCmdArgs com_consoleLines[MAX_CONSOLE_LINES];
/*
==================
idCommonLocal::ParseCommandLine
==================
*/
void idCommonLocal::ParseCommandLine( int argc, const char **argv ) {
int i, current_count;
com_numConsoleLines = 0;
current_count = 0;
// API says no program path
for ( i = 0; i < argc; i++ ) {
if ( argv[ i ][ 0 ] == '+' ) {
com_numConsoleLines++;
com_consoleLines[ com_numConsoleLines-1 ].AppendArg( argv[ i ] + 1 );
} else {
if ( !com_numConsoleLines ) {
com_numConsoleLines++;
}
com_consoleLines[ com_numConsoleLines-1 ].AppendArg( argv[ i ] );
}
}
}
/*
==================
idCommonLocal::ClearCommandLine
==================
*/
void idCommonLocal::ClearCommandLine( void ) {
com_numConsoleLines = 0;
}
/*
==================
idCommonLocal::SafeMode
Check for "safe" on the command line, which will
skip loading of config file (DoomConfig.cfg)
==================
*/
bool idCommonLocal::SafeMode( void ) {
int i;
for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "safe" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "cvar_restart" ) ) {
com_consoleLines[ i ].Clear();
return true;
}
}
return false;
}
/*
==================
idCommonLocal::CheckToolMode
Check for "renderbump", "dmap", or "editor" on the command line,
and force fullscreen off in those cases
==================
*/
void idCommonLocal::CheckToolMode( void ) {
int i;
for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "guieditor" ) ) {
com_editors |= EDITOR_GUI;
}
else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "debugger" ) ) {
com_editors |= EDITOR_DEBUGGER;
}
else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "editor" ) ) {
com_editors |= EDITOR_RADIANT;
}
// Nerve: Add support for the material editor
else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "materialEditor" ) ) {
com_editors |= EDITOR_MATERIAL;
}
if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "renderbump" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "editor" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "guieditor" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "debugger" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "dmap" )
|| !idStr::Icmp( com_consoleLines[ i ].Argv(0), "materialEditor" )
) {
cvarSystem->SetCVarBool( "r_fullscreen", false );
return;
}
}
}
/*
==================
idCommonLocal::StartupVariable
Searches for command line parameters that are set commands.
If match is not NULL, only that cvar will be looked for.
That is necessary because cddir and basedir need to be set
before the filesystem is started, but all other sets should
be after execing the config and default.
==================
*/
void idCommonLocal::StartupVariable( const char *match, bool once ) {
int i;
const char *s;
i = 0;
while ( i < com_numConsoleLines ) {
if ( strcmp( com_consoleLines[ i ].Argv( 0 ), "set" ) ) {
i++;
continue;
}
s = com_consoleLines[ i ].Argv(1);
if ( !match || !idStr::Icmp( s, match ) ) {
cvarSystem->SetCVarString( s, com_consoleLines[ i ].Argv( 2 ) );
if ( once ) {
// kill the line
int j = i + 1;
while ( j < com_numConsoleLines ) {
com_consoleLines[ j - 1 ] = com_consoleLines[ j ];
j++;
}
com_numConsoleLines--;
continue;
}
}
i++;
}
}
/*
==================
idCommonLocal::AddStartupCommands
Adds command line parameters as script statements
Commands are separated by + signs
Returns true if any late commands were added, which
will keep the demoloop from immediately starting
==================
*/
bool idCommonLocal::AddStartupCommands( void ) {
int i;
bool added;
added = false;
// quote every token, so args with semicolons can work
for ( i = 0; i < com_numConsoleLines; i++ ) {
if ( !com_consoleLines[i].Argc() ) {
continue;
}
// set commands won't override menu startup
if ( idStr::Icmpn( com_consoleLines[i].Argv(0), "set", 3 ) ) {
added = true;
}
// directly as tokenized so nothing gets screwed
cmdSystem->BufferCommandArgs( CMD_EXEC_APPEND, com_consoleLines[i] );
}
return added;
}
/*
=================
idCommonLocal::InitTool
=================
*/
void idCommonLocal::InitTool( const toolFlag_t tool, const idDict *dict ) {
#ifdef ID_ALLOW_TOOLS
if ( tool & EDITOR_SOUND ) {
SoundEditorInit( dict );
} else if ( tool & EDITOR_LIGHT ) {
LightEditorInit( dict );
} else if ( tool & EDITOR_PARTICLE ) {
ParticleEditorInit( dict );
} else if ( tool & EDITOR_AF ) {
AFEditorInit( dict );
}
#endif
}
/*
==================
idCommonLocal::ActivateTool
Activates or Deactivates a tool
==================
*/
void idCommonLocal::ActivateTool( bool active ) {
com_editorActive = active;
Sys_GrabMouseCursor( !active );
}
/*
==================
idCommonLocal::WriteFlaggedCVarsToFile
==================
*/
void idCommonLocal::WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) {
idFile *f;
f = fileSystem->OpenFileWrite( filename );
if ( !f ) {
Printf( "Couldn't write %s.\n", filename );
return;
}
cvarSystem->WriteFlaggedVariables( flags, setCmd, f );
fileSystem->CloseFile( f );
}
/*
==================
idCommonLocal::WriteConfigToFile
==================
*/
void idCommonLocal::WriteConfigToFile( const char *filename ) {
idFile *f;
#ifdef ID_WRITE_VERSION
ID_TIME_T t;
char *curtime;
idStr runtag;
idFile_Memory compressed( "compressed" );
idBase64 out;
#endif
f = fileSystem->OpenFileWrite( filename );
if ( !f ) {
Printf ("Couldn't write %s.\n", filename );
return;
}
#ifdef ID_WRITE_VERSION
assert( config_compressor );
t = time( NULL );
curtime = ctime( &t );
sprintf( runtag, "%s - %s", cvarSystem->GetCVarString( "si_version" ), curtime );
config_compressor->Init( &compressed, true, 8 );
config_compressor->Write( runtag.c_str(), runtag.Length() );
config_compressor->FinishCompress( );
out.Encode( (const byte *)compressed.GetDataPtr(), compressed.Length() );
f->Printf( "// %s\n", out.c_str() );
#endif
idKeyInput::WriteBindings( f );
cvarSystem->WriteFlaggedVariables( CVAR_ARCHIVE, "seta", f );
fileSystem->CloseFile( f );
}
/*
===============
idCommonLocal::WriteConfiguration
Writes key bindings and archived cvars to config file if modified
===============
*/
void idCommonLocal::WriteConfiguration( void ) {
// if we are quiting without fully initializing, make sure
// we don't write out anything
if ( !com_fullyInitialized ) {
return;
}
if ( !( cvarSystem->GetModifiedFlags() & CVAR_ARCHIVE ) ) {
return;
}
cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE );
// disable printing out the "Writing to:" message
bool developer = com_developer.GetBool();
com_developer.SetBool( false );
#ifdef QUAKE4
WriteConfigToFile( "Quake4Config.cfg" );
#else
WriteConfigToFile( CONFIG_FILE );
#endif
session->WriteCDKey( );
// restore the developer cvar
com_developer.SetBool( developer );
}
/*
===============
KeysFromBinding()
Returns the key bound to the command
===============
*/
const char* idCommonLocal::KeysFromBinding( const char *bind ) {
return idKeyInput::KeysFromBinding( bind );
}
/*
===============
BindingFromKey()
Returns the binding bound to key
===============
*/
const char* idCommonLocal::BindingFromKey( const char *key ) {
return idKeyInput::BindingFromKey( key );
}
/*
===============
ButtonState()
Returns the state of the button
===============
*/
int idCommonLocal::ButtonState( int key ) {
return usercmdGen->ButtonState(key);
}
/*
===============
ButtonState()
Returns the state of the key
===============
*/
int idCommonLocal::KeyState( int key ) {
return usercmdGen->KeyState(key);
}
#ifdef QUAKE4
/*
===============
idCommonLocal::GetUserCmdHz
===============
*/
int idCommonLocal::GetUserCmdHz( void ) const {
return 60;
}
/*
===============
idCommonLocal::GetUserCmdMSec
===============
*/
int idCommonLocal::GetUserCmdMSec( void ) const {
return 1000 / GetUserCmdHz();
}
/*
===============
idCommonLocal::GetFrameTime
===============
*/
int idCommonLocal::GetFrameTime( void ) const {
return com_frameTime;
}
/*
===============
idCommonLocal::IsRenderableGameFrame
===============
*/
bool idCommonLocal::IsRenderableGameFrame( void ) const {
return com_renderableGameFrame;
}
/*
===============
idCommonLocal::SetRenderableGameFrame
===============
*/
void idCommonLocal::SetRenderableGameFrame( bool in ) {
com_renderableGameFrame = in;
}
/*
===============
idCommonLocal::GetErrorMessage
===============
*/
const char *idCommonLocal::GetErrorMessage( void ) const {
return errorMessage;
}
/*
===============
idCommonLocal::IsToolActive
===============
*/
bool idCommonLocal::IsToolActive( void ) const {
return com_editorActive;
}
#ifdef _RV_MEM_SYS_SUPPORT
void idCommonLocal::InitHeaps( void ) {
}
void idCommonLocal::ShutdownHeaps( void ) {
}
#endif
/*
===============
idCommonLocal::RunAlwaysThinkGUIs
===============
*/
void idCommonLocal::RunAlwaysThinkGUIs( int time ) {
//uiManager->RunAlwaysThinkGUIs( time );
}
/*
===============
idCommonLocal::DoingDeclValidation
===============
*/
bool idCommonLocal::DoingDeclValidation( void ) {
return ( com_editors & EDITOR_DECL_VALIDATING ) == EDITOR_DECL_VALIDATING;
}
/*
===============
idCommonLocal::SetCrashReportAutoSendString
===============
*/
void idCommonLocal::SetCrashReportAutoSendString( const char *psString ) {
#ifdef _WIN32
// Quake 4 forwarded this to the crash-info layer. Keep this harmless when the
// crash-info API is not linked into the Doom 3 codebase.
// CI_SetCrashReportAutoSendString( psString );
#else
(void)psString;
#endif
}
/*
===============
idCommonLocal::GetRModeForMachineSpec
===============
*/
int idCommonLocal::GetRModeForMachineSpec( int machineSpec ) const {
if ( machineSpec == 3 ) {
return 5;
}
if ( machineSpec == 2 ) {
return 4;
}
return 3;
}
/*
===============
idCommonLocal::SetDesiredMachineSpec
===============
*/
void idCommonLocal::SetDesiredMachineSpec( int machineSpec ) {
desiredMachineSpec = machineSpec;
}
/*
===============
idCommonLocal::IsRCon
===============
*/
bool idCommonLocal::IsRCon( void ) const {
return rd_rcon;
}
#endif
//============================================================================
#ifdef ID_ALLOW_TOOLS
/*
==================
Com_Editor_f
we can start the editor dynamically, but we won't ever get back
==================
*/
static void Com_Editor_f( const idCmdArgs &args ) {
RadiantInit();
}
/*
=============
Com_ScriptDebugger_f
=============
*/
static void Com_ScriptDebugger_f( const idCmdArgs &args ) {
// Make sure it wasnt on the command line
if ( !( com_editors & EDITOR_DEBUGGER ) ) {
common->Printf( "Script debugger is currently disabled\n" );
// DebuggerClientLaunch();
}
}
/*
=============
Com_EditGUIs_f
=============
*/
static void Com_EditGUIs_f( const idCmdArgs &args ) {
GUIEditorInit();
}
/*
=============
Com_MaterialEditor_f
=============
*/
static void Com_MaterialEditor_f( const idCmdArgs &args ) {
// Turn off sounds
soundSystem->SetMute( true );
MaterialEditorInit();
}
#endif // ID_ALLOW_TOOLS
/*
============
idCmdSystemLocal::PrintMemInfo_f
This prints out memory debugging data
============
*/
static void PrintMemInfo_f( const idCmdArgs &args ) {
}
#ifdef ID_ALLOW_TOOLS
/*
==================
Com_EditLights_f
==================
*/
static void Com_EditLights_f( const idCmdArgs &args ) {
LightEditorInit( NULL );
cvarSystem->SetCVarInteger( "g_editEntityMode", 1 );
}
/*
==================
Com_EditSounds_f
==================
*/
static void Com_EditSounds_f( const idCmdArgs &args ) {
SoundEditorInit( NULL );
cvarSystem->SetCVarInteger( "g_editEntityMode", 2 );
}
/*
==================
Com_EditDecls_f
==================
*/
static void Com_EditDecls_f( const idCmdArgs &args ) {
DeclBrowserInit( NULL );
}
/*
==================
Com_EditAFs_f
==================
*/
static void Com_EditAFs_f( const idCmdArgs &args ) {
AFEditorInit( NULL );
}
/*
==================
Com_EditParticles_f
==================
*/
static void Com_EditParticles_f( const idCmdArgs &args ) {
ParticleEditorInit( NULL );
}
/*
==================
Com_EditScripts_f
==================
*/
static void Com_EditScripts_f( const idCmdArgs &args ) {
ScriptEditorInit( NULL );
}
/*
==================
Com_EditPDAs_f
==================
*/
static void Com_EditPDAs_f( const idCmdArgs &args ) {
PDAEditorInit( NULL );
}
#endif // ID_ALLOW_TOOLS
/*
==================
Com_Error_f
Just throw a fatal error to test error shutdown procedures.
==================
*/
static void Com_Error_f( const idCmdArgs &args ) {
if ( !com_developer.GetBool() ) {
commonLocal.Printf( "error may only be used in developer mode\n" );
return;
}
if ( args.Argc() > 1 ) {
commonLocal.FatalError( "Testing fatal error" );
} else {
commonLocal.Error( "Testing drop error" );
}
}
/*
==================
Com_Freeze_f
Just freeze in place for a given number of seconds to test error recovery.
==================
*/
static void Com_Freeze_f( const idCmdArgs &args ) {
float s;
int start, now;
if ( args.Argc() != 2 ) {
commonLocal.Printf( "freeze \n" );
return;
}
if ( !com_developer.GetBool() ) {
commonLocal.Printf( "freeze may only be used in developer mode\n" );
return;
}
s = atof( args.Argv(1) );
start = eventLoop->Milliseconds();
while ( 1 ) {
now = eventLoop->Milliseconds();
if ( ( now - start ) * 0.001f > s ) {
break;
}
}
}
/*
=================
Com_Crash_f
A way to force a bus error for development reasons
=================
*/
static void Com_Crash_f( const idCmdArgs &args ) {
if ( !com_developer.GetBool() ) {
commonLocal.Printf( "crash may only be used in developer mode\n" );
return;
}
* ( int * ) 0 = 0x12345678;
}
/*
=================
Com_Quit_f
=================
*/
static void Com_Quit_f( const idCmdArgs &args ) {
commonLocal.Quit();
}
/*
===============
Com_WriteConfig_f
Write the config file to a specific name
===============
*/
void Com_WriteConfig_f( const idCmdArgs &args ) {
idStr filename;
if ( args.Argc() != 2 ) {
commonLocal.Printf( "Usage: writeconfig \n" );
return;
}
filename = args.Argv(1);
filename.DefaultFileExtension( ".cfg" );
commonLocal.Printf( "Writing %s.\n", filename.c_str() );
commonLocal.WriteConfigToFile( filename );
}
/*
=================
Com_SetMachineSpecs_f
=================
*/
void Com_SetMachineSpec_f( const idCmdArgs &args ) {
commonLocal.SetMachineSpec();
}
#ifdef QUAKE4
/*
=================
Com_ExecMPMachineSpec_f
=================
*/
void Com_ExecMPMachineSpec_f( const idCmdArgs &args ) {
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "r_lod_animations_distance", 500.0f );
cvarSystem->SetCVarBool( "r_lod_shadows", true );
cvarSystem->SetCVarBool( "r_lod_entities", true );
}
/*
=================
Com_WriteAssetLog_f
=================
*/
void Com_WriteAssetLog_f( const idCmdArgs &args ) {
//fileSystem->WriteAssetLog();
}
/*
=================
Com_ClearAssetLog_f
=================
*/
void Com_ClearAssetLog_f( const idCmdArgs &args ) {
//fileSystem->ClearAssetLog();
}
/*
=================
Com_ShowMemory_f
=================
*/
void Com_ShowMemory_f( const idCmdArgs &args ) {
common->Printf( "Memory usage:\n" );
unsigned int total = 0;
if ( gameEdit ) {
total += gameEdit->ScriptSummary( args );
total += gameEdit->ClassSummary( args );
total += gameEdit->EntitySummary( args );
}
//total += declManager->ListDeclSummary( args );
if ( session->rw ) {
total += session->rw->MemorySummary( args );
}
common->Printf( "Total - %.1fMeg\n", (float)total / 1024.0f );
}
/*
=================
IncrementCVar_f
=================
*/
void IncrementCVar_f( const idCmdArgs &args ) {
if ( args.Argc() < 4 ) {
return;
}
const char *cvarName = args.Argv( 1 );
float incAmt = atof( args.Argv( 2 ) );
float maxAmt = atof( args.Argv( 3 ) );
float val = cvarSystem->GetCVarFloat( cvarName ) + incAmt;
if ( val > maxAmt ) {
val = maxAmt;
}
cvarSystem->SetCVarFloat( cvarName, val );
}
/*
=================
DecrementCVar_f
=================
*/
void DecrementCVar_f( const idCmdArgs &args ) {
if ( args.Argc() < 4 ) {
return;
}
const char *cvarName = args.Argv( 1 );
float decAmt = atof( args.Argv( 2 ) );
float minAmt = atof( args.Argv( 3 ) );
float val = cvarSystem->GetCVarFloat( cvarName ) - decAmt;
if ( val < minAmt ) {
val = minAmt;
}
cvarSystem->SetCVarFloat( cvarName, val );
}
/*
=================
idCommonLocal::WriteConfiguration_f
=================
*/
static void Com_WriteConfiguration_f( const idCmdArgs &args ) {
commonLocal.WriteConfigToFile( "Quake4Config.cfg" );
}
#endif
/*
=================
Com_ExecMachineSpecs_f
=================
*/
#ifdef MACOS_X
void OSX_GetVideoCard( int& outVendorId, int& outDeviceId );
bool OSX_GetCPUIdentification( int& cpuId, bool& oldArchitecture );
#endif
void Com_ExecMachineSpec_f( const idCmdArgs &args ) {
#ifdef QUAKE4
int downSize;
int downSizeLimit;
int downSizeSpecular;
int downSizeSpecularLimit;
int downSizeBump;
int downSizeBumpLimit;
if ( com_machineSpec.GetInteger() == 3 ) {
cvarSystem->SetCVarInteger( "image_useCompression", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 8, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
downSize = 0;
downSizeLimit = 256;
downSizeSpecular = 0;
downSizeSpecularLimit = 256;
downSizeBump = 0;
downSizeBumpLimit = 256;
cvarSystem->SetCVarInteger( "r_mode", 5, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "g_brassTime", 2.0f, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "bse_scale", 1.2f, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_skipNewAmbient", false, CVAR_ARCHIVE );
} else if ( com_machineSpec.GetInteger() == 2 ) {
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
if ( !cvarSystem->GetCVarInteger( "image_useNormalCompression" ) ) {
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
}
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 4, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
downSize = 0;
downSizeLimit = 256;
downSizeSpecular = 0;
downSizeSpecularLimit = 64;
downSizeBump = 0;
downSizeBumpLimit = 256;
cvarSystem->SetCVarInteger( "r_mode", 4, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "g_brassTime", 1.0f, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "bse_scale", 1.0f, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_skipNewAmbient", false, CVAR_ARCHIVE );
} else if ( com_machineSpec.GetInteger() == 1 ) {
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
if ( !cvarSystem->GetCVarInteger( "image_useNormalCompression" ) ) {
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
}
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 1, CVAR_ARCHIVE );
downSize = 0;
downSizeLimit = 256;
downSizeSpecular = 0;
downSizeSpecularLimit = 64;
downSizeBump = 0;
downSizeBumpLimit = 256;
cvarSystem->SetCVarInteger( "r_mode", 3, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "g_brassTime", 0.0f, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "bse_scale", 0.6f, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_skipNewAmbient", true, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
if ( !cvarSystem->GetCVarInteger( "image_useNormalCompression" ) ) {
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
}
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 1, CVAR_ARCHIVE );
downSize = 1;
downSizeLimit = 256;
downSizeSpecular = 1;
downSizeSpecularLimit = 64;
downSizeBump = 1;
downSizeBumpLimit = 256;
cvarSystem->SetCVarInteger( "r_mode", 3, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "g_brassTime", 0.0f, CVAR_ARCHIVE );
cvarSystem->SetCVarFloat( "bse_scale", 0.3f, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_skipNewAmbient", true, CVAR_ARCHIVE );
}
if ( Sys_GetVideoRam() < 80 ) {
cvarSystem->SetCVarBool( "image_ignoreHighQuality", true, CVAR_ARCHIVE );
downSize = 1;
downSizeLimit = 128;
downSizeSpecular = 1;
downSizeSpecularLimit = 64;
downSizeBump = 1;
downSizeBumpLimit = 128;
}
if ( Sys_GetSystemRam() >= 750 ) {
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", Sys_GetSystemRam() >= 1000 ? 0 : 2, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "com_purgeAll", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_forceLoadImages", false, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarBool( "image_ignoreHighQuality", true, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "com_purgeAll", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_forceLoadImages", true, CVAR_ARCHIVE );
}
cvarSystem->SetCVarInteger( "image_downSize", downSize, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeLimit", downSizeLimit, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", downSizeSpecular, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", downSizeSpecularLimit, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", downSizeBump, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", downSizeBumpLimit, CVAR_ARCHIVE );
bool oldCard = false;
bool nv10or20 = false;
renderSystem->GetCardCaps( oldCard, nv10or20 );
if ( oldCard ) {
cvarSystem->SetCVarBool( "g_decals", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_projectileLights", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_skipNewAmbient", true, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarBool( "g_decals", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_projectileLights", true, CVAR_ARCHIVE );
}
return;
#else
if ( com_machineSpec.GetInteger() == 3 ) {
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downsize", 0 , CVAR_ARCHIVE );
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 8, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useCompression", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_mode", 5, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
} else if ( com_machineSpec.GetInteger() == 2 ) {
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downsize", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 8, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_mode", 4, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
} else if ( com_machineSpec.GetInteger() == 1 ) {
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSize", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_mode", 3, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSize", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_anisotropy", 0, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_mode", 3 , CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
}
if ( Sys_GetVideoRam() < 128 ) {
cvarSystem->SetCVarBool( "image_ignoreHighQuality", true, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSize", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBump", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
}
if ( Sys_GetSystemRam() < 512 ) {
cvarSystem->SetCVarBool( "image_ignoreHighQuality", true, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSize", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeLimit", 256, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecular", 1, CVAR_ARCHIVE );
cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "com_purgeAll", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_forceLoadImages", true, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarBool( "com_purgeAll", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "r_forceLoadImages", false, CVAR_ARCHIVE );
}
bool oldCard = false;
bool nv10or20 = false;
renderSystem->GetCardCaps( oldCard, nv10or20 );
if ( oldCard ) {
cvarSystem->SetCVarBool( "g_decals", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_projectileLights", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_doubleVision", false, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_muzzleFlash", false, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarBool( "g_decals", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_projectileLights", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_doubleVision", true, CVAR_ARCHIVE );
cvarSystem->SetCVarBool( "g_muzzleFlash", true, CVAR_ARCHIVE );
}
if ( nv10or20 ) {
cvarSystem->SetCVarInteger( "image_useNormalCompression", 1, CVAR_ARCHIVE );
}
#if MACOS_X
// On low settings, G4 systems & 64MB FX5200/NV34 Systems should default shadows off
bool oldArch;
int vendorId, deviceId, cpuId;
OSX_GetVideoCard( vendorId, deviceId );
OSX_GetCPUIdentification( cpuId, oldArch );
bool isFX5200 = vendorId == 0x10DE && ( deviceId & 0x0FF0 ) == 0x0320;
if ( ( oldArch || ( isFX5200 && Sys_GetVideoRam() < 128 ) ) && com_machineSpec.GetInteger() == 0 ) {
cvarSystem->SetCVarBool( "r_shadows", false, CVAR_ARCHIVE );
} else {
cvarSystem->SetCVarBool( "r_shadows", true, CVAR_ARCHIVE );
}
#endif
#endif // QUAKE4
}
/*
=================
Com_ReloadEngine_f
=================
*/
void Com_ReloadEngine_f( const idCmdArgs &args ) {
bool menu = false;
if ( !commonLocal.IsInitialized() ) {
return;
}
#ifdef QUAKE4
fileSystem->SetIsFileLoadingAllowed( true );
#endif
if ( args.Argc() > 1 && idStr::Icmp( args.Argv( 1 ), "menu" ) == 0 ) {
menu = true;
}
common->Printf( "============= ReloadEngine start =============\n" );
if ( !menu ) {
Sys_ShowConsole( 1, false );
}
commonLocal.ShutdownGame( true );
commonLocal.InitGame();
if ( !menu && !idAsyncNetwork::serverDedicated.GetBool() ) {
Sys_ShowConsole( 0, false );
}
common->Printf( "============= ReloadEngine end ===============\n" );
if ( !cmdSystem->PostReloadEngine() ) {
if ( menu ) {
session->StartMenu( );
}
}
#ifdef QUAKE4
fileSystem->SetIsFileLoadingAllowed( false );
#endif
}
/*
===============
idCommonLocal::GetLanguageDict
===============
*/
const idLangDict *idCommonLocal::GetLanguageDict( void ) {
#ifdef QUAKE4
if ( languages.Num() > 0 ) {
return languages[languageDictIndex].GetDict();
}
#endif
return &languageDict;
}
#ifdef QUAKE4
/*
===============
idCommonLocal::GetLocalizedString
===============
*/
const char *idCommonLocal::GetLocalizedString( const char *token, int langIndex ) {
if ( !token || !token[0] ) {
return "";
}
if ( idStr::Cmpn( token, STRTABLE_ID, STRTABLE_ID_LENGTH ) != 0 ) {
return token;
}
if ( languages.Num() <= 0 ) {
return token;
}
if ( langIndex < 0 || langIndex >= languages.Num() ) {
langIndex = languageDictIndex;
}
return languages[langIndex].GetString( token );
}
/*
===============
idCommonLocal::GetLocalizedString
===============
*/
const idLangKeyValue *idCommonLocal::GetLocalizedString( int index, int langIndex ) {
if ( languages.Num() <= 0 ) {
return NULL;
}
if ( langIndex < 0 || langIndex >= languages.Num() ) {
langIndex = languageDictIndex;
}
return languages[langIndex].GetString( index );
}
int idCommonLocal::GetNumLanguages( void ) const {
return languages.Num();
}
int idCommonLocal::GetNumLocalizedStrings( void ) const {
if ( languages.Num() <= 0 ) {
return 0;
}
return languages[0].GetNumStrings();
}
const char *idCommonLocal::GetLanguage( int index ) const {
if ( languages.Num() <= 0 ) {
return "";
}
if ( index < 0 || index >= languages.Num() ) {
index = languageDictIndex;
}
return languages[index].GetLanguage();
}
bool idCommonLocal::LanguageHasVO( int index ) const {
if ( languages.Num() <= 0 ) {
return false;
}
if ( index < 0 || index >= languages.Num() ) {
index = languageDictIndex;
}
return languages[index].HasVO();
}
bool idCommonLocal::AddLanguage( const idStr &language ) {
for ( int i = 0; i < languages.Num(); i++ ) {
if ( idStr::Icmp( languages[i].GetLanguage(), language.c_str() ) == 0 ) {
return false;
}
}
rvLanguage lang;
lang.SetLanguage( language );
languages.Append( lang );
return true;
}
void idCommonLocal::SetActiveLanguage( void ) {
idStr lang = cvarSystem->GetCVarString( "sys_lang" );
for ( int i = 0; i < languages.Num(); i++ ) {
if ( idStr::Icmp( lang.c_str(), languages[i].GetLanguage() ) == 0 ) {
languageDictIndex = i;
return;
}
}
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reset sys_lang" );
languageDictIndex = 0;
}
#endif
/*
===============
idCommonLocal::FilterLangList
===============
*/
void idCommonLocal::FilterLangList( idStrList* list, idStr lang ) {
idStr temp;
for( int i = 0; i < list->Num(); i++ ) {
temp = (*list)[i];
temp = temp.Right(temp.Length()-strlen("strings/"));
temp = temp.Left(lang.Length());
if(idStr::Icmp(temp, lang) != 0) {
list->RemoveIndex(i);
i--;
}
}
}
/*
===============
idCommonLocal::InitLanguageDict
===============
*/
void idCommonLocal::InitLanguageDict( void ) {
#ifdef QUAKE4
languageDict.Clear();
languages.Clear();
idFileList *langFiles = fileSystem->ListFilesTree( "strings", ".lang", true );
idStrList langList = langFiles->GetList();
StartupVariable( "sys_lang", false );
idStr english = "english";
AddLanguage( english );
for ( int i = 0; i < langList.Num(); i++ ) {
idStr temp = langList[i];
temp.StripLeading( "strings/" );
int underscore = temp.Find( '_' );
if ( underscore > 0 ) {
temp = temp.Left( underscore );
AddLanguage( temp );
}
}
if ( languages.Num() > 0 ) {
Printf( "Found default language English with VO\n" );
languages[0].SetHasVO( true );
}
for ( int i = 1; i < languages.Num(); i++ ) {
idStr folder = va( "sound/vo_%s", languages[i].GetLanguage() );
idFileList *langFolders = fileSystem->ListFiles( folder, "/" );
bool hasVO = ( langFolders && langFolders->GetNumFiles() > 0 );
languages[i].SetHasVO( hasVO );
if ( hasVO ) {
Printf( ".... found additional language '%s' with VO\n", languages[i].GetLanguage() );
} else {
Printf( ".... found additional language '%s' without VO\n", languages[i].GetLanguage() );
}
fileSystem->FreeFileList( langFolders );
}
for ( int i = 0; i < languages.Num(); i++ ) {
idStrList currentLangList = langList;
FilterLangList( ¤tLangList, languages[i].GetLanguage() );
for ( int j = 0; j < currentLangList.Num(); j++ ) {
languages[i].Load( currentLangList[j], false );
if ( i == 0 ) {
languageDict.Load( currentLangList[j], false );
}
}
}
fileSystem->FreeFileList( langFiles );
SetActiveLanguage();
Sys_InitScanTable();
#else
idStr fileName;
languageDict.Clear();
//D3XP: Instead of just loading a single lang file for each language
//we are going to load all files that begin with the language name
//similar to the way pak files work. So you can place english001.lang
//to add new strings to the english language dictionary
idFileList* langFiles;
langFiles = fileSystem->ListFilesTree( "strings", ".lang", true );
idStrList langList = langFiles->GetList();
StartupVariable( "sys_lang", false ); // let it be set on the command line - this is needed because this init happens very early
idStr langName = cvarSystem->GetCVarString( "sys_lang" );
//Loop through the list and filter
idStrList currentLangList = langList;
FilterLangList(¤tLangList, langName);
if ( currentLangList.Num() == 0 ) {
// reset cvar to default and try to load again
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reset sys_lang" );
langName = cvarSystem->GetCVarString( "sys_lang" );
currentLangList = langList;
FilterLangList(¤tLangList, langName);
}
for( int i = 0; i < currentLangList.Num(); i++ ) {
//common->Printf("%s\n", currentLangList[i].c_str());
languageDict.Load( currentLangList[i], false );
}
fileSystem->FreeFileList(langFiles);
Sys_InitScanTable();
#endif
}
/*
===============
idCommonLocal::LocalizeSpecificMapData
===============
*/
void idCommonLocal::LocalizeSpecificMapData( const char *fileName, idLangDict &langDict, const idLangDict &replaceArgs ) {
idStr out, ws, work;
idMapFile map;
if ( map.Parse( fileName, false, false ) ) {
int count = map.GetNumEntities();
for ( int i = 0; i < count; i++ ) {
idMapEntity *ent = map.GetEntity( i );
if ( ent ) {
for ( int j = 0; j < replaceArgs.GetNumKeyVals(); j++ ) {
const idLangKeyValue *kv = replaceArgs.GetKeyVal( j );
const char *temp = ent->epairs.GetString( kv->key );
if ( temp && *temp ) {
idStr val = kv->value;
if ( val == temp ) {
ent->epairs.Set( kv->key, langDict.AddString( temp ) );
}
}
}
}
}
map.Write( fileName, ".map" );
}
}
/*
===============
idCommonLocal::LocalizeMapData
===============
*/
void idCommonLocal::LocalizeMapData( const char *fileName, idLangDict &langDict ) {
const char *buffer = NULL;
idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
common->SetRefreshOnPrint( true );
if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
src.LoadMemory( buffer, strlen(buffer), fileName );
if ( src.IsLoaded() ) {
common->Printf( "Processing %s\n", fileName );
idStr mapFileName;
idToken token, token2;
idLangDict replaceArgs;
while ( src.ReadToken( &token ) ) {
mapFileName = token;
replaceArgs.Clear();
src.ExpectTokenString( "{" );
while ( src.ReadToken( &token) ) {
if ( token == "}" ) {
break;
}
if ( src.ReadToken( &token2 ) ) {
if ( token2 == "}" ) {
break;
}
replaceArgs.AddKeyVal( token, token2 );
}
}
common->Printf( " localizing map %s...\n", mapFileName.c_str() );
LocalizeSpecificMapData( mapFileName, langDict, replaceArgs );
}
}
fileSystem->FreeFile( (void*)buffer );
}
common->SetRefreshOnPrint( false );
}
/*
===============
idCommonLocal::LocalizeGui
===============
*/
void idCommonLocal::LocalizeGui( const char *fileName, idLangDict &langDict ) {
idStr out, ws, work;
const char *buffer = NULL;
out.Empty();
int k;
char ch;
char slash = '\\';
char tab = 't';
char nl = 'n';
idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
src.LoadMemory( buffer, strlen(buffer), fileName );
if ( src.IsLoaded() ) {
idFile *outFile = fileSystem->OpenFileWrite( fileName );
common->Printf( "Processing %s\n", fileName );
session->UpdateScreen();
idToken token;
while( src.ReadToken( &token ) ) {
src.GetLastWhiteSpace( ws );
out += ws;
if ( token.type == TT_STRING ) {
out += va( "\"%s\"", token.c_str() );
} else {
out += token;
}
if ( out.Length() > 200000 ) {
outFile->Write( out.c_str(), out.Length() );
out = "";
}
work = token.Right( 6 );
if ( token.Icmp( "text" ) == 0 || work.Icmp( "::text" ) == 0 || token.Icmp( "choices" ) == 0 ) {
if ( src.ReadToken( &token ) ) {
// see if already exists, if so save that id to this position in this file
// otherwise add this to the list and save the id to this position in this file
src.GetLastWhiteSpace( ws );
out += ws;
token = langDict.AddString( token );
out += "\"";
for ( k = 0; k < token.Length(); k++ ) {
ch = token[k];
if ( ch == '\t' ) {
out += slash;
out += tab;
} else if ( ch == '\n' || ch == '\r' ) {
out += slash;
out += nl;
} else {
out += ch;
}
}
out += "\"";
}
} else if ( token.Icmp( "comment" ) == 0 ) {
if ( src.ReadToken( &token ) ) {
// need to write these out by hand to preserve any \n's
// see if already exists, if so save that id to this position in this file
// otherwise add this to the list and save the id to this position in this file
src.GetLastWhiteSpace( ws );
out += ws;
out += "\"";
for ( k = 0; k < token.Length(); k++ ) {
ch = token[k];
if ( ch == '\t' ) {
out += slash;
out += tab;
} else if ( ch == '\n' || ch == '\r' ) {
out += slash;
out += nl;
} else {
out += ch;
}
}
out += "\"";
}
}
}
outFile->Write( out.c_str(), out.Length() );
fileSystem->CloseFile( outFile );
}
fileSystem->FreeFile( (void*)buffer );
}
}
/*
=================
ReloadLanguage_f
=================
*/
void Com_ReloadLanguage_f( const idCmdArgs &args ) {
#ifdef QUAKE4
fileSystem->SetIsFileLoadingAllowed( true );
#endif
commonLocal.InitLanguageDict();
#ifdef QUAKE4
fileSystem->SetIsFileLoadingAllowed( false );
#endif
}
typedef idHashTable ListHash;
void LoadMapLocalizeData(ListHash& listHash) {
idStr fileName = "map_localize.cfg";
const char *buffer = NULL;
idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
src.LoadMemory( buffer, strlen(buffer), fileName );
if ( src.IsLoaded() ) {
idStr classname;
idToken token;
while ( src.ReadToken( &token ) ) {
classname = token;
src.ExpectTokenString( "{" );
idStrList list;
while ( src.ReadToken( &token) ) {
if ( token == "}" ) {
break;
}
list.Append(token);
}
listHash.Set(classname, list);
}
}
fileSystem->FreeFile( (void*)buffer );
}
}
void LoadGuiParmExcludeList(idStrList& list) {
idStr fileName = "guiparm_exclude.cfg";
const char *buffer = NULL;
idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
src.LoadMemory( buffer, strlen(buffer), fileName );
if ( src.IsLoaded() ) {
idStr classname;
idToken token;
while ( src.ReadToken( &token ) ) {
list.Append(token);
}
}
fileSystem->FreeFile( (void*)buffer );
}
}
bool TestMapVal(idStr& str) {
//Already Localized?
if(str.Find("#str_") != -1) {
return false;
}
return true;
}
bool TestGuiParm(const char* parm, const char* value, idStrList& excludeList) {
idStr testVal = value;
//Already Localized?
if(testVal.Find("#str_") != -1) {
return false;
}
//Numeric
if(testVal.IsNumeric()) {
return false;
}
//Contains ::
if(testVal.Find("::") != -1) {
return false;
}
//Contains /
if(testVal.Find("/") != -1) {
return false;
}
if(excludeList.Find(testVal)) {
return false;
}
return true;
}
void GetFileList(const char* dir, const char* ext, idStrList& list) {
//Recurse Subdirectories
idStrList dirList;
Sys_ListFiles(dir, "/", dirList);
for(int i = 0; i < dirList.Num(); i++) {
if(dirList[i] == "." || dirList[i] == "..") {
continue;
}
idStr fullName = va("%s/%s", dir, dirList[i].c_str());
GetFileList(fullName, ext, list);
}
idStrList fileList;
Sys_ListFiles(dir, ext, fileList);
for(int i = 0; i < fileList.Num(); i++) {
idStr fullName = va("%s/%s", dir, fileList[i].c_str());
list.Append(fullName);
}
}
int LocalizeMap(const char* mapName, idLangDict &langDict, ListHash& listHash, idStrList& excludeList, bool writeFile) {
common->Printf("Localizing Map '%s'\n", mapName);
int strCount = 0;
idMapFile map;
if ( map.Parse(mapName, false, false ) ) {
int count = map.GetNumEntities();
for ( int j = 0; j < count; j++ ) {
idMapEntity *ent = map.GetEntity( j );
if ( ent ) {
idStr classname = ent->epairs.GetString("classname");
//Hack: for info_location
bool hasLocation = false;
idStrList* list;
listHash.Get(classname, &list);
if(list) {
for(int k = 0; k < list->Num(); k++) {
idStr val = ent->epairs.GetString((*list)[k], "");
if(val.Length() && classname == "info_location" && (*list)[k] == "location") {
hasLocation = true;
}
if(val.Length() && TestMapVal(val)) {
if(!hasLocation || (*list)[k] == "location") {
//Localize it!!!
strCount++;
ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
}
}
}
}
listHash.Get("all", &list);
if(list) {
for(int k = 0; k < list->Num(); k++) {
idStr val = ent->epairs.GetString((*list)[k], "");
if(val.Length() && TestMapVal(val)) {
//Localize it!!!
strCount++;
ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
}
}
}
//Localize the gui_parms
const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
while( kv ) {
if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
//Localize It!
strCount++;
ent->epairs.Set( kv->GetKey(), langDict.AddString( kv->GetValue() ) );
}
kv = ent->epairs.MatchPrefix( "gui_parm", kv );
}
}
}
if(writeFile && strCount > 0) {
//Before we write the map file lets make a backup of the original
idStr file = fileSystem->RelativePathToOSPath(mapName);
idStr bak = file.Left(file.Length() - 4);
bak.Append(".bak_loc");
fileSystem->CopyFile( file, bak );
map.Write( mapName, ".map" );
}
}
common->Printf("Count: %d\n", strCount);
return strCount;
}
/*
=================
LocalizeMaps_f
=================
*/
void Com_LocalizeMaps_f( const idCmdArgs &args ) {
if ( args.Argc() < 2 ) {
common->Printf( "Usage: localizeMaps