mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-25 22:01:09 +00:00
3407 lines
97 KiB
C++
3407 lines
97 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 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.
|
|
|
|
Doom 3 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 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 Doom 3 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 id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/containers/HashTable.h"
|
|
#include "idlib/LangDict.h"
|
|
#include "idlib/MapFile.h"
|
|
#include "cm/CollisionModel.h"
|
|
#include "framework/async/AsyncNetwork.h"
|
|
#include "framework/async/NetworkSystem.h"
|
|
#include "framework/BuildVersion.h"
|
|
#include "framework/Licensee.h"
|
|
#include "framework/Console.h"
|
|
#include "framework/Session.h"
|
|
#include "framework/Game.h"
|
|
#include "framework/KeyInput.h"
|
|
#include "framework/EventLoop.h"
|
|
#include "renderer/Image.h"
|
|
#include "renderer/Model.h"
|
|
#include "renderer/ModelManager.h"
|
|
#include "renderer/RenderSystem.h"
|
|
#include "tools/compilers/compiler_public.h"
|
|
#include "tools/compilers/aas/AASFileManager.h"
|
|
#include "tools/edit_public.h"
|
|
|
|
#include "sys/sys_imgui.h"
|
|
|
|
#include "framework/Common.h"
|
|
|
|
#include "GameCallbacks_local.h"
|
|
#include "Session_local.h" // DG: For FT_IsDemo/isDemo() hack
|
|
|
|
#define MAX_PRINT_MSG_SIZE 4096
|
|
#define MAX_WARNING_LIST 256
|
|
|
|
// DG: implemented in Dhewm3SettingsMenu.cpp (the only Com_*_f() function not implemented in this file)
|
|
extern void Com_Dhewm3Settings_f( const idCmdArgs &args );
|
|
|
|
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 %s", ENGINE_VERSION, BUILD_NUMBER, BUILD_DEBUG, BUILD_OS, BUILD_CPU, ID__DATE__, ID__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 or 3: async update every 16ms 2: async update about every 100ms (original behavior)"
|
|
idCVar com_asyncSound( "com_asyncSound", "1", CVAR_INTEGER|CVAR_SYSTEM, ASYNCSOUND_INFO, 0, 3 );
|
|
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_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_enableDebuggerServer( "com_enableDebuggerServer", "0", CVAR_BOOL | CVAR_SYSTEM, "toggle debugger server and try to connect to com_dbgClientAdr" );
|
|
idCVar com_dbgClientAdr( "com_dbgClientAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debuggerApp client address" );
|
|
idCVar com_dbgServerAdr( "com_dbgServerAdr", "localhost", CVAR_SYSTEM | CVAR_ARCHIVE, "debugger server address" );
|
|
|
|
idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." );
|
|
|
|
// com_speeds times
|
|
int time_gameFrame;
|
|
int time_gameDraw;
|
|
int time_frontend; // renderSystem frontend time
|
|
int time_backend; // renderSystem backend time
|
|
|
|
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
|
|
|
|
bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction()
|
|
|
|
#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
|
|
|
|
class idCommonLocal : public idCommon {
|
|
public:
|
|
idCommonLocal( void );
|
|
|
|
virtual void Init( int argc, char **argv );
|
|
virtual void Shutdown( void );
|
|
virtual void Quit( void );
|
|
virtual bool IsInitialized( void ) const;
|
|
virtual void Frame( void );
|
|
virtual void GUIFrame( bool execCmd, bool network );
|
|
virtual void Async( void );
|
|
virtual void StartupVariable( const char *match, bool once );
|
|
virtual void InitTool( const toolFlag_t tool, const idDict *dict );
|
|
virtual void ActivateTool( bool active );
|
|
virtual void WriteConfigToFile( const char *filename );
|
|
virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd );
|
|
virtual void BeginRedirect( char *buffer, int buffersize, void (*flush)( const char * ) );
|
|
virtual void EndRedirect( void );
|
|
virtual void SetRefreshOnPrint( bool set );
|
|
virtual void Printf( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual void VPrintf( const char *fmt, va_list arg );
|
|
virtual void DPrintf( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual void Warning( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual void DWarning( const char *fmt, ...) id_attribute((format(printf,2,3)));
|
|
virtual void PrintWarnings( void );
|
|
virtual void ClearWarnings( const char *reason );
|
|
virtual void Error( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual void FatalError( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual const idLangDict * GetLanguageDict( void );
|
|
|
|
virtual const char * KeysFromBinding( const char *bind );
|
|
virtual const char * BindingFromKey( const char *key );
|
|
|
|
virtual int ButtonState( int key );
|
|
virtual int KeyState( int key );
|
|
|
|
// DG: hack to allow adding callbacks and exporting additional functions without breaking the game ABI
|
|
// see Common.h for longer explanation...
|
|
|
|
// returns true if setting the callback was successful, else false
|
|
// When a game DLL is unloaded the callbacks are automatically removed from the Engine
|
|
// so you usually don't have to worry about that; but you can call this with cb = NULL
|
|
// and userArg = NULL to remove a callback manually (e.g. if userArg refers to an object you deleted)
|
|
virtual bool SetCallback(idCommon::CallbackType cbt, idCommon::FunctionPointer cb, void* userArg);
|
|
|
|
// returns true if that function is available in this version of dhewm3
|
|
// *out_fnptr will be the function (you'll have to cast it probably)
|
|
// *out_userArg will be an argument you have to pass to the function, if appropriate (else NULL)
|
|
// NOTE: this doesn't do anything yet, but allows to add ugly mod-specific hacks without breaking the Game interface
|
|
virtual bool GetAdditionalFunction(idCommon::FunctionType ft, idCommon::FunctionPointer* out_fnptr, void** out_userArg);
|
|
|
|
// DG end
|
|
|
|
void InitGame( void );
|
|
void ShutdownGame( bool reloading );
|
|
|
|
// localization
|
|
void InitLanguageDict( void );
|
|
void LocalizeGui( const char *fileName, idLangDict &langDict );
|
|
void LocalizeMapData( const char *fileName, idLangDict &langDict );
|
|
void LocalizeSpecificMapData( const char *fileName, idLangDict &langDict, const idLangDict &replaceArgs );
|
|
|
|
void SetMachineSpec( void );
|
|
|
|
private:
|
|
void InitCommands( void );
|
|
void InitRenderSystem( void );
|
|
void InitSIMD( void );
|
|
bool AddStartupCommands( void );
|
|
void ParseCommandLine( int argc, char **argv );
|
|
void ClearCommandLine( void );
|
|
bool SafeMode( void );
|
|
void CheckToolMode( void );
|
|
void WriteConfiguration( void );
|
|
void DumpWarnings( void );
|
|
void SingleAsyncTic( void );
|
|
void LoadGameDLL( void );
|
|
void LoadGameDLLbyName( const char *dll, idStr& s );
|
|
void UnloadGameDLL( void );
|
|
void PrintLoadingMessage( const char *msg );
|
|
void FilterLangList( idStrList* list, idStr lang );
|
|
|
|
bool com_fullyInitialized;
|
|
bool com_refreshOnPrint; // update the screen every print for dmap
|
|
int com_errorEntered; // 0, ERP_DROP, etc
|
|
|
|
char errorMessage[MAX_PRINT_MSG_SIZE];
|
|
|
|
char * rd_buffer;
|
|
int rd_buffersize;
|
|
void (*rd_flush)( const char *buffer );
|
|
|
|
idStr warningCaption;
|
|
idStrList warningList;
|
|
idStrList errorList;
|
|
|
|
uintptr_t gameDLL;
|
|
|
|
idLangDict languageDict;
|
|
|
|
#ifdef ID_WRITE_VERSION
|
|
idCompressor * config_compressor;
|
|
#endif
|
|
|
|
SDL_TimerID async_timer;
|
|
};
|
|
|
|
idCommonLocal commonLocal;
|
|
idCommon * common = &commonLocal;
|
|
|
|
/*
|
|
==================
|
|
idCommonLocal::idCommonLocal
|
|
==================
|
|
*/
|
|
idCommonLocal::idCommonLocal( void ) {
|
|
com_fullyInitialized = false;
|
|
com_refreshOnPrint = false;
|
|
com_errorEntered = 0;
|
|
com_debuggerSupported = false;
|
|
|
|
strcpy( errorMessage, "" );
|
|
|
|
rd_buffer = NULL;
|
|
rd_buffersize = 0;
|
|
rd_flush = NULL;
|
|
|
|
gameDLL = 0;
|
|
|
|
#ifdef ID_WRITE_VERSION
|
|
config_compressor = NULL;
|
|
#endif
|
|
|
|
async_timer = 0;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idCommonLocal::BeginRedirect
|
|
==================
|
|
*/
|
|
void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *) ) {
|
|
if ( !buffer || !buffersize || !flush ) {
|
|
return;
|
|
}
|
|
rd_buffer = buffer;
|
|
rd_buffersize = buffersize;
|
|
rd_flush = flush;
|
|
|
|
*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 _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::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;
|
|
|
|
// 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 %zd 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 );
|
|
|
|
if ( com_enableDebuggerServer.GetBool( ) ) {
|
|
// print to script debugger server
|
|
if ( com_editors & EDITOR_DEBUGGER )
|
|
DebuggerServerPrint( msg );
|
|
else
|
|
// only echo to dedicated console and early console when debugger is not running so no
|
|
// deadlocks occur if engine functions called from the debuggerthread trace stuff..
|
|
Sys_Printf( "%s", msg );
|
|
} else {
|
|
Sys_Printf( "%s", msg );
|
|
}
|
|
#if 0 // !@#
|
|
#if defined(_DEBUG) && defined(WIN32)
|
|
if ( strlen( msg ) < 512 ) {
|
|
TRACE( msg );
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// 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<LPARAM>(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;
|
|
|
|
Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
|
|
|
|
if ( warningList.Num() < MAX_WARNING_LIST ) {
|
|
warningList.AddUnique( msg );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idCommonLocal::PrintWarnings
|
|
==================
|
|
*/
|
|
void idCommonLocal::PrintWarnings( void ) {
|
|
int i;
|
|
|
|
if ( !warningList.Num() ) {
|
|
return;
|
|
}
|
|
|
|
warningList.Sort();
|
|
|
|
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();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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() );
|
|
warningList.Sort();
|
|
for ( i = 0; i < warningList.Num(); i++ ) {
|
|
warningList[i].RemoveColors();
|
|
warningFile->Printf( "WARNING: %s\n", warningList[i].c_str() );
|
|
}
|
|
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)
|
|
idStr osPath;
|
|
osPath = fileSystem->RelativePathToOSPath( "warnings.txt", "fs_savepath" );
|
|
WinExec( va( "Notepad.exe %s", osPath.c_str() ), SW_SHOW );
|
|
#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_Printf( "shutting down: %s\n", 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, char **argv ) {
|
|
int i;
|
|
|
|
com_numConsoleLines = 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;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idCommonLocal::WriteFlaggedCVarsToFile
|
|
==================
|
|
*/
|
|
void idCommonLocal::WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) {
|
|
idFile *f;
|
|
|
|
f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
|
|
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, "fs_configpath" );
|
|
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 );
|
|
|
|
WriteConfigToFile( CONFIG_FILE );
|
|
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 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 ) ) {
|
|
|
|
//start debugger server if needed
|
|
if ( !com_enableDebuggerServer.GetBool() )
|
|
com_enableDebuggerServer.SetBool( true );
|
|
|
|
//start debugger client.
|
|
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 ) {
|
|
MemInfo_t mi;
|
|
|
|
memset( &mi, 0, sizeof( mi ) );
|
|
mi.filebase = session->GetCurrentMapName();
|
|
|
|
renderSystem->PrintMemInfo( &mi ); // textures and models
|
|
soundSystem->PrintMemInfo( &mi ); // sounds
|
|
|
|
common->Printf( " Used image memory: %s bytes\n", idStr::FormatNumber( mi.imageAssetsTotal ).c_str() );
|
|
mi.assetTotals += mi.imageAssetsTotal;
|
|
|
|
common->Printf( " Used model memory: %s bytes\n", idStr::FormatNumber( mi.modelAssetsTotal ).c_str() );
|
|
mi.assetTotals += mi.modelAssetsTotal;
|
|
|
|
common->Printf( " Used sound memory: %s bytes\n", idStr::FormatNumber( mi.soundAssetsTotal ).c_str() );
|
|
mi.assetTotals += mi.soundAssetsTotal;
|
|
|
|
common->Printf( " Used asset memory: %s bytes\n", idStr::FormatNumber( mi.assetTotals ).c_str() );
|
|
|
|
// write overview file
|
|
idFile *f;
|
|
|
|
f = fileSystem->OpenFileAppend( "maps/printmeminfo.txt" );
|
|
if ( !f ) {
|
|
return;
|
|
}
|
|
|
|
f->Printf( "total(%s ) image(%s ) model(%s ) sound(%s ): %s\n", idStr::FormatNumber( mi.assetTotals ).c_str(), idStr::FormatNumber( mi.imageAssetsTotal ).c_str(),
|
|
idStr::FormatNumber( mi.modelAssetsTotal ).c_str(), idStr::FormatNumber( mi.soundAssetsTotal ).c_str(), mi.filebase.c_str() );
|
|
|
|
fileSystem->CloseFile( f );
|
|
}
|
|
|
|
#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 <seconds>\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;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
__builtin_trap();
|
|
#else
|
|
* ( int * ) 0 = 0x12345678;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 <filename>\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();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 ) {
|
|
// DG: add an optional "nores" argument for "don't change the resolution" (r_mode)
|
|
bool nores = args.Argc() > 1 && idStr::Icmp( args.Argv(1), "nores" ) == 0;
|
|
if ( com_machineSpec.GetInteger() == 3 ) { // ultra
|
|
//cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE ); DG: redundant, set again below
|
|
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 );
|
|
if ( !nores ) // DG: added optional "nores" argument
|
|
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 ) { // high
|
|
cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
|
|
//cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE ); DG: redundant, set again below
|
|
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 );
|
|
if ( !nores ) // DG: added optional "nores" argument
|
|
cvarSystem->SetCVarInteger( "", 4, CVAR_ARCHIVE );
|
|
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
|
|
} else if ( com_machineSpec.GetInteger() == 1 ) { // medium
|
|
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 );
|
|
if ( !nores ) // DG: added optional "nores" argument
|
|
cvarSystem->SetCVarInteger( "r_mode", 3, CVAR_ARCHIVE );
|
|
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
|
|
} else { // low
|
|
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 );
|
|
if ( !nores ) // DG: added optional "nores" argument
|
|
cvarSystem->SetCVarInteger( "r_mode", 3 , CVAR_ARCHIVE );
|
|
cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
|
|
cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
|
|
}
|
|
|
|
cvarSystem->SetCVarBool( "com_purgeAll", false, CVAR_ARCHIVE );
|
|
cvarSystem->SetCVarBool( "r_forceLoadImages", false, CVAR_ARCHIVE );
|
|
|
|
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 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 && ( com_machineSpec.GetInteger() == 0 ) ) {
|
|
cvarSystem->SetCVarBool( "r_shadows", false, CVAR_ARCHIVE );
|
|
} else {
|
|
cvarSystem->SetCVarBool( "r_shadows", true, CVAR_ARCHIVE );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Com_ReloadEngine_f
|
|
=================
|
|
*/
|
|
void Com_ReloadEngine_f( const idCmdArgs &args ) {
|
|
bool menu = false;
|
|
|
|
if ( !commonLocal.IsInitialized() ) {
|
|
return;
|
|
}
|
|
|
|
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( );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idCommonLocal::GetLanguageDict
|
|
===============
|
|
*/
|
|
const idLangDict *idCommonLocal::GetLanguageDict( void ) {
|
|
return &languageDict;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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 ) {
|
|
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();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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 ) {
|
|
commonLocal.InitLanguageDict();
|
|
}
|
|
|
|
typedef idHashTable<idStrList> 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 <count | dictupdate | all> <map>\n" );
|
|
return;
|
|
}
|
|
|
|
int strCount = 0;
|
|
|
|
bool count = false;
|
|
bool dictUpdate = false;
|
|
bool write = false;
|
|
|
|
if ( idStr::Icmp( args.Argv(1), "count" ) == 0 ) {
|
|
count = true;
|
|
} else if ( idStr::Icmp( args.Argv(1), "dictupdate" ) == 0 ) {
|
|
count = true;
|
|
dictUpdate = true;
|
|
} else if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
|
|
count = true;
|
|
dictUpdate = true;
|
|
write = true;
|
|
} else {
|
|
common->Printf( "Invalid Command\n" );
|
|
common->Printf( "Usage: localizeMaps <count | dictupdate | all>\n" );
|
|
return;
|
|
|
|
}
|
|
|
|
idLangDict strTable;
|
|
idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
|
|
if(strTable.Load( filename ) == false) {
|
|
//This is a new file so set the base index
|
|
strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
|
|
}
|
|
|
|
common->SetRefreshOnPrint( true );
|
|
|
|
ListHash listHash;
|
|
LoadMapLocalizeData(listHash);
|
|
|
|
idStrList excludeList;
|
|
LoadGuiParmExcludeList(excludeList);
|
|
|
|
if(args.Argc() == 3) {
|
|
strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write);
|
|
} else {
|
|
idStrList files;
|
|
//wow, what now? a hardcoded path?
|
|
GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
|
|
for ( int i = 0; i < files.Num(); i++ ) {
|
|
idStr file = fileSystem->OSPathToRelativePath(files[i]);
|
|
strCount += LocalizeMap(file, strTable, listHash, excludeList, write);
|
|
}
|
|
}
|
|
|
|
if(count) {
|
|
common->Printf("Localize String Count: %d\n", strCount);
|
|
}
|
|
|
|
common->SetRefreshOnPrint( false );
|
|
|
|
if(dictUpdate) {
|
|
strTable.Save( filename );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
LocalizeGuis_f
|
|
=================
|
|
*/
|
|
void Com_LocalizeGuis_f( const idCmdArgs &args ) {
|
|
|
|
if ( args.Argc() != 2 ) {
|
|
common->Printf( "Usage: localizeGuis <all | gui>\n" );
|
|
return;
|
|
}
|
|
|
|
idLangDict strTable;
|
|
|
|
idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
|
|
if(strTable.Load( filename ) == false) {
|
|
//This is a new file so set the base index
|
|
strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
|
|
}
|
|
|
|
idFileList *files;
|
|
if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
|
|
idStr game = cvarSystem->GetCVarString( "fs_game" );
|
|
if(game.Length()) {
|
|
files = fileSystem->ListFilesTree( "guis", "*.gui", true, game );
|
|
} else {
|
|
files = fileSystem->ListFilesTree( "guis", "*.gui", true );
|
|
}
|
|
for ( int i = 0; i < files->GetNumFiles(); i++ ) {
|
|
commonLocal.LocalizeGui( files->GetFile( i ), strTable );
|
|
}
|
|
fileSystem->FreeFileList( files );
|
|
|
|
if(game.Length()) {
|
|
files = fileSystem->ListFilesTree( "guis", "*.pd", true, game );
|
|
} else {
|
|
files = fileSystem->ListFilesTree( "guis", "*.pd", true, "d3xp" );
|
|
}
|
|
|
|
for ( int i = 0; i < files->GetNumFiles(); i++ ) {
|
|
commonLocal.LocalizeGui( files->GetFile( i ), strTable );
|
|
}
|
|
fileSystem->FreeFileList( files );
|
|
|
|
} else {
|
|
commonLocal.LocalizeGui( args.Argv(1), strTable );
|
|
}
|
|
strTable.Save( filename );
|
|
}
|
|
|
|
void Com_LocalizeGuiParmsTest_f( const idCmdArgs &args ) {
|
|
|
|
common->SetRefreshOnPrint( true );
|
|
|
|
idFile *localizeFile = fileSystem->OpenFileWrite( "gui_parm_localize.csv" );
|
|
idFile *noLocalizeFile = fileSystem->OpenFileWrite( "gui_parm_nolocalize.csv" );
|
|
|
|
idStrList excludeList;
|
|
LoadGuiParmExcludeList(excludeList);
|
|
|
|
idStrList files;
|
|
GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
|
|
|
|
for ( int i = 0; i < files.Num(); i++ ) {
|
|
|
|
common->Printf("Testing Map '%s'\n", files[i].c_str());
|
|
idMapFile map;
|
|
|
|
idStr file = fileSystem->OSPathToRelativePath(files[i]);
|
|
if ( map.Parse(file, false, false ) ) {
|
|
int count = map.GetNumEntities();
|
|
for ( int j = 0; j < count; j++ ) {
|
|
idMapEntity *ent = map.GetEntity( j );
|
|
if ( ent ) {
|
|
const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
|
|
while( kv ) {
|
|
if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
|
|
idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
|
|
localizeFile->Write( out.c_str(), out.Length() );
|
|
} else {
|
|
idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
|
|
noLocalizeFile->Write( out.c_str(), out.Length() );
|
|
}
|
|
kv = ent->epairs.MatchPrefix( "gui_parm", kv );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fileSystem->CloseFile( localizeFile );
|
|
fileSystem->CloseFile( noLocalizeFile );
|
|
|
|
common->SetRefreshOnPrint( false );
|
|
}
|
|
|
|
|
|
void Com_LocalizeMapsTest_f( const idCmdArgs &args ) {
|
|
|
|
ListHash listHash;
|
|
LoadMapLocalizeData(listHash);
|
|
|
|
|
|
common->SetRefreshOnPrint( true );
|
|
|
|
idFile *localizeFile = fileSystem->OpenFileWrite( "map_localize.csv" );
|
|
|
|
idStrList files;
|
|
GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
|
|
|
|
for ( int i = 0; i < files.Num(); i++ ) {
|
|
|
|
common->Printf("Testing Map '%s'\n", files[i].c_str());
|
|
idMapFile map;
|
|
|
|
idStr file = fileSystem->OSPathToRelativePath(files[i]);
|
|
if ( map.Parse(file, false, false ) ) {
|
|
int count = map.GetNumEntities();
|
|
for ( int j = 0; j < count; j++ ) {
|
|
idMapEntity *ent = map.GetEntity( j );
|
|
if ( ent ) {
|
|
|
|
//Temp code to get a list of all entity key value pairs
|
|
/*idStr classname = ent->epairs.GetString("classname");
|
|
if(classname == "worldspawn" || classname == "func_static" || classname == "light" || classname == "speaker" || classname.Left(8) == "trigger_") {
|
|
continue;
|
|
}
|
|
for( int i = 0; i < ent->epairs.GetNumKeyVals(); i++) {
|
|
const idKeyValue* kv = ent->epairs.GetKeyVal(i);
|
|
idStr out = va("%s,%s,%s,%s\r\n", classname.c_str(), kv->GetKey().c_str(), kv->GetValue().c_str(), file.c_str());
|
|
localizeFile->Write( out.c_str(), out.Length() );
|
|
}*/
|
|
|
|
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(classname == "info_location" && (*list)[k] == "location") {
|
|
hasLocation = true;
|
|
}
|
|
|
|
if(val.Length() && TestMapVal(val)) {
|
|
|
|
if(!hasLocation || (*list)[k] == "location") {
|
|
idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
|
|
localizeFile->Write( out.c_str(), out.Length() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
|
|
localizeFile->Write( out.c_str(), out.Length() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fileSystem->CloseFile( localizeFile );
|
|
|
|
common->SetRefreshOnPrint( false );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Com_StartBuild_f
|
|
=================
|
|
*/
|
|
void Com_StartBuild_f( const idCmdArgs &args ) {
|
|
globalImages->StartBuild();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Com_FinishBuild_f
|
|
=================
|
|
*/
|
|
void Com_FinishBuild_f( const idCmdArgs &args ) {
|
|
if ( game ) {
|
|
game->CacheDictionaryMedia( NULL );
|
|
}
|
|
globalImages->FinishBuild( ( args.Argc() > 1 ) );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Com_Help_f
|
|
==============
|
|
*/
|
|
void Com_Help_f( const idCmdArgs &args ) {
|
|
common->Printf( "\nCommonly used commands:\n" );
|
|
common->Printf( " spawnServer - start the server.\n" );
|
|
common->Printf( " disconnect - shut down the server.\n" );
|
|
common->Printf( " listCmds - list all console commands.\n" );
|
|
common->Printf( " listCVars - list all console variables.\n" );
|
|
common->Printf( " kick - kick a client by number.\n" );
|
|
common->Printf( " gameKick - kick a client by name.\n" );
|
|
common->Printf( " serverNextMap - immediately load next map.\n" );
|
|
common->Printf( " serverMapRestart - restart the current map.\n" );
|
|
common->Printf( " serverForceReady - force all players to ready status.\n" );
|
|
common->Printf( "\nCommonly used variables:\n" );
|
|
common->Printf( " si_name - server name (change requires a restart to see)\n" );
|
|
common->Printf( " si_gametype - type of game.\n" );
|
|
common->Printf( " si_fragLimit - max kills to win (or lives in Last Man Standing).\n" );
|
|
common->Printf( " si_timeLimit - maximum time a game will last.\n" );
|
|
common->Printf( " si_warmup - do pre-game warmup.\n" );
|
|
common->Printf( " si_pure - pure server.\n" );
|
|
common->Printf( " g_mapCycle - name of .scriptcfg file for cycling maps.\n" );
|
|
common->Printf( "See mapcycle.scriptcfg for an example of a mapcyle script.\n\n" );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::InitCommands
|
|
=================
|
|
*/
|
|
void idCommonLocal::InitCommands( void ) {
|
|
cmdSystem->AddCommand( "error", Com_Error_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "causes an error" );
|
|
cmdSystem->AddCommand( "crash", Com_Crash_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "causes a crash" );
|
|
cmdSystem->AddCommand( "freeze", Com_Freeze_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "freezes the game for a number of seconds" );
|
|
cmdSystem->AddCommand( "quit", Com_Quit_f, CMD_FL_SYSTEM, "quits the game" );
|
|
cmdSystem->AddCommand( "exit", Com_Quit_f, CMD_FL_SYSTEM, "exits the game" );
|
|
cmdSystem->AddCommand( "writeConfig", Com_WriteConfig_f, CMD_FL_SYSTEM, "writes a config file" );
|
|
cmdSystem->AddCommand( "reloadEngine", Com_ReloadEngine_f, CMD_FL_SYSTEM, "reloads the engine down to including the file system" );
|
|
cmdSystem->AddCommand( "setMachineSpec", Com_SetMachineSpec_f, CMD_FL_SYSTEM, "detects system capabilities and sets com_machineSpec to appropriate value" );
|
|
cmdSystem->AddCommand( "execMachineSpec", Com_ExecMachineSpec_f, CMD_FL_SYSTEM, "execs the appropriate config files and sets cvars based on com_machineSpec" );
|
|
|
|
cmdSystem->AddCommand( "dhewm3Settings", Com_Dhewm3Settings_f, CMD_FL_SYSTEM, "Toggles (opens/closes) the (advanced) dhewm3 settings menu" );
|
|
|
|
#if !defined( ID_DEDICATED )
|
|
// compilers
|
|
cmdSystem->AddCommand( "dmap", Dmap_f, CMD_FL_TOOL, "compiles a map", idCmdSystem::ArgCompletion_MapName );
|
|
cmdSystem->AddCommand( "renderbump", RenderBump_f, CMD_FL_TOOL, "renders a bump map", idCmdSystem::ArgCompletion_ModelName );
|
|
cmdSystem->AddCommand( "renderbumpFlat", RenderBumpFlat_f, CMD_FL_TOOL, "renders a flat bump map", idCmdSystem::ArgCompletion_ModelName );
|
|
cmdSystem->AddCommand( "runAAS", RunAAS_f, CMD_FL_TOOL, "compiles an AAS file for a map", idCmdSystem::ArgCompletion_MapName );
|
|
cmdSystem->AddCommand( "runAASDir", RunAASDir_f, CMD_FL_TOOL, "compiles AAS files for all maps in a folder", idCmdSystem::ArgCompletion_MapName );
|
|
cmdSystem->AddCommand( "runReach", RunReach_f, CMD_FL_TOOL, "calculates reachability for an AAS file", idCmdSystem::ArgCompletion_MapName );
|
|
cmdSystem->AddCommand( "roq", RoQFileEncode_f, CMD_FL_TOOL, "encodes a roq file" );
|
|
#endif
|
|
|
|
#ifdef ID_ALLOW_TOOLS
|
|
// editors
|
|
cmdSystem->AddCommand( "editor", Com_Editor_f, CMD_FL_TOOL, "launches the level editor Radiant" );
|
|
cmdSystem->AddCommand( "editLights", Com_EditLights_f, CMD_FL_TOOL, "launches the in-game Light Editor" );
|
|
cmdSystem->AddCommand( "editSounds", Com_EditSounds_f, CMD_FL_TOOL, "launches the in-game Sound Editor" );
|
|
cmdSystem->AddCommand( "editDecls", Com_EditDecls_f, CMD_FL_TOOL, "launches the in-game Declaration Editor" );
|
|
cmdSystem->AddCommand( "editAFs", Com_EditAFs_f, CMD_FL_TOOL, "launches the in-game Articulated Figure Editor" );
|
|
cmdSystem->AddCommand( "editParticles", Com_EditParticles_f, CMD_FL_TOOL, "launches the in-game Particle Editor" );
|
|
cmdSystem->AddCommand( "editScripts", Com_EditScripts_f, CMD_FL_TOOL, "launches the in-game Script Editor" );
|
|
cmdSystem->AddCommand( "editGUIs", Com_EditGUIs_f, CMD_FL_TOOL, "launches the GUI Editor" );
|
|
cmdSystem->AddCommand( "editPDAs", Com_EditPDAs_f, CMD_FL_TOOL, "launches the in-game PDA Editor" );
|
|
cmdSystem->AddCommand( "debugger", Com_ScriptDebugger_f, CMD_FL_TOOL, "launches the Script Debugger" );
|
|
|
|
//BSM Nerve: Add support for the material editor
|
|
cmdSystem->AddCommand( "materialEditor", Com_MaterialEditor_f, CMD_FL_TOOL, "launches the Material Editor" );
|
|
#endif
|
|
|
|
cmdSystem->AddCommand( "printMemInfo", PrintMemInfo_f, CMD_FL_SYSTEM, "prints memory debugging data" );
|
|
|
|
// idLib commands
|
|
cmdSystem->AddCommand( "memoryDump", Mem_Dump_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "creates a memory dump" );
|
|
cmdSystem->AddCommand( "memoryDumpCompressed", Mem_DumpCompressed_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "creates a compressed memory dump" );
|
|
cmdSystem->AddCommand( "showStringMemory", idStr::ShowMemoryUsage_f, CMD_FL_SYSTEM, "shows memory used by strings" );
|
|
cmdSystem->AddCommand( "showDictMemory", idDict::ShowMemoryUsage_f, CMD_FL_SYSTEM, "shows memory used by dictionaries" );
|
|
cmdSystem->AddCommand( "listDictKeys", idDict::ListKeys_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "lists all keys used by dictionaries" );
|
|
cmdSystem->AddCommand( "listDictValues", idDict::ListValues_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "lists all values used by dictionaries" );
|
|
cmdSystem->AddCommand( "testSIMD", idSIMD::Test_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "test SIMD code" );
|
|
|
|
// localization
|
|
cmdSystem->AddCommand( "localizeGuis", Com_LocalizeGuis_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "localize guis" );
|
|
cmdSystem->AddCommand( "localizeMaps", Com_LocalizeMaps_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "localize maps" );
|
|
cmdSystem->AddCommand( "reloadLanguage", Com_ReloadLanguage_f, CMD_FL_SYSTEM, "reload language dict" );
|
|
|
|
//D3XP Localization
|
|
cmdSystem->AddCommand( "localizeGuiParmsTest", Com_LocalizeGuiParmsTest_f, CMD_FL_SYSTEM, "Create test files that show gui parms localized and ignored." );
|
|
cmdSystem->AddCommand( "localizeMapsTest", Com_LocalizeMapsTest_f, CMD_FL_SYSTEM, "Create test files that shows which strings will be localized." );
|
|
|
|
// build helpers
|
|
cmdSystem->AddCommand( "startBuild", Com_StartBuild_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "prepares to make a build" );
|
|
cmdSystem->AddCommand( "finishBuild", Com_FinishBuild_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "finishes the build process" );
|
|
|
|
#ifdef ID_DEDICATED
|
|
cmdSystem->AddCommand( "help", Com_Help_f, CMD_FL_SYSTEM, "shows help" );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::InitRenderSystem
|
|
=================
|
|
*/
|
|
void idCommonLocal::InitRenderSystem( void ) {
|
|
if ( com_skipRenderer.GetBool() ) {
|
|
return;
|
|
}
|
|
|
|
renderSystem->InitOpenGL();
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04343" ) );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::PrintLoadingMessage
|
|
=================
|
|
*/
|
|
void idCommonLocal::PrintLoadingMessage( const char *msg ) {
|
|
if ( !( msg && *msg ) ) {
|
|
return;
|
|
}
|
|
renderSystem->BeginFrame( renderSystem->GetScreenWidth(), renderSystem->GetScreenHeight() );
|
|
renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 1, 1, declManager->FindMaterial( "splashScreen" ) );
|
|
int len = strlen( msg );
|
|
renderSystem->DrawSmallStringExt( ( 640 - len * SMALLCHAR_WIDTH ) / 2, 410, msg, idVec4( 0.0f, 0.81f, 0.94f, 1.0f ), true, declManager->FindMaterial( "textures/bigchars" ) );
|
|
renderSystem->EndFrame( NULL, NULL );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::InitSIMD
|
|
=================
|
|
*/
|
|
void idCommonLocal::InitSIMD( void ) {
|
|
idSIMD::InitProcessor( "doom", com_forceGenericSIMD.GetBool() );
|
|
com_forceGenericSIMD.ClearModified();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::Frame
|
|
=================
|
|
*/
|
|
void idCommonLocal::Frame( void ) {
|
|
try {
|
|
|
|
// pump all the events
|
|
Sys_GenerateEvents();
|
|
|
|
// write config file if anything changed
|
|
WriteConfiguration();
|
|
|
|
// change SIMD implementation if required
|
|
if ( com_forceGenericSIMD.IsModified() ) {
|
|
InitSIMD();
|
|
}
|
|
|
|
if ( com_enableDebuggerServer.IsModified() ) {
|
|
if ( com_enableDebuggerServer.GetBool() ) {
|
|
DebuggerServerInit();
|
|
} else {
|
|
DebuggerServerShutdown();
|
|
}
|
|
}
|
|
|
|
eventLoop->RunEventLoop();
|
|
|
|
// DG: prepare new ImGui frame - I guess this is a good place, as all new events should be available?
|
|
D3::ImGuiHooks::NewFrame();
|
|
|
|
com_frameTime = com_ticNumber * USERCMD_MSEC;
|
|
|
|
idAsyncNetwork::RunFrame();
|
|
|
|
if ( idAsyncNetwork::IsActive() ) {
|
|
if ( idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
|
|
session->GuiFrameEvents();
|
|
session->UpdateScreen( false );
|
|
}
|
|
} else {
|
|
session->Frame();
|
|
|
|
// normal, in-sequence screen update
|
|
session->UpdateScreen( false );
|
|
}
|
|
|
|
// report timing information
|
|
if ( com_speeds.GetBool() ) {
|
|
static int lastTime;
|
|
int nowTime = Sys_Milliseconds();
|
|
int com_frameMsec = nowTime - lastTime;
|
|
lastTime = nowTime;
|
|
Printf( "frame:%i all:%3i gfr:%3i rf:%3i bk:%3i\n", com_frameNumber, com_frameMsec, time_gameFrame, time_frontend, time_backend );
|
|
time_gameFrame = 0;
|
|
time_gameDraw = 0;
|
|
}
|
|
|
|
com_frameNumber++;
|
|
|
|
// set idLib frame number for frame based memory dumps
|
|
idLib::frameNumber = com_frameNumber;
|
|
}
|
|
|
|
catch( idException & ) {
|
|
return; // an ERP_DROP was thrown
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::GUIFrame
|
|
=================
|
|
*/
|
|
void idCommonLocal::GUIFrame( bool execCmd, bool network ) {
|
|
Sys_GenerateEvents();
|
|
eventLoop->RunEventLoop( execCmd ); // and execute any commands
|
|
com_frameTime = com_ticNumber * USERCMD_MSEC;
|
|
if ( network ) {
|
|
idAsyncNetwork::RunFrame();
|
|
}
|
|
session->Frame();
|
|
session->UpdateScreen( false );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::SingleAsyncTic
|
|
|
|
The system will asyncronously call this function 60 times a second to
|
|
handle the time-critical functions that we don't want limited to
|
|
the frame rate:
|
|
|
|
sound mixing
|
|
user input generation (conditioned by com_asyncInput)
|
|
packet server operation
|
|
packet client operation
|
|
|
|
We are not using thread safe libraries, so any functionality put here must
|
|
be VERY VERY careful about what it calls.
|
|
=================
|
|
*/
|
|
|
|
typedef struct {
|
|
int milliseconds; // should always be incremeting by 60hz
|
|
int deltaMsec; // should always be 16
|
|
int timeConsumed; // msec spent in Com_AsyncThread()
|
|
int clientPacketsReceived;
|
|
int serverPacketsReceived;
|
|
int mostRecentServerPacketSequence;
|
|
} asyncStats_t;
|
|
|
|
static const int MAX_ASYNC_STATS = 1024;
|
|
asyncStats_t com_asyncStats[MAX_ASYNC_STATS]; // indexed by com_ticNumber
|
|
int prevAsyncMsec;
|
|
int lastTicMsec;
|
|
|
|
void idCommonLocal::SingleAsyncTic( void ) {
|
|
// main thread code can prevent this from happening while modifying
|
|
// critical data structures
|
|
Sys_EnterCriticalSection();
|
|
|
|
asyncStats_t *stat = &com_asyncStats[com_ticNumber & (MAX_ASYNC_STATS-1)];
|
|
memset( stat, 0, sizeof( *stat ) );
|
|
stat->milliseconds = Sys_Milliseconds();
|
|
stat->deltaMsec = stat->milliseconds - com_asyncStats[(com_ticNumber - 1) & (MAX_ASYNC_STATS-1)].milliseconds;
|
|
|
|
if ( usercmdGen && com_asyncInput.GetBool() ) {
|
|
usercmdGen->UsercmdInterrupt();
|
|
}
|
|
|
|
switch ( com_asyncSound.GetInteger() ) {
|
|
case 1:
|
|
case 3:
|
|
// DG: these are now used for the new default behavior of "update every async tic (every 16ms)"
|
|
soundSystem->AsyncUpdateWrite( stat->milliseconds );
|
|
break;
|
|
case 2:
|
|
// DG: use 2 for the old "update only 10x/second" behavior in case anyone likes that..
|
|
soundSystem->AsyncUpdate( stat->milliseconds );
|
|
break;
|
|
}
|
|
|
|
// we update com_ticNumber after all the background tasks
|
|
// have completed their work for this tic
|
|
com_ticNumber++;
|
|
|
|
stat->timeConsumed = Sys_Milliseconds() - stat->milliseconds;
|
|
|
|
Sys_LeaveCriticalSection();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::Async
|
|
=================
|
|
*/
|
|
void idCommonLocal::Async( void ) {
|
|
int msec = Sys_Milliseconds();
|
|
if ( !lastTicMsec ) {
|
|
lastTicMsec = msec - USERCMD_MSEC;
|
|
}
|
|
|
|
if ( !com_preciseTic.GetBool() ) {
|
|
// just run a single tic, even if the exact msec isn't precise
|
|
SingleAsyncTic();
|
|
return;
|
|
}
|
|
|
|
int ticMsec = USERCMD_MSEC;
|
|
|
|
// the number of msec per tic can be varies with the timescale cvar
|
|
float timescale = com_timescale.GetFloat();
|
|
if ( timescale != 1.0f ) {
|
|
ticMsec /= timescale;
|
|
if ( ticMsec < 1 ) {
|
|
ticMsec = 1;
|
|
}
|
|
}
|
|
|
|
// don't skip too many
|
|
if ( timescale == 1.0f ) {
|
|
if ( lastTicMsec + 10 * USERCMD_MSEC < msec ) {
|
|
lastTicMsec = msec - 10*USERCMD_MSEC;
|
|
}
|
|
}
|
|
|
|
while ( lastTicMsec + ticMsec <= msec ) {
|
|
SingleAsyncTic();
|
|
lastTicMsec += ticMsec;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::LoadGameDLLbyName
|
|
|
|
Helper for LoadGameDLL() to make it less painful to try different dll names.
|
|
=================
|
|
*/
|
|
void idCommonLocal::LoadGameDLLbyName( const char *dll, idStr& s ) {
|
|
s.CapLength(0);
|
|
// try next to the binary first (build tree)
|
|
if (Sys_GetPath(PATH_EXE, s)) {
|
|
// "s = " seems superfluous, but works around g++ 4.7 bug else StripFilename()
|
|
// (and possibly even CapLength()) seems to be "optimized" away and the string contains garbage
|
|
s = s.StripFilename();
|
|
s.AppendPath(dll);
|
|
gameDLL = sys->DLL_Load(s);
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
// then the lib/ dir relative to the binary on windows
|
|
if (!gameDLL && Sys_GetPath(PATH_EXE, s)) {
|
|
s.StripFilename();
|
|
s.AppendPath("lib");
|
|
s.AppendPath(dll);
|
|
gameDLL = sys->DLL_Load(s);
|
|
}
|
|
#elif defined(MACOS_X)
|
|
// then the binary dir in the bundle on osx
|
|
if (!gameDLL && Sys_GetPath(PATH_EXE, s)) {
|
|
s.StripFilename();
|
|
s.AppendPath(dll);
|
|
gameDLL = sys->DLL_Load(s);
|
|
}
|
|
|
|
// if not found in the bundle's directory, try in the bundle itself
|
|
if (!gameDLL && Sys_GetPath(PATH_EXE, s)) {
|
|
s.AppendPath("Contents/MacOS/base");
|
|
s.AppendPath(dll);
|
|
gameDLL = sys->DLL_Load(s);
|
|
}
|
|
#else
|
|
// then the install folder on *nix
|
|
if (!gameDLL) {
|
|
s = BUILD_LIBDIR;
|
|
s.AppendPath(dll);
|
|
gameDLL = sys->DLL_Load(s);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::LoadGameDLL
|
|
=================
|
|
*/
|
|
void idCommonLocal::LoadGameDLL( void ) {
|
|
#ifdef __DOOM_DLL__
|
|
const char *fs_game;
|
|
char dll[MAX_OSPATH];
|
|
idStr s;
|
|
|
|
gameImport_t gameImport;
|
|
gameExport_t gameExport;
|
|
GetGameAPI_t GetGameAPI;
|
|
|
|
fs_game = cvarSystem->GetCVarString("fs_game");
|
|
if (!fs_game || !fs_game[0])
|
|
fs_game = BASE_GAMEDIR;
|
|
|
|
gameDLL = 0;
|
|
|
|
sys->DLL_GetFileName(fs_game, dll, sizeof(dll));
|
|
LoadGameDLLbyName(dll, s);
|
|
|
|
// there was no gamelib for this mod, use default one from base game
|
|
if (!gameDLL) {
|
|
common->Printf( "\n" );
|
|
common->Warning( "couldn't load mod-specific %s, defaulting to base game's library!\n", dll );
|
|
sys->DLL_GetFileName(BASE_GAMEDIR, dll, sizeof(dll));
|
|
LoadGameDLLbyName(dll, s);
|
|
}
|
|
|
|
if ( !gameDLL ) {
|
|
common->FatalError( "couldn't load game dynamic library" );
|
|
return;
|
|
}
|
|
|
|
common->Printf("loaded game library '%s'.\n", s.c_str());
|
|
|
|
GetGameAPI = (GetGameAPI_t) Sys_DLL_GetProcAddress( gameDLL, "GetGameAPI" );
|
|
if ( !GetGameAPI ) {
|
|
Sys_DLL_Unload( gameDLL );
|
|
gameDLL = 0;
|
|
common->FatalError( "couldn't find game DLL API" );
|
|
return;
|
|
}
|
|
|
|
gameImport.version = GAME_API_VERSION;
|
|
gameImport.sys = ::sys;
|
|
gameImport.common = ::common;
|
|
gameImport.cmdSystem = ::cmdSystem;
|
|
gameImport.cvarSystem = ::cvarSystem;
|
|
gameImport.fileSystem = ::fileSystem;
|
|
gameImport.networkSystem = ::networkSystem;
|
|
gameImport.renderSystem = ::renderSystem;
|
|
gameImport.soundSystem = ::soundSystem;
|
|
gameImport.renderModelManager = ::renderModelManager;
|
|
gameImport.uiManager = ::uiManager;
|
|
gameImport.declManager = ::declManager;
|
|
gameImport.AASFileManager = ::AASFileManager;
|
|
gameImport.collisionModelManager = ::collisionModelManager;
|
|
|
|
gameExport = *GetGameAPI( &gameImport);
|
|
|
|
if ( gameExport.version != GAME_API_VERSION ) {
|
|
Sys_DLL_Unload( gameDLL );
|
|
gameDLL = 0;
|
|
common->FatalError( "wrong game DLL API version" );
|
|
return;
|
|
}
|
|
|
|
game = gameExport.game;
|
|
gameEdit = gameExport.gameEdit;
|
|
|
|
#endif
|
|
|
|
// initialize the game object
|
|
if ( game != NULL ) {
|
|
game->Init();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::UnloadGameDLL
|
|
=================
|
|
*/
|
|
void idCommonLocal::UnloadGameDLL( void ) {
|
|
|
|
// shut down the game object
|
|
if ( game != NULL ) {
|
|
game->Shutdown();
|
|
}
|
|
|
|
#ifdef __DOOM_DLL__
|
|
|
|
if ( gameDLL ) {
|
|
Sys_DLL_Unload( gameDLL );
|
|
gameDLL = 0;
|
|
}
|
|
game = NULL;
|
|
gameEdit = NULL;
|
|
|
|
#endif
|
|
|
|
com_debuggerSupported = false; // HvG: Reset debugger availability.
|
|
gameCallbacks.Reset(); // DG: these callbacks are invalid now because DLL has been unloaded
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::IsInitialized
|
|
=================
|
|
*/
|
|
bool idCommonLocal::IsInitialized( void ) const {
|
|
return com_fullyInitialized;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::SetMachineSpec
|
|
=================
|
|
*/
|
|
void idCommonLocal::SetMachineSpec( void ) {
|
|
int sysRam = Sys_GetSystemRam();
|
|
|
|
Printf( "Detected\n\t%i MB of System memory\n\n", sysRam );
|
|
|
|
if ( sysRam >= 1024 ) {
|
|
Printf( "This system qualifies for Ultra quality!\n" );
|
|
com_machineSpec.SetInteger( 3 );
|
|
} else if ( sysRam >= 512 ) {
|
|
Printf( "This system qualifies for High quality!\n" );
|
|
com_machineSpec.SetInteger( 2 );
|
|
} else if ( sysRam >= 384 ) {
|
|
Printf( "This system qualifies for Medium quality.\n" );
|
|
com_machineSpec.SetInteger( 1 );
|
|
} else {
|
|
Printf( "This system qualifies for Low quality.\n" );
|
|
com_machineSpec.SetInteger( 0 );
|
|
}
|
|
}
|
|
|
|
static unsigned int AsyncTimer(unsigned int interval, void *) {
|
|
common->Async();
|
|
Sys_TriggerEvent(TRIGGER_EVENT_ONE);
|
|
|
|
// calculate the next interval to get as close to 60fps as possible
|
|
unsigned int now = SDL_GetTicks();
|
|
unsigned int tick = com_ticNumber * USERCMD_MSEC;
|
|
// FIXME: this is pretty broken and basically always returns 1 because now now is much bigger than tic
|
|
// (probably com_tickNumber only starts incrementing a second after engine starts?)
|
|
// only reason this works is common->Async() checking again before calling SingleAsyncTic()
|
|
|
|
if (now >= tick)
|
|
return 1;
|
|
|
|
return tick - now;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#include "../sys/win32/win_local.h" // for Conbuf_AppendText()
|
|
#endif // _WIN32
|
|
|
|
static bool checkForHelp(int argc, char **argv)
|
|
{
|
|
const char* helpArgs[] = { "--help", "-h", "-help", "-?", "/?" };
|
|
const int numHelpArgs = sizeof(helpArgs)/sizeof(helpArgs[0]);
|
|
|
|
for (int i=0; i<argc; ++i)
|
|
{
|
|
const char* arg = argv[i];
|
|
for (int h=0; h<numHelpArgs; ++h)
|
|
{
|
|
if (idStr::Icmp(arg, helpArgs[h]) == 0)
|
|
{
|
|
#ifdef _WIN32
|
|
// write it to the Windows-only console window
|
|
#define WriteString(s) Conbuf_AppendText(s)
|
|
#else // not windows
|
|
// write it to stdout
|
|
#define WriteString(s) fputs(s, stdout);
|
|
#endif // _WIN32
|
|
WriteString(ENGINE_VERSION " - http://dhewm3.org\n");
|
|
WriteString("Commandline arguments:\n");
|
|
WriteString("-h or --help: Show this help\n");
|
|
WriteString("+<command> [command arguments]\n");
|
|
WriteString(" executes a command (with optional arguments)\n");
|
|
|
|
WriteString("\nSome interesting commands:\n");
|
|
WriteString("+map <map>\n");
|
|
WriteString(" directly loads the given level, e.g. +map game/hell1\n");
|
|
WriteString("+exec <config>\n");
|
|
WriteString(" execute the given config (mainly relevant for dedicated servers)\n");
|
|
WriteString("+disconnect\n");
|
|
WriteString(" starts the game, goes directly into main menu without showing\n logo video\n");
|
|
WriteString("+connect <host>[:port]\n");
|
|
WriteString(" directly connect to multiplayer server at given host/port\n");
|
|
WriteString(" e.g. +connect d3.example.com\n");
|
|
WriteString(" e.g. +connect d3.example.com:27667\n");
|
|
WriteString(" e.g. +connect 192.168.0.42:27666\n");
|
|
WriteString("+set <cvarname> <value>\n");
|
|
WriteString(" Set the given cvar to the given value, e.g. +set r_fullscreen 0\n");
|
|
WriteString("+seta <cvarname> <value>\n");
|
|
WriteString(" like +set, but also makes sure the changed cvar is saved (\"archived\")\n in a cfg\n");
|
|
|
|
WriteString("\nSome interesting cvars:\n");
|
|
WriteString("+set fs_basepath <gamedata path>\n");
|
|
WriteString(" set path to your Doom3 game data (the directory base/ is in)\n");
|
|
WriteString("+set fs_game <modname>\n");
|
|
WriteString(" start the given addon/mod, e.g. +set fs_game d3xp\n");
|
|
#ifndef ID_DEDICATED
|
|
WriteString("+set r_fullscreen <0 or 1>\n");
|
|
WriteString(" start game in windowed (0) or fullscreen (1) mode\n");
|
|
WriteString("+set r_mode <modenumber>\n");
|
|
WriteString(" start game in resolution belonging to <modenumber>,\n");
|
|
WriteString(" use -1 for custom resolutions:\n");
|
|
WriteString("+set r_customWidth <size in pixels>\n");
|
|
WriteString("+set r_customHeight <size in pixels>\n");
|
|
WriteString(" if r_mode is set to -1, these cvars allow you to specify the\n");
|
|
WriteString(" width/height of your custom resolution\n");
|
|
#endif // !ID_DEDICATED
|
|
WriteString("\nSee https://modwiki.dhewm3.org/CVars_%28Doom_3%29 for more cvars\n");
|
|
WriteString("See https://modwiki.dhewm3.org/Commands_%28Doom_3%29 for more commands\n");
|
|
|
|
#undef WriteString
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef UINTPTR_MAX // DG: make sure D3_SIZEOFPTR is consistent with reality
|
|
|
|
#if D3_SIZEOFPTR == 4
|
|
#if UINTPTR_MAX != 0xFFFFFFFFUL
|
|
#error "CMake assumes that we're building for a 32bit architecture, but UINTPTR_MAX doesn't match!"
|
|
#endif
|
|
#elif D3_SIZEOFPTR == 8
|
|
#if UINTPTR_MAX != 18446744073709551615ULL
|
|
#error "CMake assumes that we're building for a 64bit architecture, but UINTPTR_MAX doesn't match!"
|
|
#endif
|
|
#else
|
|
// Hello future person with a 128bit(?) CPU, I hope the future doesn't suck too much and that you don't still use CMake.
|
|
// Also, please adapt this check and send a pull request (or whatever way we have to send patches in the future)
|
|
#error "D3_SIZEOFPTR should really be 4 (for 32bit targets) or 8 (for 64bit targets), what kind of machine is this?!"
|
|
#endif
|
|
|
|
#endif // UINTPTR_MAX defined
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::Init
|
|
=================
|
|
*/
|
|
void idCommonLocal::Init( int argc, char **argv ) {
|
|
|
|
// in case UINTPTR_MAX isn't defined (or wrong), do a runtime check at startup
|
|
if ( D3_SIZEOFPTR != sizeof(void*) ) {
|
|
Sys_Error( "Something went wrong in your build: CMake assumed that sizeof(void*) == %d but in reality it's %d!\n",
|
|
(int)D3_SIZEOFPTR, (int)sizeof(void*) );
|
|
}
|
|
|
|
if(checkForHelp(argc, argv))
|
|
{
|
|
// game has been started with --help (or similar), usage message has been shown => quit
|
|
#ifdef _WIN32
|
|
// this enforces that the console window is shown until the user closes it
|
|
// => checkForHelp() writes to the console window on Windows
|
|
Sys_Error(".");
|
|
#endif // _WIN32
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef ID_DEDICATED
|
|
// we want to use the SDL event queue for dedicated servers. That
|
|
// requires video to be initialized, so we just use the dummy
|
|
// driver for headless boxen
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
|
|
#else
|
|
char dummy[] = "SDL_VIDEODRIVER=dummy\0";
|
|
SDL_putenv(dummy);
|
|
#endif
|
|
#endif
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
|
|
{
|
|
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) == 0) { // retry without joystick/gamecontroller if it failed
|
|
Sys_Printf( "WARNING: Couldn't get SDL gamecontroller support! Gamepads won't work!\n" );
|
|
} else
|
|
#else
|
|
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) // no gamecontroller support in SDL1
|
|
{
|
|
#endif
|
|
{
|
|
Sys_Error("Error while initializing SDL: %s", SDL_GetError());
|
|
}
|
|
}
|
|
|
|
Sys_InitThreads();
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
/* Force the window to minimize when focus is lost. This was the
|
|
* default behavior until SDL 2.0.12 and changed with 2.0.14.
|
|
* The windows staying maximized has some odd implications for
|
|
* window ordering under Windows and some X11 window managers
|
|
* like kwin. See:
|
|
* * https://github.com/libsdl-org/SDL/issues/4039
|
|
* * https://github.com/libsdl-org/SDL/issues/3656 */
|
|
SDL_SetHint( SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1" );
|
|
#endif
|
|
|
|
try {
|
|
|
|
// set interface pointers used by idLib
|
|
idLib::sys = sys;
|
|
idLib::common = common;
|
|
idLib::cvarSystem = cvarSystem;
|
|
idLib::fileSystem = fileSystem;
|
|
|
|
// initialize idLib
|
|
idLib::Init();
|
|
|
|
// clear warning buffer
|
|
ClearWarnings( GAME_NAME " initialization" );
|
|
|
|
// parse command line options
|
|
ParseCommandLine( argc, argv );
|
|
|
|
// init console command system
|
|
cmdSystem->Init();
|
|
|
|
// init CVar system
|
|
cvarSystem->Init();
|
|
|
|
// start file logging right away, before early console or whatever
|
|
StartupVariable( "win_outputDebugString", false );
|
|
|
|
// register all static CVars
|
|
idCVar::RegisterStaticVars();
|
|
|
|
// print engine version
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
SDL_version sdlv;
|
|
SDL_GetVersion(&sdlv);
|
|
#else
|
|
SDL_version sdlv = *SDL_Linked_Version();
|
|
#endif
|
|
Printf( "%s using SDL v%u.%u.%u\n",
|
|
version.string, sdlv.major, sdlv.minor, sdlv.patch );
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
Printf( "SDL video driver: %s\n", SDL_GetCurrentVideoDriver() );
|
|
#endif
|
|
|
|
// initialize key input/binding, done early so bind command exists
|
|
idKeyInput::Init();
|
|
|
|
// init the console so we can take prints
|
|
console->Init();
|
|
|
|
// get architecture info
|
|
Sys_Init();
|
|
|
|
// initialize networking
|
|
Sys_InitNetworking();
|
|
|
|
// override cvars from command line
|
|
StartupVariable( NULL, false );
|
|
|
|
// set fpu double extended precision
|
|
Sys_FPU_SetPrecision();
|
|
|
|
// initialize processor specific SIMD implementation
|
|
InitSIMD();
|
|
|
|
// init commands
|
|
InitCommands();
|
|
|
|
#ifdef ID_WRITE_VERSION
|
|
config_compressor = idCompressor::AllocArithmetic();
|
|
#endif
|
|
|
|
// game specific initialization
|
|
InitGame();
|
|
|
|
// don't add startup commands if no CD key is present
|
|
#if ID_ENFORCE_KEY
|
|
if ( !session->CDKeysAreValid( false ) || !AddStartupCommands() ) {
|
|
#else
|
|
if ( !AddStartupCommands() ) {
|
|
#endif
|
|
// if the user didn't give any commands, run default action
|
|
session->StartMenu( true );
|
|
}
|
|
|
|
// print all warnings queued during initialization
|
|
PrintWarnings();
|
|
|
|
#ifdef ID_DEDICATED
|
|
Printf( "\nType 'help' for dedicated server info.\n\n" );
|
|
#endif
|
|
|
|
// remove any prints from the notify lines
|
|
console->ClearNotifyLines();
|
|
|
|
ClearCommandLine();
|
|
|
|
// load the persistent console history
|
|
console->LoadHistory();
|
|
|
|
com_fullyInitialized = true;
|
|
}
|
|
|
|
catch( idException & ) {
|
|
Sys_Error( "Error during initialization" );
|
|
}
|
|
|
|
async_timer = SDL_AddTimer(USERCMD_MSEC, AsyncTimer, NULL);
|
|
|
|
if (!async_timer)
|
|
Sys_Error("Error while starting the async timer: %s", SDL_GetError());
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::Shutdown
|
|
=================
|
|
*/
|
|
void idCommonLocal::Shutdown( void ) {
|
|
if (async_timer) {
|
|
SDL_RemoveTimer(async_timer);
|
|
async_timer = 0;
|
|
}
|
|
|
|
idAsyncNetwork::server.Kill();
|
|
idAsyncNetwork::client.Shutdown();
|
|
|
|
// save persistent console history
|
|
console->SaveHistory();
|
|
|
|
// game specific shut down
|
|
ShutdownGame( false );
|
|
|
|
// shut down non-portable system services
|
|
Sys_Shutdown();
|
|
|
|
// shut down the console
|
|
console->Shutdown();
|
|
|
|
// shut down the key system
|
|
idKeyInput::Shutdown();
|
|
|
|
// shut down the cvar system
|
|
cvarSystem->Shutdown();
|
|
|
|
// shut down the console command system
|
|
cmdSystem->Shutdown();
|
|
|
|
#ifdef ID_WRITE_VERSION
|
|
delete config_compressor;
|
|
config_compressor = NULL;
|
|
#endif
|
|
|
|
// free any buffered warning messages
|
|
ClearWarnings( GAME_NAME " shutdown" );
|
|
warningCaption.Clear();
|
|
errorList.Clear();
|
|
|
|
// free language dictionary
|
|
languageDict.Clear();
|
|
|
|
// enable leak test
|
|
Mem_EnableLeakTest( "doom" );
|
|
|
|
// shutdown idLib
|
|
idLib::ShutDown();
|
|
|
|
Sys_ShutdownThreads();
|
|
|
|
SDL_Quit();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::InitGame
|
|
=================
|
|
*/
|
|
void idCommonLocal::InitGame( void ) {
|
|
// initialize the file system
|
|
fileSystem->Init();
|
|
|
|
// initialize the declaration manager
|
|
declManager->Init();
|
|
|
|
// force r_fullscreen 0 if running a tool
|
|
CheckToolMode();
|
|
|
|
idFile *file = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( CONFIG_SPEC, "fs_configpath" ) );
|
|
bool sysDetect = ( file == NULL );
|
|
if ( file ) {
|
|
fileSystem->CloseFile( file );
|
|
} else {
|
|
file = fileSystem->OpenFileWrite( CONFIG_SPEC, "fs_configpath" );
|
|
fileSystem->CloseFile( file );
|
|
}
|
|
|
|
idCmdArgs args;
|
|
if ( sysDetect ) {
|
|
SetMachineSpec();
|
|
Com_ExecMachineSpec_f( args );
|
|
}
|
|
|
|
// initialize the renderSystem data structures, but don't start OpenGL yet
|
|
renderSystem->Init();
|
|
|
|
// initialize string database right off so we can use it for loading messages
|
|
InitLanguageDict();
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04344" ) );
|
|
|
|
// load the font, etc
|
|
console->LoadGraphics();
|
|
|
|
// init journalling, etc
|
|
eventLoop->Init();
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04345" ) );
|
|
|
|
// exec the startup scripts
|
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec editor.cfg\n" );
|
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec default.cfg\n" );
|
|
|
|
// skip the config file if "safe" is on the command line
|
|
if ( !SafeMode() ) {
|
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec " CONFIG_FILE "\n" );
|
|
}
|
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec autoexec.cfg\n" );
|
|
|
|
// reload the language dictionary now that we've loaded config files
|
|
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reloadLanguage\n" );
|
|
|
|
// run cfg execution
|
|
cmdSystem->ExecuteCommandBuffer();
|
|
|
|
// re-override anything from the config files with command line args
|
|
StartupVariable( NULL, false );
|
|
|
|
// if any archived cvars are modified after this, we will trigger a writing of the config file
|
|
cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE );
|
|
|
|
// init the user command input code
|
|
usercmdGen->Init();
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04346" ) );
|
|
|
|
// start the sound system, but don't do any hardware operations yet
|
|
soundSystem->Init();
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04347" ) );
|
|
|
|
// init async network
|
|
idAsyncNetwork::Init();
|
|
|
|
#ifdef ID_DEDICATED
|
|
idAsyncNetwork::server.InitPort();
|
|
cvarSystem->SetCVarBool( "s_noSound", true );
|
|
#else
|
|
if ( idAsyncNetwork::serverDedicated.GetInteger() == 1 ) {
|
|
idAsyncNetwork::server.InitPort();
|
|
cvarSystem->SetCVarBool( "s_noSound", true );
|
|
} else {
|
|
// init OpenGL, which will open a window and connect sound and input hardware
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04348" ) );
|
|
InitRenderSystem();
|
|
}
|
|
#endif
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04349" ) );
|
|
|
|
// initialize the user interfaces
|
|
uiManager->Init();
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) );
|
|
|
|
// load the game dll
|
|
LoadGameDLL();
|
|
|
|
// startup the script debugger
|
|
if ( com_enableDebuggerServer.GetBool( ) )
|
|
DebuggerServerInit( );
|
|
|
|
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) );
|
|
|
|
// init the session
|
|
session->Init();
|
|
|
|
// have to do this twice.. first one sets the correct r_mode for the renderer init
|
|
// this time around the backend is all setup correct.. a bit fugly but do not want
|
|
// to mess with all the gl init at this point.. an old vid card will never qualify for
|
|
if ( sysDetect ) {
|
|
SetMachineSpec();
|
|
Com_ExecMachineSpec_f( args );
|
|
cvarSystem->SetCVarInteger( "s_numberOfSpeakers", 6 );
|
|
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "s_restart\n" );
|
|
cmdSystem->ExecuteCommandBuffer();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idCommonLocal::ShutdownGame
|
|
=================
|
|
*/
|
|
void idCommonLocal::ShutdownGame( bool reloading ) {
|
|
|
|
// kill sound first
|
|
idSoundWorld *sw = soundSystem->GetPlayingSoundWorld();
|
|
if ( sw ) {
|
|
sw->StopAllSounds();
|
|
}
|
|
|
|
// shutdown the script debugger
|
|
if ( com_enableDebuggerServer.GetBool() )
|
|
DebuggerServerShutdown();
|
|
|
|
idAsyncNetwork::client.Shutdown();
|
|
|
|
// shut down the session
|
|
session->Shutdown();
|
|
|
|
// shut down the user interfaces
|
|
uiManager->Shutdown();
|
|
|
|
// shut down the sound system
|
|
soundSystem->Shutdown();
|
|
|
|
// shut down async networking
|
|
idAsyncNetwork::Shutdown();
|
|
|
|
// shut down the user command input code
|
|
usercmdGen->Shutdown();
|
|
|
|
// shut down the event loop
|
|
eventLoop->Shutdown();
|
|
|
|
// shut down the renderSystem
|
|
renderSystem->Shutdown();
|
|
|
|
// shutdown the decl manager
|
|
declManager->Shutdown();
|
|
|
|
// unload the game dll
|
|
UnloadGameDLL();
|
|
|
|
// dump warnings to "warnings.txt"
|
|
#ifdef DEBUG
|
|
DumpWarnings();
|
|
#endif
|
|
|
|
// shut down the file system
|
|
fileSystem->Shutdown( reloading );
|
|
}
|
|
|
|
// DG: below here are hacks to allow adding callbacks and exporting additional functions to the
|
|
// Game DLL without breaking the ABI. See Common.h for longer explanation...
|
|
|
|
|
|
// returns true if setting the callback was successful, else false
|
|
// When a game DLL is unloaded the callbacks are automatically removed from the Engine
|
|
// so you usually don't have to worry about that; but you can call this with cb = NULL
|
|
// and userArg = NULL to remove a callback manually (e.g. if userArg refers to an object you deleted)
|
|
bool idCommonLocal::SetCallback(idCommon::CallbackType cbt, idCommon::FunctionPointer cb, void* userArg)
|
|
{
|
|
switch(cbt)
|
|
{
|
|
case idCommon::CB_ReloadImages:
|
|
gameCallbacks.reloadImagesCB = (idGameCallbacks::ReloadImagesCallback)cb;
|
|
gameCallbacks.reloadImagesUserArg = userArg;
|
|
return true;
|
|
|
|
default:
|
|
Warning("Called idCommon::SetCallback() with unknown CallbackType %d!\n", cbt);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool isDemo( void )
|
|
{
|
|
return sessLocal.IsDemoVersion();
|
|
}
|
|
|
|
static bool updateDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer )
|
|
{
|
|
if (com_editors & EDITOR_DEBUGGER)
|
|
{
|
|
DebuggerServerCheckBreakpoint( interpreter, program, instructionPointer );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// returns true if that function is available in this version of dhewm3
|
|
// *out_fnptr will be the function (you'll have to cast it probably)
|
|
// *out_userArg will be an argument you have to pass to the function, if appropriate (else NULL)
|
|
bool idCommonLocal::GetAdditionalFunction(idCommon::FunctionType ft, idCommon::FunctionPointer* out_fnptr, void** out_userArg)
|
|
{
|
|
if(out_userArg != NULL)
|
|
*out_userArg = NULL;
|
|
|
|
if(out_fnptr == NULL)
|
|
{
|
|
Warning("Called idCommon::GetAdditionalFunction() with out_fnptr == NULL!\n");
|
|
return false;
|
|
}
|
|
|
|
switch(ft)
|
|
{
|
|
case idCommon::FT_IsDemo:
|
|
*out_fnptr = (idCommon::FunctionPointer)isDemo;
|
|
// don't set *out_userArg, this function takes no arguments
|
|
return true;
|
|
|
|
case idCommon::FT_UpdateDebugger:
|
|
*out_fnptr = (idCommon::FunctionPointer)updateDebugger;
|
|
com_debuggerSupported = true;
|
|
return true;
|
|
|
|
default:
|
|
*out_fnptr = NULL;
|
|
Warning("Called idCommon::SetCallback() with unknown FunctionType %d!\n", ft);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
idGameCallbacks gameCallbacks;
|
|
|
|
idGameCallbacks::idGameCallbacks()
|
|
: reloadImagesCB(NULL), reloadImagesUserArg(NULL)
|
|
{}
|
|
|
|
void idGameCallbacks::Reset()
|
|
{
|
|
reloadImagesCB = NULL;
|
|
reloadImagesUserArg = NULL;
|
|
}
|