/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program 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.
**
** This program 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 this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/ArchiveFileSystem.h"
#include "Common/CommandLine.h"
#include "Common/CRCDebug.h"
#include "Common/LocalFileSystem.h"
#include "Common/Version.h"
#include "GameClient/TerrainVisual.h" // for TERRAIN_LOD_MIN definition
#include "GameClient/GameText.h"
Bool TheDebugIgnoreSyncErrors = FALSE;
extern Int DX8Wrapper_PreserveFPU;
#ifdef DEBUG_CRC
Int TheCRCFirstFrameToLog = -1;
UnsignedInt TheCRCLastFrameToLog = 0xffffffff;
Bool g_keepCRCSaves = FALSE;
Bool g_crcModuleDataFromLogic = FALSE;
Bool g_crcModuleDataFromClient = FALSE;
Bool g_verifyClientCRC = FALSE; // verify that GameLogic CRC doesn't change from client
Bool g_clientDeepCRC = FALSE;
Bool g_logObjectCRCs = FALSE;
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
extern Bool g_useStringFile;
#endif
// Retval is number of cmd-line args eaten
typedef Int (*FuncPtr)( char *args[], int num );
static const UnsignedByte F_NOCASE = 1; // Case-insensitive
struct CommandLineParam
{
const char *name;
FuncPtr func;
};
static void ConvertShortMapPathToLongMapPath(AsciiString &mapName)
{
AsciiString path = mapName;
AsciiString token;
AsciiString actualpath;
if ((path.find('\\') == NULL) && (path.find('/') == NULL))
{
DEBUG_CRASH(("Invalid map name %s", mapName.str()));
return;
}
path.nextToken(&token, "\\/");
while (!token.endsWithNoCase(".map") && (token.getLength() > 0))
{
actualpath.concat(token);
actualpath.concat('\\');
path.nextToken(&token, "\\/");
}
if (!token.endsWithNoCase(".map"))
{
DEBUG_CRASH(("Invalid map name %s", mapName.str()));
}
// remove the .map from the end.
token.removeLastChar();
token.removeLastChar();
token.removeLastChar();
token.removeLastChar();
actualpath.concat(token);
actualpath.concat('\\');
actualpath.concat(token);
actualpath.concat(".map");
mapName = actualpath;
}
//=============================================================================
//=============================================================================
Int parseNoLogOrCrash(char *args[], int)
{
#ifdef ALLOW_DEBUG_UTILS
DEBUG_CRASH(("-NoLogOrCrash not supported in this build\n"));
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseWin(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_windowed = true;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoMusic(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_musicOn = false;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoVideo(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_videoOn = false;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseFPUPreserve(char *args[], int argc)
{
if (argc > 1)
{
DX8Wrapper_PreserveFPU = atoi(args[1]);
}
return 2;
}
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
//=============================================================================
Int parseUseCSF(char *args[], int)
{
g_useStringFile = FALSE;
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoInputDisable(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_disableScriptedInputDisabling = true;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoFade(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_disableCameraFade = true;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoMilCap(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_disableMilitaryCaption = true;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseDebugCRCFromFrame(char *args[], int argc)
{
#ifdef DEBUG_CRC
if (argc > 1)
{
TheCRCFirstFrameToLog = atoi(args[1]);
}
#endif
return 2;
}
//=============================================================================
//=============================================================================
Int parseDebugCRCUntilFrame(char *args[], int argc)
{
#ifdef DEBUG_CRC
if (argc > 1)
{
TheCRCLastFrameToLog = atoi(args[1]);
}
#endif
return 2;
}
//=============================================================================
//=============================================================================
Int parseKeepCRCSave(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_keepCRCSaves = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseCRCLogicModuleData(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_crcModuleDataFromLogic = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseCRCClientModuleData(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_crcModuleDataFromClient = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseClientDeepCRC(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_clientDeepCRC = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseVerifyClientCRC(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_verifyClientCRC = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseLogObjectCRCs(char *args[], int argc)
{
#ifdef DEBUG_CRC
g_logObjectCRCs = TRUE;
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseNetCRCInterval(char *args[], int argc)
{
#ifdef DEBUG_CRC
if (argc > 1)
{
NET_CRC_INTERVAL = atoi(args[1]);
}
#endif
return 2;
}
//=============================================================================
//=============================================================================
Int parseReplayCRCInterval(char *args[], int argc)
{
#ifdef DEBUG_CRC
if (argc > 1)
{
REPLAY_CRC_INTERVAL = atoi(args[1]);
}
#endif
return 2;
}
//=============================================================================
//=============================================================================
Int parseNoDraw(char *args[], int argc)
{
#ifdef DEBUG_CRC
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_noDraw = TRUE;
}
#endif
return 1;
}
//=============================================================================
//=============================================================================
Int parseLogToConsole(char *args[], int)
{
DebugSetFlags(DebugGetFlags() | DEBUG_FLAG_LOG_TO_CONSOLE);
return 1;
}
#endif // _DEBUG || _INTERNAL
//=============================================================================
//=============================================================================
Int parseNoAudio(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_audioOn = false;
TheWritableGlobalData->m_speechOn = false;
TheWritableGlobalData->m_soundsOn = false;
TheWritableGlobalData->m_musicOn = false;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoWin(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_windowed = false;
}
return 1;
}
Int parseFullVersion(char *args[], int num)
{
if (TheVersion && num > 1)
{
TheVersion->setShowFullVersion(atoi(args[1]) != 0);
}
return 1;
}
Int parseNoShadows(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_useShadowVolumes = false;
TheWritableGlobalData->m_useShadowDecals = false;
}
return 1;
}
Int parseMapName(char *args[], int num)
{
if (TheWritableGlobalData && num == 2)
{
TheWritableGlobalData->m_mapName.set( args[ 1 ] );
ConvertShortMapPathToLongMapPath(TheWritableGlobalData->m_mapName);
}
return 1;
}
Int parseXRes(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_xResolution = atoi(args[1]);
return 2;
}
return 1;
}
Int parseYRes(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_yResolution = atoi(args[1]);
return 2;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
//=============================================================================
Int parseLatencyAverage(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_latencyAverage = atoi(args[1]);
}
return 2;
}
//=============================================================================
//=============================================================================
Int parseLatencyAmplitude(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_latencyAmplitude = atoi(args[1]);
}
return 2;
}
//=============================================================================
//=============================================================================
Int parseLatencyPeriod(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_latencyPeriod = atoi(args[1]);
}
return 2;
}
//=============================================================================
//=============================================================================
Int parseLatencyNoise(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_latencyNoise = atoi(args[1]);
}
return 2;
}
//=============================================================================
//=============================================================================
Int parsePacketLoss(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_packetLoss = atoi(args[1]);
}
return 2;
}
//=============================================================================
//=============================================================================
Int parseLowDetail(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_terrainLOD = TERRAIN_LOD_MIN;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoDynamicLOD(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_enableDynamicLOD = FALSE;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseNoStaticLOD(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_enableStaticLOD = FALSE;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseUseWaveEditor(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_usingWaterTrackEditor = TRUE;
}
return 1;
}
//=============================================================================
//=============================================================================
Int parseFPSLimit(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_framesPerSecondLimit = atoi(args[1]);
}
return 2;
}
//=============================================================================
Int parseNoViewLimit(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_useCameraConstraints = FALSE;
}
return 1;
}
Int parseWireframe(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_wireframe = TRUE;
}
return 1;
}
Int parseShowCollision(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_showCollisionExtents = TRUE;
}
return 1;
}
Int parseNoShowClientPhysics(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_showClientPhysics = FALSE;
}
return 1;
}
Int parseShowTerrainNormals(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_showTerrainNormals = TRUE;
}
return 1;
}
Int parseStateMachineDebug(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_stateMachineDebug = TRUE;
}
return 1;
}
Int parseJabber(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_jabberOn = TRUE;
}
return 1;
}
Int parseMunkee(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_munkeeOn = TRUE;
}
return 1;
}
#endif // defined(_DEBUG) || defined(_INTERNAL)
Int parseScriptDebug(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_scriptDebug = TRUE;
TheWritableGlobalData->m_winCursors = TRUE;
}
return 1;
}
Int parseParticleEdit(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_particleEdit = TRUE;
TheWritableGlobalData->m_winCursors = TRUE;
TheWritableGlobalData->m_windowed = TRUE;
}
return 1;
}
Int parseBuildMapCache(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_buildMapCache = true;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseDisplayDebug(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_displayDebug = TRUE;
}
return 1;
}
Int parseFile(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_initialFile = args[1];
ConvertShortMapPathToLongMapPath(TheWritableGlobalData->m_initialFile);
}
return 2;
}
Int parsePreload( char *args[], int num )
{
if( TheWritableGlobalData )
TheWritableGlobalData->m_preloadAssets = TRUE;
return 1;
}
Int parsePreloadEverything( char *args[], int num )
{
if( TheWritableGlobalData )
{
TheWritableGlobalData->m_preloadAssets = TRUE;
TheWritableGlobalData->m_preloadEverything = TRUE;
}
return 1;
}
Int parseLogAssets( char *args[], int num )
{
if( TheWritableGlobalData )
{
FILE *logfile=fopen("PreloadedAssets.txt","w");
if (logfile) //clear the file
fclose(logfile);
TheWritableGlobalData->m_preloadReport = TRUE;
}
return 1;
}
/// begin stuff for VTUNE
Int parseVTune ( char *args[], int num )
{
if( TheWritableGlobalData )
TheWritableGlobalData->m_vTune = TRUE;
return 1;
}
/// end stuff for VTUNE
#endif // defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
//=============================================================================
Int parseNoFX(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_useFX = FALSE;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseNoShroud(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_shroudOn = FALSE;
}
return 1;
}
#endif
Int parseForceBenchmark(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_forceBenchmark = TRUE;
}
return 1;
}
Int parseNoMoveCamera(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_disableCameraMovement = true;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseNoCinematic(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_disableCameraMovement = true;
TheWritableGlobalData->m_disableMilitaryCaption = true;
TheWritableGlobalData->m_disableCameraFade = true;
TheWritableGlobalData->m_disableScriptedInputDisabling = true;
}
return 1;
}
#endif
Int parseSync(char *args[], int)
{
if (TheWritableGlobalData)
{
TheDebugIgnoreSyncErrors = true;
}
return 1;
}
Int parseNoShellMap(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_shellMapOn = FALSE;
}
return 1;
}
#if !defined(_PLAYTEST) || (defined(_DEBUG) || defined(_INTERNAL))
Int parseNoLogo(char *args[], int)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_playIntro = FALSE;
TheWritableGlobalData->m_afterIntro = TRUE;
}
return 1;
}
#endif
Int parseShellMap(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_shellMapName = args[1];
}
return 2;
}
Int parseNoWindowAnimation(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_animateWindows = FALSE;
}
return 1;
}
Int parseWinCursors(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_winCursors = TRUE;
}
return 1;
}
Int parseQuickStart( char *args[], int num )
{
parseNoLogo( args, num );
parseNoShellMap( args, num );
parseNoWindowAnimation( args, num );
return 1;
}
Int parseConstantDebug( char *args[], int num )
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_constantDebugUpdate = TRUE;
}
return 1;
}
Int parseShowTeamDot( char *args[], int num )
{
if( TheWritableGlobalData )
{
TheWritableGlobalData->m_showTeamDot = TRUE;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseSelectAll( char *args[], int num )
{
if( TheWritableGlobalData )
{
TheWritableGlobalData->m_allowUnselectableSelection = TRUE;
}
return 1;
}
Int parseRunAhead( char *args[], Int num )
{
if (num > 2)
{
MIN_RUNAHEAD = atoi(args[1]);
MAX_FRAMES_AHEAD = atoi(args[2]);
FRAME_DATA_LENGTH = (MAX_FRAMES_AHEAD + 1)*2;
}
return 3;
}
#endif
Int parseSeed(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_fixedSeed = atoi(args[1]);
}
return 2;
}
Int parseIncrAGPBuf(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_incrementalAGPBuf = TRUE;
}
return 1;
}
Int parseNetMinPlayers(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_netMinPlayers = atoi(args[1]);
}
return 2;
}
Int parsePlayStats(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_playStats = atoi(args[1]);
}
return 2;
}
Int parseDemoLoadScreen(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_loadScreenDemo = TRUE;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseSaveStats(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_saveStats = TRUE;
TheWritableGlobalData->m_baseStatsDir = args[1];
}
return 2;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseSaveAllStats(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_saveStats = TRUE;
TheWritableGlobalData->m_baseStatsDir = args[1];
TheWritableGlobalData->m_saveAllStats = TRUE;
}
return 2;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseLocalMOTD(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_useLocalMOTD = TRUE;
TheWritableGlobalData->m_MOTDPath = args[1];
}
return 2;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseCameraDebug(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_debugCamera = TRUE;
}
return 1;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseBenchmark(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
TheWritableGlobalData->m_benchmarkTimer = atoi(args[1]);
TheWritableGlobalData->m_playStats = atoi(args[1]);
}
return 2;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseIgnoreAsserts(char *args[], int num)
{
if (TheWritableGlobalData && num > 0)
{
TheWritableGlobalData->m_debugIgnoreAsserts = true;
}
return 1;
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseIgnoreStackTrace(char *args[], int num)
{
if (TheWritableGlobalData && num > 0)
{
TheWritableGlobalData->m_debugIgnoreStackTrace = true;
}
return 1;
}
#endif
Int parseNoFPSLimit(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_useFpsLimit = false;
TheWritableGlobalData->m_framesPerSecondLimit = 30000;
}
return 1;
}
Int parseDumpAssetUsage(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_dumpAssetUsage = true;
}
return 1;
}
Int parseJumpToFrame(char *args[], int num)
{
if (TheWritableGlobalData && num > 1)
{
parseNoFPSLimit(args, num);
TheWritableGlobalData->m_noDraw = atoi(args[1]);
return 2;
}
return 1;
}
Int parseUpdateImages(char *args[], int num)
{
if (TheWritableGlobalData)
{
TheWritableGlobalData->m_shouldUpdateTGAToDDS = TRUE;
}
return 1;
}
Int parseMod(char *args[], Int num)
{
if (TheWritableGlobalData && num > 1)
{
AsciiString modPath = args[1];
if (strchr(modPath.str(), ':') || modPath.startsWith("/") || modPath.startsWith("\\"))
{
// full path passed in. Don't append base path.
}
else
{
modPath.format("%s%s", TheGlobalData->getPath_UserData().str(), args[1]);
}
DEBUG_LOG(("Looking for mod '%s'\n", modPath.str()));
if (!TheLocalFileSystem->doesFileExist(modPath.str()))
{
DEBUG_LOG(("Mod does not exist.\n"));
return 2; // no such file/dir.
}
// now check for dir-ness
struct stat statBuf;
if (stat(modPath.str(), &statBuf) != 0)
{
DEBUG_LOG(("Could not _stat() mod.\n"));
return 2; // could not stat the file/dir.
}
if (statBuf.st_mode & S_IFDIR)
{
if (!modPath.endsWith("\\") && !modPath.endsWith("/"))
modPath.concat('\\');
DEBUG_LOG(("Mod dir is '%s'.\n", modPath.str()));
TheWritableGlobalData->m_modDir = modPath;
}
else
{
DEBUG_LOG(("Mod file is '%s'.\n", modPath.str()));
TheWritableGlobalData->m_modBIG = modPath;
}
return 2;
}
return 1;
}
#if defined(_DEBUG) || defined(_INTERNAL)
Int parseSetDebugLevel(char *args[], int num)
{
if (num > 1)
{
AsciiString val = args[1];
for (Int i=0; i 1)
{
AsciiString val = args[1];
for (Int i=0; iloadMods();
}