Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA
2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions

View File

@@ -0,0 +1,851 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: AudioEventRTS.cpp
/*---------------------------------------------------------------------------*/
/* EA Pacific */
/* Confidential Information */
/* Copyright (C) 2001 - All Rights Reserved */
/* DO NOT DISTRIBUTE */
/*---------------------------------------------------------------------------*/
/* Project: RTS3 */
/* File name: AudioEventRTS.cpp */
/* Created: John K. McDonald, Jr., 3/21/2002 */
/* Desc: AudioEventRTS constructors and assignment operator, etc. */
/* Revision History: */
/* 3/21/2002 : Initial creation */
/*---------------------------------------------------------------------------*/
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/AudioEventRTS.h"
#include "Common/AudioEventInfo.h"
#include "Common/AudioRandomValue.h"
#include "Common/AudioSettings.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/GameSounds.h"
#include "Common/GlobalData.h"
#include "Common/Player.h"
#include "Common/Registry.h"
#include "GameLogic/GameLogic.h" // For getObjectByID
#include "GameLogic/LogicRandomValue.h"
#include "GameLogic/Object.h"
#include "GameClient/Drawable.h" // For getPosition
#include "GameClient/GameClient.h" // For getDrawableByID
#ifdef _INTERNAL
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS()
: m_eventName(AsciiString::TheEmptyString),
m_priority(AP_NORMAL),
m_volume(-1.0),
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
m_ownerType(OT_INVALID),
m_shouldFade(false),
m_isLogicalAudio(false),
m_filenameToLoad(AsciiString::TheEmptyString),
m_eventInfo(NULL),
m_playingHandle(0),
m_killThisHandle(0),
m_pitchShift(1.0),
m_volumeShift(0.0),
m_loopCount(1),
m_playingAudioIndex(-1),
m_allCount(0),
m_playerIndex(-1),
m_delay(0.0f),
m_uninterruptable(FALSE)
{
m_attackName.clear();
m_decayName.clear();
m_positionOfAudio.zero();
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS( const AsciiString& eventName )
: m_eventName(eventName),
m_priority(AP_NORMAL),
m_volume(-1.0),
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
m_ownerType(OT_INVALID),
m_shouldFade(false),
m_isLogicalAudio(false),
m_filenameToLoad(AsciiString::TheEmptyString),
m_eventInfo(NULL),
m_playingHandle(0),
m_killThisHandle(0),
m_pitchShift(1.0),
m_volumeShift(0.0),
m_loopCount(1),
m_playingAudioIndex(-1),
m_allCount(0),
m_playerIndex(-1),
m_delay(0.0f),
m_uninterruptable(FALSE)
{
m_attackName.clear();
m_decayName.clear();
m_positionOfAudio.zero();
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, ObjectID ownerID )
: m_eventName(eventName),
m_priority(AP_NORMAL),
m_volume(-1.0),
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
m_objectID(ownerID),
m_ownerType(OT_INVALID),
m_shouldFade(false),
m_isLogicalAudio(false),
m_filenameToLoad(AsciiString::TheEmptyString),
m_eventInfo(NULL),
m_playingHandle(0),
m_killThisHandle(0),
m_pitchShift(1.0),
m_volumeShift(0.0),
m_loopCount(1),
m_playingAudioIndex(-1),
m_allCount(0),
m_playerIndex(-1),
m_delay(0.0f),
m_uninterruptable(FALSE)
{
m_attackName.clear();
m_decayName.clear();
if( m_objectID )
{
m_ownerType = OT_Object;
}
else
{
m_objectID = INVALID_ID;
}
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, DrawableID drawableID )
: m_eventName(eventName),
m_priority(AP_NORMAL),
m_volume(-1.0),
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
m_drawableID(drawableID),
m_ownerType(OT_INVALID),
m_shouldFade(false),
m_isLogicalAudio(false),
m_filenameToLoad(AsciiString::TheEmptyString),
m_eventInfo(NULL),
m_playingHandle(0),
m_killThisHandle(0),
m_pitchShift(1.0),
m_volumeShift(0.0),
m_loopCount(1),
m_playingAudioIndex(-1),
m_allCount(0),
m_playerIndex(-1),
m_delay(0.0f),
m_uninterruptable(FALSE)
{
m_attackName.clear();
m_decayName.clear();
if( m_drawableID )
{
m_ownerType = OT_Drawable;
}
else
{
m_drawableID = INVALID_DRAWABLE_ID;
}
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, const Coord3D *positionOfAudio )
: m_eventName(eventName),
m_priority(AP_NORMAL),
m_volume(-1.0),
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
m_ownerType(OT_Positional),
m_shouldFade(false),
m_isLogicalAudio(false),
m_filenameToLoad(AsciiString::TheEmptyString),
m_eventInfo(NULL),
m_playingHandle(0),
m_killThisHandle(0),
m_pitchShift(1.0),
m_volumeShift(0.0),
m_loopCount(1),
m_playingAudioIndex(-1),
m_allCount(0),
m_playerIndex(-1),
m_delay(0.0f),
m_uninterruptable(FALSE)
{
m_positionOfAudio.set( positionOfAudio );
m_attackName.clear();
m_decayName.clear();
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::AudioEventRTS( const AudioEventRTS& right )
{
m_filenameToLoad = right.m_filenameToLoad;
m_eventInfo = right.m_eventInfo;
m_playingHandle = right.m_playingHandle;
m_killThisHandle = right.m_killThisHandle;
m_eventName = right.m_eventName;
m_priority = right.m_priority;
m_volume = right.m_volume;
m_timeOfDay = right.m_timeOfDay;
m_ownerType = right.m_ownerType;
m_shouldFade = right.m_shouldFade;
m_isLogicalAudio = right.m_isLogicalAudio;
m_pitchShift = right.m_pitchShift;
m_volumeShift = right.m_volumeShift;
m_loopCount = right.m_loopCount;
m_playingAudioIndex = right.m_playingAudioIndex;
m_allCount = right.m_allCount;
m_playerIndex = right.m_playerIndex;
m_delay = right.m_delay;
m_attackName = right.m_attackName;
m_decayName = right.m_decayName;
m_portionToPlayNext = right.m_portionToPlayNext;
m_uninterruptable = right.m_uninterruptable;
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
{
m_positionOfAudio.set( &right.m_positionOfAudio );
}
else if( m_ownerType == OT_Drawable )
{
m_drawableID = right.m_drawableID;
}
else if( m_ownerType == OT_Object )
{
m_objectID = right.m_objectID;
}
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS& AudioEventRTS::operator=( const AudioEventRTS& right )
{
m_filenameToLoad = right.m_filenameToLoad;
m_eventInfo = right.m_eventInfo;
m_playingHandle = right.m_playingHandle;
m_killThisHandle = right.m_killThisHandle;
m_eventName = right.m_eventName;
m_priority = right.m_priority;
m_volume = right.m_volume;
m_timeOfDay = right.m_timeOfDay;
m_ownerType = right.m_ownerType;
m_shouldFade = right.m_shouldFade;
m_isLogicalAudio = right.m_isLogicalAudio;
m_pitchShift = right.m_pitchShift;
m_volumeShift = right.m_volumeShift;
m_loopCount = right.m_loopCount;
m_playingAudioIndex = right.m_playingAudioIndex;
m_allCount = right.m_allCount;
m_playerIndex = right.m_playerIndex;
m_delay = right.m_delay;
m_attackName = right.m_attackName;
m_decayName = right.m_decayName;
m_portionToPlayNext = right.m_portionToPlayNext;
m_uninterruptable = right.m_uninterruptable;
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
{
m_positionOfAudio.set( &right.m_positionOfAudio );
}
else if( m_ownerType == OT_Drawable )
{
m_drawableID = right.m_drawableID;
}
else if( m_ownerType == OT_Object )
{
m_objectID = right.m_objectID;
}
return *this;
}
//-------------------------------------------------------------------------------------------------
AudioEventRTS::~AudioEventRTS()
{
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setEventName( AsciiString name )
{
if ((name != m_eventName) && m_eventInfo != NULL) {
// Clear out the audio event info, cause its not valid for the new event.
m_eventInfo = NULL;
}
m_eventName = name;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::generateFilename( void )
{
// A Logic Random Value is used because we may ask "How long will it take to play this sound?"
// In that case, we need the same answer across all pcs.
if (!m_eventInfo) {
return;
}
m_filenameToLoad = generateFilenamePrefix(m_eventInfo->m_soundType, false);
Int which = 0;
if (m_eventInfo->m_soundType == AT_Music || m_eventInfo->m_soundType == AT_Streaming) {
m_filenameToLoad.concat(m_eventInfo->m_filename);
adjustForLocalization(m_filenameToLoad);
return;
} else {
if (m_eventInfo->m_sounds.size() == 0) {
m_filenameToLoad = AsciiString::TheEmptyString;
return;
}
if (BitTest(m_eventInfo->m_control, AC_RANDOM))
{
if (m_isLogicalAudio)
{
which = GameLogicRandomValue(0, m_eventInfo->m_sounds.size() - 1);
}
else
{
which = GameAudioRandomValue(0, m_eventInfo->m_sounds.size() - 1);
}
if (which == m_playingAudioIndex && m_eventInfo->m_sounds.size() > 2)
which = ( which + 1 ) % m_eventInfo->m_sounds.size();
m_playingAudioIndex = which;//caching random choice to compare next call
}
else
which = (++m_playingAudioIndex) % m_eventInfo->m_sounds.size();
}
m_filenameToLoad.concat(m_eventInfo->m_sounds[which]);
m_filenameToLoad.concat(generateFilenameExtension(m_eventInfo->m_soundType));
adjustForLocalization(m_filenameToLoad);
// Note: Also generate Delay when generating a filename, cause
// we want delay to apply between every loop of a sound.
m_delay = GameAudioRandomValueReal(m_eventInfo->m_delayMin, m_eventInfo->m_delayMax);
}
//-------------------------------------------------------------------------------------------------
AsciiString AudioEventRTS::getFilename( void )
{
return m_filenameToLoad;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::generatePlayInfo( void )
{
m_pitchShift = GameAudioRandomValueReal(m_eventInfo->m_pitchShiftMin, m_eventInfo->m_pitchShiftMax);
m_volumeShift = GameAudioRandomValueReal(1.0f + m_eventInfo->m_volumeShift, 1.0f); // volume shifts are between 0 and 1
m_loopCount = m_eventInfo->m_loopCount;
m_portionToPlayNext = PP_Attack;
Int attackSize = m_eventInfo->m_attackSounds.size();
if (attackSize > 0) {
m_attackName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
// needs to be logic because it needs to be the same on all systems.
Int attackToPlay;
if (m_isLogicalAudio) {
attackToPlay = GameLogicRandomValue(0, attackSize - 1);
} else {
attackToPlay = GameAudioRandomValue(0, attackSize - 1);
}
m_attackName.concat(m_eventInfo->m_attackSounds[attackToPlay]);
m_attackName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
adjustForLocalization(m_attackName);
} else {
m_portionToPlayNext = PP_Sound;
}
Int decaySize = m_eventInfo->m_decaySounds.size();
if (decaySize > 0) {
m_decayName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
// needs to be logic because it needs to be the same on all systems.
Int decayToPlay;
if (m_isLogicalAudio) {
decayToPlay = GameLogicRandomValue(0, decaySize - 1);
} else {
decayToPlay = GameAudioRandomValue(0, decaySize - 1);
}
m_decayName.concat(m_eventInfo->m_decaySounds[decayToPlay]);
m_decayName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
adjustForLocalization(m_decayName);
}
m_isLogicalAudio = FALSE;
}
//-------------------------------------------------------------------------------------------------
Real AudioEventRTS::getPitchShift( void ) const
{
return m_pitchShift;
}
//-------------------------------------------------------------------------------------------------
Real AudioEventRTS::getVolumeShift( void ) const
{
return m_volumeShift;
}
//-------------------------------------------------------------------------------------------------
AsciiString AudioEventRTS::getAttackFilename( void ) const
{
return m_attackName;
}
//-------------------------------------------------------------------------------------------------
AsciiString AudioEventRTS::getDecayFilename( void ) const
{
return m_decayName;
}
//-------------------------------------------------------------------------------------------------
Real AudioEventRTS::getDelay( void ) const
{
return m_delay;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::decrementDelay( Real timeToDecrement )
{
m_delay -= timeToDecrement;
}
//-------------------------------------------------------------------------------------------------
PortionToPlay AudioEventRTS::getNextPlayPortion( void ) const
{
return m_portionToPlayNext;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::advanceNextPlayPortion( void )
{
switch (m_portionToPlayNext)
{
case PP_Attack:
m_portionToPlayNext = PP_Sound;
break;
case PP_Sound:
if (m_eventInfo && BitTest(m_eventInfo->m_control, AC_ALL))
{
if (m_allCount == m_eventInfo->m_sounds.size()) {
m_portionToPlayNext = PP_Decay;
}
// Advance the all count so that we move to the next sound.
++m_allCount;
}
if (!m_decayName.isEmpty()) {
m_portionToPlayNext = PP_Decay;
} else {
m_portionToPlayNext = PP_Done;
}
break;
case PP_Decay:
m_portionToPlayNext = PP_Done;
break;
}
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setNextPlayPortion( PortionToPlay ptp )
{
m_portionToPlayNext = ptp;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::decreaseLoopCount( void )
{
if (m_loopCount == 1) {
m_loopCount = -1;
} else if (m_loopCount > 1) {
--m_loopCount;
}
}
//-------------------------------------------------------------------------------------------------
Bool AudioEventRTS::hasMoreLoops( void ) const
{
return (m_loopCount >= 0);
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setAudioEventInfo( const AudioEventInfo *eventInfo ) const
{
m_eventInfo = eventInfo;
}
//-------------------------------------------------------------------------------------------------
const AudioEventInfo *AudioEventRTS::getAudioEventInfo( void ) const
{
if (m_eventInfo) {
if (m_eventInfo->m_audioName == m_eventName) {
return m_eventInfo;
} else {
m_eventInfo = NULL;
}
}
return m_eventInfo;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setPlayingHandle( AudioHandle handle )
{
m_playingHandle = handle;
}
//-------------------------------------------------------------------------------------------------
AudioHandle AudioEventRTS::getPlayingHandle( void )
{
return m_playingHandle;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setPosition( const Coord3D *pos )
{
if (!pos) {
return;
}
if (!(m_ownerType == OT_Positional || m_ownerType == OT_INVALID)) {
return;
}
m_positionOfAudio = *pos;
m_ownerType = OT_Positional;
}
//-------------------------------------------------------------------------------------------------
const Coord3D* AudioEventRTS::getPosition( void )
{
if( m_ownerType != OT_INVALID )
{
return &m_positionOfAudio;
}
return NULL;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setObjectID( ObjectID objID )
{
if (!(m_ownerType == OT_Object || m_ownerType == OT_INVALID)) {
return;
}
m_objectID = objID;
m_ownerType = OT_Object;
}
//-------------------------------------------------------------------------------------------------
ObjectID AudioEventRTS::getObjectID( void )
{
if (m_ownerType == OT_Object) {
return m_objectID;
}
return INVALID_ID;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setDrawableID( DrawableID drawID )
{
if (!(m_ownerType == OT_Drawable || m_ownerType == OT_INVALID)) {
return;
}
m_drawableID = drawID;
m_ownerType = OT_Drawable;
}
//-------------------------------------------------------------------------------------------------
DrawableID AudioEventRTS::getDrawableID( void )
{
if (m_ownerType == OT_Drawable) {
return m_drawableID;
}
return INVALID_DRAWABLE_ID;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setTimeOfDay( TimeOfDay tod )
{
m_timeOfDay = tod;
}
//-------------------------------------------------------------------------------------------------
TimeOfDay AudioEventRTS::getTimeOfDay( void ) const
{
return m_timeOfDay;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setHandleToKill( AudioHandle handleToKill )
{
m_killThisHandle = handleToKill;
}
//-------------------------------------------------------------------------------------------------
AudioHandle AudioEventRTS::getHandleToKill( void ) const
{
return m_killThisHandle;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setShouldFade( Bool shouldFade )
{
m_shouldFade = shouldFade;
}
//-------------------------------------------------------------------------------------------------
Bool AudioEventRTS::getShouldFade( void ) const
{
return m_shouldFade;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setIsLogicalAudio( Bool isLogicalAudio )
{
m_isLogicalAudio = isLogicalAudio;
}
//-------------------------------------------------------------------------------------------------
Bool AudioEventRTS::getIsLogicalAudio( void ) const
{
return m_isLogicalAudio;
}
//-------------------------------------------------------------------------------------------------
Bool AudioEventRTS::isPositionalAudio( void ) const
{
if( m_eventInfo )
{
if( !BitTest( m_eventInfo->m_type, ST_WORLD ) )
{
return FALSE;
}
}
if( m_ownerType != OT_INVALID )
{
if( m_drawableID != INVALID_DRAWABLE_ID || m_objectID != INVALID_ID || m_ownerType == OT_Positional )
{
return TRUE;
}
}
return FALSE;
}
//-------------------------------------------------------------------------------------------------
Bool AudioEventRTS::isCurrentlyPlaying( void ) const
{
return TheAudio->isCurrentlyPlaying(m_playingHandle);
}
//-------------------------------------------------------------------------------------------------
AudioPriority AudioEventRTS::getAudioPriority( void ) const
{
return m_priority;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setAudioPriority( AudioPriority newPriority )
{
m_priority = newPriority;
}
//-------------------------------------------------------------------------------------------------
Real AudioEventRTS::getVolume( void ) const
{
if (m_volume == -1.0f) {
if (m_eventInfo) {
return m_eventInfo->m_volume;
}
return 0.5;
}
return m_volume;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setVolume( Real vol )
{
m_volume = vol;
}
//-------------------------------------------------------------------------------------------------
const Coord3D *AudioEventRTS::getCurrentPosition( void )
{
if (m_ownerType == OT_Positional)
{
return &m_positionOfAudio;
}
else if (m_ownerType == OT_Object)
{
Object *obj = TheGameLogic->findObjectByID(m_objectID);
if (obj)
{
m_positionOfAudio.set( obj->getPosition() );
}
else
{
m_ownerType = OT_Dead;
}
return &m_positionOfAudio;
}
else if (m_ownerType == OT_Drawable)
{
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
if( draw )
{
m_positionOfAudio.set( draw->getPosition() );
}
else
{
m_ownerType = OT_Dead;
}
return &m_positionOfAudio;
}
else if( m_ownerType == OT_Dead )
{
return &m_positionOfAudio;
}
return NULL;
}
//-------------------------------------------------------------------------------------------------
AsciiString AudioEventRTS::generateFilenamePrefix( AudioType audioTypeToPlay, Bool localized )
{
AsciiString retStr;
retStr = TheAudio->getAudioSettings()->m_audioRoot;
retStr.concat("\\");
if (audioTypeToPlay == AT_Music) {
retStr.concat(TheAudio->getAudioSettings()->m_musicFolder);
} else if (audioTypeToPlay == AT_Streaming) {
retStr.concat(TheAudio->getAudioSettings()->m_streamingFolder);
} else {
retStr.concat(TheAudio->getAudioSettings()->m_soundsFolder);
}
retStr.concat("\\");
if (localized) {
retStr.concat(GetRegistryLanguage());
retStr.concat("\\");
}
return retStr;
}
//-------------------------------------------------------------------------------------------------
AsciiString AudioEventRTS::generateFilenameExtension( AudioType audioTypeToPlay )
{
AsciiString retStr = AsciiString::TheEmptyString;
if (audioTypeToPlay != AT_Music) {
retStr = ".";
retStr.concat(TheAudio->getAudioSettings()->m_soundsExtension);
}
return retStr;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::adjustForLocalization(AsciiString &strToAdjust)
{
const char *str = strToAdjust.reverseFind('\\');
if (!str) {
return;
}
// try the localized version first so that we're guarenteed to get it
// even if the generic data directory holds a version of the file
AsciiString localizedFilePath = generateFilenamePrefix(m_eventInfo->m_soundType, TRUE);
AsciiString filename = str;
localizedFilePath.concat(filename);
if (TheFileSystem->doesFileExist(localizedFilePath.str())) {
strToAdjust = localizedFilePath;
}
// else there was no localized version, so leave the path we received unchanged
return;
}
//-------------------------------------------------------------------------------------------------
Int AudioEventRTS::getPlayerIndex( void ) const
{
if (m_ownerType == OT_Object) {
Object *obj = TheGameLogic->findObjectByID(m_objectID);
if (obj) {
return obj->getControllingPlayer()->getPlayerIndex();
}
} else if (m_ownerType == OT_Drawable) {
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
if (draw) {
Object *obj = draw->getObject();
if (obj) {
return obj->getControllingPlayer()->getPlayerIndex();
}
}
}
return m_playerIndex;
}
//-------------------------------------------------------------------------------------------------
void AudioEventRTS::setPlayerIndex( Int playerNdx )
{
m_playerIndex = playerNdx;
}

View File

@@ -0,0 +1,34 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/AudioRequest.h"
AudioRequest::~AudioRequest()
{
}

View File

@@ -0,0 +1,233 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: DynamicAudioEventInfo.cpp /////////////////////////////////////////////////////////////////////////
// Derivation of AudioEventInfo structure, for customized sounds
// Author: Ian Barkley-Yeung, June 2003
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/DynamicAudioEventInfo.h"
#include "Common/Xfer.h"
/** Default constructor */
DynamicAudioEventInfo::DynamicAudioEventInfo()
{
}
/** Initialize AudioEventInfo portion of DynamicAudioEventInfo as copy; leave remainder uninitialized */
DynamicAudioEventInfo::DynamicAudioEventInfo( const AudioEventInfo & baseInfo )
: AudioEventInfo( baseInfo )
{
}
DynamicAudioEventInfo::~DynamicAudioEventInfo()
{
}
/** Override; dynamic audio events are used only for level-specific stuff at the moment */
Bool DynamicAudioEventInfo::isLevelSpecific() const
{
return true;
}
/** Override; cheap dynamic casting */
DynamicAudioEventInfo * DynamicAudioEventInfo::getDynamicAudioEventInfo()
{
return this;
}
/** Override; cheap dynamic casting */
const DynamicAudioEventInfo * DynamicAudioEventInfo::getDynamicAudioEventInfo() const
{
return this;
}
/** Override; change the name of this audio event*/
void DynamicAudioEventInfo::overrideAudioName( const AsciiString & newName )
{
// Record new name. Needed later for load & save
m_originalName = m_audioName;
m_overriddenFields.set( OVERRIDE_NAME );
m_audioName = newName;
}
/** Override; change the looping property of this audio event */
void DynamicAudioEventInfo::overrideLoopFlag( Bool newLoopFlag )
{
m_overriddenFields.set( OVERRIDE_LOOP_FLAG );
if ( newLoopFlag)
BitSet( m_control, AC_LOOP );
else
BitClear( m_control, AC_LOOP );
}
/** Override; change the looping properties of this audio event*/
void DynamicAudioEventInfo::overrideLoopCount( Int newLoopCount )
{
m_overriddenFields.set( OVERRIDE_LOOP_COUNT );
m_loopCount = newLoopCount;
}
/** Override; change the volume of this audio event*/
void DynamicAudioEventInfo::overrideVolume( Real newVolume )
{
m_overriddenFields.set( OVERRIDE_VOLUME );
m_volume = newVolume;
}
/** Override; change the minimum volume of this audio event*/
void DynamicAudioEventInfo::overrideMinVolume( Real newMinVolume )
{
m_overriddenFields.set( OVERRIDE_MIN_VOLUME );
m_minVolume = newMinVolume;
}
/** Override; change the name of this audio event*/
void DynamicAudioEventInfo::overrideMinRange( Real newMinRange )
{
m_overriddenFields.set( OVERRIDE_MIN_RANGE );
m_minDistance = newMinRange;
}
/** Override; change the name of this audio event*/
void DynamicAudioEventInfo::overrideMaxRange( Real newMaxRange )
{
m_overriddenFields.set( OVERRIDE_MAX_RANGE );
m_maxDistance = newMaxRange;
}
/** Override; change the name of this audio event*/
void DynamicAudioEventInfo::overridePriority( AudioPriority newPriority )
{
m_overriddenFields.set( OVERRIDE_PRIORITY );
m_priority = newPriority;
}
/** Get the name of the INI entry this event info was derived from */
const AsciiString & DynamicAudioEventInfo::getOriginalName() const
{
if ( wasAudioNameOverriden() )
{
return m_originalName;
}
else
{
return m_audioName;
}
}
/** Transfer all overridden fields except the customized name */
void DynamicAudioEventInfo::xferNoName( Xfer * xfer )
{
const XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// The default BitFlags xfer function is really strange. It also requires us to name our
// bits, which I don't want to do, since it forces anyone who has the same number of bits
// as us to use our names. Instead, just transfer directly and count on our local version
// to keep things straight, If you change the list, be sure to change this code too...
if ( xfer->getXferMode() == XFER_LOAD )
{
UnsignedByte overriddenFlags;
DEBUG_ASSERTCRASH( OVERRIDE_COUNT <= sizeof( overriddenFlags ) * 8, ("Save/load code assumes override flags can fit into UnsignedByte, but it doesn't work anymore! Move up to larger integer type") );
xfer->xferUnsignedByte( &overriddenFlags );
Int field;
for ( field = 0; field < OVERRIDE_COUNT; field++ )
{
m_overriddenFields.set( field, BitTest( overriddenFlags, 1 << field ) );
}
}
else
{
UnsignedByte overriddenFlags = 0;
DEBUG_ASSERTCRASH( OVERRIDE_COUNT <= sizeof( overriddenFlags ) * 8, ("Save/load code assumes override flags can fit into UnsignedByte, but it doesn't work anymore! Move up to larger integer type") );
Int field;
for ( field = 0; field < OVERRIDE_COUNT; field++ )
{
if ( m_overriddenFields.test( field ) )
{
BitSet( overriddenFlags, 1 << field );
}
}
xfer->xferUnsignedByte( &overriddenFlags );
}
if ( wasLoopFlagOverriden() )
{
Bool loopFlag = BitTest( m_control, AC_LOOP );
xfer->xferBool( &loopFlag );
if ( loopFlag )
{
BitSet( m_control, AC_LOOP );
}
else
{
BitClear( m_control, AC_LOOP );
}
}
if ( wasLoopCountOverriden() )
{
xfer->xferInt( &m_loopCount );
}
if ( wasVolumeOverriden() )
{
xfer->xferReal( &m_volume );
}
if ( wasMinVolumeOverriden() )
{
xfer->xferReal( &m_minVolume );
}
if ( wasMinRangeOverriden() )
{
xfer->xferReal( &m_minDistance );
}
if ( wasMaxRangeOverriden() )
{
xfer->xferReal( &m_maxDistance );
}
if ( wasPriorityOverriden() )
{
UnsignedByte priority = (UnsignedByte)m_priority;
xfer->xferUnsignedByte( &priority );
m_priority = (AudioPriority)priority;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: GameMusic.cpp
//
// Created: 5/01/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameMusic.h"
#include "Common/AudioEventRTS.h"
#include "Common/AudioRequest.h"
#include "Common/GameAudio.h"
#include "Common/INI.h"
#ifdef _INTERNAL
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
#define MUSIC_PATH "Data\\Audio\\Tracks" // directory path to the music files
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/** The INI data fields for music tracks */
//-------------------------------------------------------------------------------------------------
const FieldParse MusicTrack::m_musicTrackFieldParseTable[] =
{
{ "Filename", INI::parseAsciiString, NULL, offsetof( MusicTrack, filename ) },
{ "Volume", INI::parsePercentToReal, NULL, offsetof( MusicTrack, volume ) },
{ "Ambient", INI::parseBool, NULL, offsetof( MusicTrack, ambient ) },
{ NULL, NULL, NULL, 0 },
};
//-------------------------------------------------------------------------------------------------
MusicManager::MusicManager()
{
}
//-------------------------------------------------------------------------------------------------
MusicManager::~MusicManager()
{
}
//-------------------------------------------------------------------------------------------------
void MusicManager::playTrack( AudioEventRTS *eventToUse )
{
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
audioRequest->m_pendingEvent = eventToUse;
audioRequest->m_request = AR_Play;
TheAudio->appendAudioRequest( audioRequest );
}
//-------------------------------------------------------------------------------------------------
void MusicManager::stopTrack( AudioHandle eventToRemove )
{
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( false );
audioRequest->m_handleToInteractOn = eventToRemove;
audioRequest->m_request = AR_Stop;
TheAudio->appendAudioRequest( audioRequest );
}
//-------------------------------------------------------------------------------------------------
void MusicManager::addAudioEvent( AudioEventRTS *eventToAdd )
{
playTrack( eventToAdd );
}
//-------------------------------------------------------------------------------------------------
void MusicManager::removeAudioEvent( AudioHandle eventToRemove )
{
stopTrack( eventToRemove );
}

View File

@@ -0,0 +1,333 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: GameSounds.cpp
//
// Created: 5/02/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Lib/Basetype.h"
#include "Common/GameSounds.h"
#include "Common/AudioEventInfo.h"
#include "Common/AudioEventRTS.h"
#include "Common/AudioRequest.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "GameLogic/PartitionManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef _INTERNAL
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
SoundManager::SoundManager()
{
// nada to do
}
//-------------------------------------------------------------------------------------------------
SoundManager::~SoundManager()
{
// nada to do
}
//-------------------------------------------------------------------------------------------------
void SoundManager::init( void )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::postProcessLoad()
{
// The AudioManager should actually be live now, so go ahead and get the info we need from it
// here
}
//-------------------------------------------------------------------------------------------------
void SoundManager::update( void )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::reset( void )
{
m_numPlaying2DSamples = 0;
m_numPlaying3DSamples = 0;
}
//-------------------------------------------------------------------------------------------------
void SoundManager::loseFocus( void )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::regainFocus( void )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::setListenerPosition( const Coord3D *position )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::setViewRadius( Real viewRadius )
{
}
//-------------------------------------------------------------------------------------------------
void SoundManager::setCameraAudibleDistance( Real audibleDistance )
{
}
//-------------------------------------------------------------------------------------------------
Real SoundManager::getCameraAudibleDistance( void )
{
return 1.0f;
}
//-------------------------------------------------------------------------------------------------
void SoundManager::addAudioEvent(AudioEventRTS *eventToAdd)
{
if (m_num2DSamples == 0 && m_num3DSamples == 0) {
m_num2DSamples = TheAudio->getNum2DSamples();
m_num3DSamples = TheAudio->getNum3DSamples();
}
if (canPlayNow(eventToAdd)) {
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG((" - appended to request list with handle '%d'.\n", (UnsignedInt) eventToAdd->getPlayingHandle()));
#endif
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
audioRequest->m_pendingEvent = eventToAdd;
audioRequest->m_request = AR_Play;
TheAudio->appendAudioRequest(audioRequest);
} else {
TheAudio->releaseAudioEventRTS(eventToAdd);
}
}
//-------------------------------------------------------------------------------------------------
void SoundManager::notifyOf2DSampleStart( void )
{
++m_numPlaying2DSamples;
}
//-------------------------------------------------------------------------------------------------
void SoundManager::notifyOf3DSampleStart( void )
{
++m_numPlaying3DSamples;
}
//-------------------------------------------------------------------------------------------------
void SoundManager::notifyOf2DSampleCompletion( void )
{
if (m_numPlaying2DSamples > 0) {
--m_numPlaying2DSamples;
}
}
//-------------------------------------------------------------------------------------------------
void SoundManager::notifyOf3DSampleCompletion( void )
{
if (m_numPlaying3DSamples > 0) {
--m_numPlaying3DSamples;
}
}
//-------------------------------------------------------------------------------------------------
Int SoundManager::getAvailableSamples( void )
{
return (m_num2DSamples - m_numPlaying2DSamples);
}
//-------------------------------------------------------------------------------------------------
Int SoundManager::getAvailable3DSamples( void )
{
return (m_num3DSamples - m_numPlaying3DSamples);
}
//-------------------------------------------------------------------------------------------------
AsciiString SoundManager::getFilenameForPlayFromAudioEvent( const AudioEventRTS *eventToGetFrom )
{
return AsciiString::TheEmptyString;
}
//-------------------------------------------------------------------------------------------------
Bool SoundManager::canPlayNow( AudioEventRTS *event )
{
Bool retVal = false;
// 1) Are we muted because we're beyond our maximum distance?
// 2) Are we shrouded and this is a shroud sound?
// 3) Are we violating our voice count or are we playing above the limit? (If so, stop now)
// 4) is there an avaiable channel open?
// 5) if not, then determine if there is anything of lower priority that we can kill
// 6) if not, are we an interrupt-sound type?
// if so, are there any sounds of our type playing right now that we can interrupt?
// potentially here: Are there any sounds that are playing that are now beyond their distance?
// if so, kill them and start our sound
// if not, we're done. Can't play dude.
if( event->isPositionalAudio() && !BitTest( event->getAudioEventInfo()->m_type, ST_GLOBAL) && event->getAudioEventInfo()->m_priority != AP_CRITICAL )
{
Coord3D distance = *TheAudio->getListenerPosition();
const Coord3D *pos = event->getCurrentPosition();
if (pos)
{
distance.sub(pos);
if (distance.length() >= event->getAudioEventInfo()->m_maxDistance)
{
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- culled due to distance (%.2f).\n", distance.length()));
#endif
return false;
}
Int localPlayerNdx = ThePlayerList->getLocalPlayer()->getPlayerIndex();
if( (event->getAudioEventInfo()->m_type & ST_SHROUDED) &&
ThePartitionManager->getShroudStatusForPlayer(localPlayerNdx, pos) != CELLSHROUD_CLEAR )
{
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- culled due to shroud.\n"));
#endif
return false;
}
}
}
if (violatesVoice(event))
{
retVal = isInterrupting(event);
if (retVal)
{
return true;
}
else
{
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- culled due to voice.\n"));
#endif
return false;
}
}
if( TheAudio->doesViolateLimit( event ) )
{
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- culled due to limit.\n" ));
#endif
return false;
}
else if( isInterrupting( event ) )
{
return true;
}
if (event->isPositionalAudio())
{
if (m_numPlaying3DSamples < m_num3DSamples)
{
return true;
}
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- %d samples playing, %d samples available", m_numPlaying3DSamples, m_num3DSamples));
#endif
}
else
{
// its a UI sound (and thus, 2-D)
if (m_numPlaying2DSamples < m_num2DSamples)
{
return true;
}
}
if (TheAudio->isPlayingLowerPriority(event))
{
return true;
}
if (isInterrupting(event))
{
retVal = TheAudio->isPlayingAlready(event);
if (retVal)
{
return true;
}
else
{
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("- culled due to no channels available and non-interrupting.\n" ));
#endif
return false;
}
}
#ifdef INTENSIVE_AUDIO_DEBUG
DEBUG_LOG(("culled due to unavailable channels"));
#endif
return false;
}
//-------------------------------------------------------------------------------------------------
Bool SoundManager::violatesVoice( AudioEventRTS *event )
{
if (event->getAudioEventInfo()->m_type & ST_VOICE) {
return (event->getObjectID() && TheAudio->isObjectPlayingVoice(event->getObjectID()));
}
return false;
}
//-------------------------------------------------------------------------------------------------
Bool SoundManager::isInterrupting( AudioEventRTS *event )
{
return event->getAudioEventInfo()->m_control & AC_INTERRUPT;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,708 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
#include "Common/SimplePlayer.h"
#include "Common/URLLaunch.h"
///////////////////////////////////////////////////////////////////////////////
CSimplePlayer::CSimplePlayer( HRESULT* phr )
{
m_cRef = 1;
m_cBuffersOutstanding = 0;
m_pReader = NULL;
m_pHeader = NULL;
m_hwo = NULL;
m_fEof = FALSE;
m_pszUrl = NULL;
*phr = S_OK;
m_hOpenEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_OPEN_EVENT );
if ( NULL == m_hOpenEvent )
{
*phr = E_OUTOFMEMORY;
}
m_hCloseEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_CLOSE_EVENT );
if ( NULL == m_hCloseEvent )
{
*phr = E_OUTOFMEMORY;
}
m_hrOpen = S_OK;
m_hCompletionEvent = NULL;
InitializeCriticalSection( &m_CriSec );
m_whdrHead = NULL;
}
///////////////////////////////////////////////////////////////////////////////
CSimplePlayer::~CSimplePlayer()
{
DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
Close();
//
// final remove of everything in the wave header list
//
RemoveWaveHeaders();
DeleteCriticalSection( &m_CriSec );
if( m_pHeader != NULL )
{
m_pHeader->Release();
m_pHeader = NULL;
}
if( m_pReader != NULL )
{
m_pReader->Release();
m_pReader = NULL;
}
if( m_hwo != NULL )
{
waveOutClose( m_hwo );
}
delete [] m_pszUrl;
if ( m_hOpenEvent )
{
CloseHandle( m_hOpenEvent );
}
if ( m_hCloseEvent )
{
CloseHandle( m_hCloseEvent );
}
}
///////////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
REFIID riid,
void **ppvObject )
{
return( E_NOINTERFACE );
}
///////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
{
return( InterlockedIncrement( &m_cRef ) );
}
///////////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
{
ULONG uRet = InterlockedDecrement( &m_cRef );
if( 0 == uRet )
{
delete this;
}
return( uRet );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
/* [in] */ DWORD dwOutputNum,
/* [in] */ QWORD cnsSampleTime,
/* [in] */ QWORD cnsSampleDuration,
/* [in] */ DWORD dwFlags,
/* [in] */ INSSBuffer __RPC_FAR *pSample,
/* [in] */ VOID *pvContext )
{
if( 0 != dwOutputNum )
{
return( S_OK );
}
HRESULT hr = S_OK;
BYTE *pData;
DWORD cbData;
//
// first UnprepareHeader and remove everthing in the ready list
//
RemoveWaveHeaders( );
hr = pSample->GetBufferAndLength( &pData, &cbData );
if ( FAILED( hr ) )
{
return( E_UNEXPECTED );
}
DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
if( NULL == pwh )
{
DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
*m_phrCompletion = E_OUTOFMEMORY;
SetEvent( m_hCompletionEvent );
return( E_UNEXPECTED );
}
pwh->lpData = (LPSTR)&pwh[1];
pwh->dwBufferLength = cbData;
pwh->dwBytesRecorded = cbData;
pwh->dwUser = 0;
pwh->dwLoops = 0;
pwh->dwFlags = 0;
CopyMemory( pwh->lpData, pData, cbData );
MMRESULT mmr;
mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
mmr = MMSYSERR_NOERROR;
if( mmr != MMSYSERR_NOERROR )
{
DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
*m_phrCompletion = E_UNEXPECTED;
SetEvent( m_hCompletionEvent );
return( E_UNEXPECTED );
}
mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
mmr = MMSYSERR_NOERROR;
if( mmr != MMSYSERR_NOERROR )
{
delete pwh;
DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
*m_phrCompletion = E_UNEXPECTED;
SetEvent( m_hCompletionEvent );
return( E_UNEXPECTED );
}
InterlockedIncrement( &m_cBuffersOutstanding );
return( S_OK );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
{
HRESULT hr;
//
// If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
// a relative local file name that needs to be expanded to a full path.
//
WCHAR wszFullUrl[ MAX_PATH ];
if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
&& ( 0 == wcsstr( pszUrl, L":\\" ) )
&& ( 0 == wcsstr( pszUrl, L"://" ) ) )
{
//
// Expand to a full path name
//
LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
if( NULL == pszCheck )
{
DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
return E_UNEXPECTED ;
}
pszUrl = wszFullUrl;
}
//
// Save a copy of the URL
//
delete[] m_pszUrl;
m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
if( NULL == m_pszUrl )
{
DEBUG_LOG(( "insufficient Memory\n" )) ;
return( E_OUTOFMEMORY );
}
wcscpy( m_pszUrl, pszUrl );
//
// Attempt to open the URL
//
m_hCompletionEvent = hCompletionEvent;
m_phrCompletion = phrCompletion;
#ifdef SUPPORT_DRM
hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
#else
hr = WMCreateReader( NULL, 0, &m_pReader );
#endif
if( FAILED( hr ) )
{
DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
return( hr );
}
//
// Open the file
//
hr = m_pReader->Open( m_pszUrl, this, NULL );
if ( SUCCEEDED( hr ) )
{
WaitForSingleObject( m_hOpenEvent, INFINITE );
hr = m_hrOpen;
}
if ( NS_E_NO_STREAM == hr )
{
DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
WaitForSingleObject( m_hOpenEvent, INFINITE );
hr = m_hrOpen;
}
if ( FAILED( hr ) )
{
DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
return( hr );
}
//
// It worked! Display various attributes
//
hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
return( hr );
}
WORD i, wAttrCnt;
hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
return( hr );
}
WCHAR *pwszName = NULL;
BYTE *pValue = NULL;
for ( i = 0; i < wAttrCnt ; i++ )
{
WORD wStream = 0;
WORD cchNamelen = 0;
WMT_ATTR_DATATYPE type;
WORD cbLength = 0;
hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
break;
}
pwszName = new WCHAR[ cchNamelen ];
pValue = new BYTE[ cbLength ];
if( NULL == pwszName || NULL == pValue )
{
hr = E_OUTOFMEMORY;
break;
}
hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
break;
}
switch ( type )
{
case WMT_TYPE_DWORD:
DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
break;
case WMT_TYPE_STRING:
DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
break;
case WMT_TYPE_BINARY:
DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
break;
case WMT_TYPE_BOOL:
DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
break;
case WMT_TYPE_WORD:
DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
break;
case WMT_TYPE_QWORD:
DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
break;
case WMT_TYPE_GUID:
DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
break;
default:
DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
break;
}
delete pwszName;
pwszName = NULL;
delete pValue;
pValue = NULL;
}
delete pwszName;
pwszName = NULL;
delete pValue;
pValue = NULL;
if ( FAILED( hr ) )
{
return( hr );
}
//
// Make sure we're audio only
//
DWORD cOutputs;
hr = m_pReader->GetOutputCount( &cOutputs );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
return( hr );
}
if ( cOutputs != 1 )
{
DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
// return( E_UNEXPECTED );
}
IWMOutputMediaProps *pProps;
hr = m_pReader->GetOutputProps( 0, &pProps );
if ( FAILED( hr ) )
{
DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
return( hr );
}
DWORD cbBuffer = 0;
hr = pProps->GetMediaType( NULL, &cbBuffer );
if ( FAILED( hr ) )
{
pProps->Release( );
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
return( hr );
}
WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
hr = pProps->GetMediaType( pMediaType, &cbBuffer );
if ( FAILED( hr ) )
{
pProps->Release( );
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
return( hr );
}
pProps->Release( );
if ( pMediaType->majortype != WMMEDIATYPE_Audio )
{
delete[] (BYTE *) pMediaType ;
DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
return( E_UNEXPECTED );
}
//
// Set up for audio playback
//
WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
delete[] (BYTE *)pMediaType ;
pMediaType = NULL ;
MMRESULT mmr;
mmr = waveOutOpen( &m_hwo,
WAVE_MAPPER,
&m_wfx,
(DWORD)WaveProc,
(DWORD)this,
CALLBACK_FUNCTION );
mmr = MMSYSERR_NOERROR;
if( mmr != MMSYSERR_NOERROR )
{
DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
return( E_UNEXPECTED );
}
//
// Start reading the data (and rendering the audio)
//
QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
if( FAILED( hr ) )
{
DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
return( hr );
}
return( hr );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
/* [in] */ WMT_STATUS Status,
/* [in] */ HRESULT hr,
/* [in] */ WMT_ATTR_DATATYPE dwType,
/* [in] */ BYTE __RPC_FAR *pValue,
/* [in] */ void __RPC_FAR *pvContext)
{
switch( Status )
{
case WMT_OPENED:
DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
m_hrOpen = hr;
SetEvent( m_hOpenEvent );
break;
case WMT_SOURCE_SWITCH:
DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
m_hrOpen = hr;
SetEvent( m_hOpenEvent );
break;
case WMT_ERROR:
DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
break;
case WMT_STARTED:
DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
break;
case WMT_STOPPED:
DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
break;
case WMT_BUFFERING_START:
DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
break;
case WMT_BUFFERING_STOP:
DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
break;
case WMT_EOF:
DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
//
// cleanup and exit
//
m_fEof = TRUE;
if( 0 == m_cBuffersOutstanding )
{
SetEvent( m_hCompletionEvent );
}
break;
case WMT_END_OF_SEGMENT:
DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
//
// cleanup and exit
//
m_fEof = TRUE;
if( 0 == m_cBuffersOutstanding )
{
SetEvent( m_hCompletionEvent );
}
break;
case WMT_LOCATING:
DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
break;
case WMT_CONNECTING:
DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
break;
case WMT_NO_RIGHTS:
{
LPWSTR pwszEscapedURL = NULL;
hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
if( SUCCEEDED( hr ) )
{
WCHAR wszURL[ 0x1000 ];
swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
hr = LaunchURL( wszURL );
if( FAILED( hr ) )
{
DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
}
delete [] pwszEscapedURL;
pwszEscapedURL = NULL ;
}
}
break;
case WMT_MISSING_CODEC:
{
DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
break;
}
case WMT_CLOSED:
SetEvent( m_hCloseEvent );
break;
};
return( S_OK );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimplePlayer::Close()
{
HRESULT hr = S_OK;
if( NULL != m_pReader )
{
hr = m_pReader->Close();
if( SUCCEEDED( hr ) )
{
WaitForSingleObject( m_hCloseEvent, INFINITE );
}
}
return( hr );
}
///////////////////////////////////////////////////////////////////////////////
void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
{
if( WOM_DONE == uMsg )
{
//
// add the wave header to ready-to-free list for the caller
// to pick up and free in the next OnSample call
//
AddWaveHeader( ( LPWAVEHDR )dwParam1 );
InterlockedDecrement( &m_cBuffersOutstanding );
if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
{
SetEvent( m_hCompletionEvent );
}
}
}
///////////////////////////////////////////////////////////////////////////////
void CALLBACK CSimplePlayer::WaveProc(
HWAVEOUT hwo,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2 )
{
CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
}
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
{
WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
if( NULL == tmp )
{
return( E_OUTOFMEMORY );
}
tmp->pwh = pwh;
EnterCriticalSection( &m_CriSec );
tmp->next = m_whdrHead;
m_whdrHead = tmp;
LeaveCriticalSection( &m_CriSec );
return( S_OK );
}
//////////////////////////////////////////////////////////////////////////////
void CSimplePlayer::RemoveWaveHeaders( )
{
WAVEHDR_LIST *tmp;
EnterCriticalSection( &m_CriSec );
while( NULL != m_whdrHead )
{
tmp = m_whdrHead->next;
DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
delete m_whdrHead->pwh;
delete m_whdrHead;
m_whdrHead = tmp;
}
LeaveCriticalSection( &m_CriSec );
}

View File

@@ -0,0 +1,338 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
#include "Common/URLLaunch.h"
#define FILE_PREFIX L"file://"
///////////////////////////////////////////////////////////////////////////////
HRESULT MakeEscapedURL( LPWSTR pszInURL, LPWSTR *ppszOutURL )
{
if( ( NULL == pszInURL ) || ( NULL == ppszOutURL ) )
{
return( E_INVALIDARG );
}
//
// Do we need to pre-pend file://?
//
BOOL fNeedFilePrefix = ( 0 == wcsstr( pszInURL, L"://" ) );
//
// Count how many characters need to be escaped
//
LPWSTR pszTemp = pszInURL;
DWORD cEscapees = 0;
while( TRUE )
{
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
if( NULL == pchToEscape )
{
break;
}
cEscapees++;
pszTemp = pchToEscape + 1;
}
//
// Allocate sufficient outgoing buffer space
//
int cchNeeded = wcslen( pszInURL ) + ( 2 * cEscapees ) + 1;
if( fNeedFilePrefix )
{
cchNeeded += wcslen( FILE_PREFIX );
}
*ppszOutURL = new WCHAR[ cchNeeded ];
if( NULL == *ppszOutURL )
{
return( E_OUTOFMEMORY );
}
//
// Fill in the outgoing escaped buffer
//
pszTemp = pszInURL;
LPWSTR pchNext = *ppszOutURL;
if( fNeedFilePrefix )
{
wcscpy( *ppszOutURL, FILE_PREFIX );
pchNext += wcslen( FILE_PREFIX );
}
while( TRUE )
{
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
if( NULL == pchToEscape )
{
//
// Copy the rest of the input string and get out
//
wcscpy( pchNext, pszTemp );
break;
}
//
// Copy all characters since the previous escapee
//
int cchToCopy = pchToEscape - pszTemp;
if( cchToCopy > 0 )
{
wcsncpy( pchNext, pszTemp, cchToCopy );
pchNext += cchToCopy;
}
//
// Expand this character into an escape code and move on
//
pchNext += swprintf( pchNext, L"%%%02x", *pchToEscape );
pszTemp = pchToEscape + 1;
}
return( S_OK );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT GetShellOpenCommand( LPTSTR ptszShellOpenCommand, DWORD cbShellOpenCommand )
{
LONG lResult;
HKEY hKey = NULL;
HKEY hFileKey = NULL;
BOOL fFoundExtensionCommand = FALSE;
do
{
//
// Look for the file type associated with .html files
//
TCHAR szFileType[ MAX_PATH ];
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( ".html" ), 0, KEY_READ, &hKey );
if( ERROR_SUCCESS != lResult )
{
break;
}
DWORD dwLength = sizeof( szFileType );
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)szFileType, &dwLength );
if( ERROR_SUCCESS != lResult )
{
break;
}
//
// Find the command for the shell's open verb associated with this file type
//
TCHAR szKeyName[ MAX_PATH + 20 ];
wsprintf( szKeyName, _T( "%s\\shell\\open\\command" ), szFileType );
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, szKeyName, 0, KEY_READ, &hFileKey );
if( ERROR_SUCCESS != lResult )
{
break;
}
dwLength = cbShellOpenCommand;
lResult = RegQueryValueEx( hFileKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
if( 0 == lResult )
{
fFoundExtensionCommand = TRUE;
}
}
while( FALSE );
//
// If there was no application associated with .html files by extension, look for
// an application associated with the http protocol
//
if( !fFoundExtensionCommand )
{
if( NULL != hKey )
{
RegCloseKey( hKey );
}
do
{
//
// Find the command for the shell's open verb associated with the http protocol
//
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( "http\\shell\\open\\command" ), 0, KEY_READ, &hKey );
if( ERROR_SUCCESS != lResult )
{
break;
}
DWORD dwLength = cbShellOpenCommand;
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
}
while( FALSE );
}
if( NULL != hKey )
{
RegCloseKey( hKey );
}
if( NULL != hFileKey )
{
RegCloseKey( hFileKey );
}
return( HRESULT_FROM_WIN32( lResult ) );
}
///////////////////////////////////////////////////////////////////////////////
HRESULT LaunchURL( LPCWSTR pszURL )
{
HRESULT hr;
//
// Find the appropriate command to launch URLs with
//
TCHAR szShellOpenCommand[ MAX_PATH * 2 ];
hr = GetShellOpenCommand( szShellOpenCommand, sizeof( szShellOpenCommand ) );
if( FAILED( hr ) )
{
return( hr );
}
//
// Build the appropriate command line, substituting our URL parameter
//
TCHAR szLaunchCommand[ 2000 ];
LPTSTR pszParam = _tcsstr( szShellOpenCommand, _T( "\"%1\"" ) );
if( NULL == pszParam )
{
pszParam = _tcsstr( szShellOpenCommand, _T( "\"%*\"" ) );
}
if( NULL != pszParam )
{
*pszParam = _T( '\0' ) ;
wsprintf( szLaunchCommand, _T( "%s%ws%s" ), szShellOpenCommand, pszURL, pszParam + 4 );
}
else
{
wsprintf( szLaunchCommand, _T( "%s %ws" ), szShellOpenCommand, pszURL );
}
//
// Find the application name, stripping quotes if necessary
//
TCHAR szExe[ MAX_PATH * 2 ];
LPTSTR pchFirst = szShellOpenCommand;
LPTSTR pchNext = NULL;
while( _T( ' ' ) == *pchFirst )
{
pchFirst++;
}
if( _T( '"' ) == *pchFirst )
{
pchFirst++;
pchNext = _tcschr( pchFirst, _T( '"' ) );
}
else
{
pchNext = _tcschr( pchFirst + 1, _T( ' ' ) );
}
if( NULL == pchNext )
{
pchNext = szShellOpenCommand + _tcslen( szShellOpenCommand );
}
_tcsncpy( szExe, pchFirst, pchNext - pchFirst );
szExe[ pchNext - pchFirst ] = _T( '\0' ) ;
//
// Because of the extremely long length of the URLs, neither
// WinExec, nor ShellExecute, were working correctly. For this reason
// we use CreateProcess. The CreateProcess documentation in MSDN says
// that the most robust way to call CreateProcess is to pass the full
// command line, where the first element is the application name, in the
// lpCommandLine parameter. In our case this is necesssary to get Netscape
// to function properly.
//
PROCESS_INFORMATION ProcInfo;
ZeroMemory( (LPVOID)&ProcInfo, sizeof( PROCESS_INFORMATION ) );
STARTUPINFO StartUp;
ZeroMemory( (LPVOID)&StartUp, sizeof( STARTUPINFO ) );
StartUp.cb = sizeof(STARTUPINFO);
if( !CreateProcess( szExe, szLaunchCommand, NULL, NULL,
FALSE, 0, NULL, NULL, &StartUp, &ProcInfo) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
//
// CreateProcess succeeded and we do not need the handles to the thread
// or the process, so close them now.
//
if( NULL != ProcInfo.hThread )
{
CloseHandle( ProcInfo.hThread );
}
if( NULL != ProcInfo.hProcess )
{
CloseHandle( ProcInfo.hProcess );
}
}
return( hr );
}

View File

@@ -0,0 +1,124 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/BezFwdIterator.h"
//-------------------------------------------------------------------------------------------------
BezFwdIterator::BezFwdIterator(): mStep(0), mStepsDesired(0)
{
// Added by Sadullah Nader
mCurrPoint.zero();
mDDDq.zero();
mDDq.zero();
mDq.zero();
}
//-------------------------------------------------------------------------------------------------
BezFwdIterator::BezFwdIterator(Int stepsDesired, const BezierSegment *bezSeg)
{
// Added by Sadullah Nader
mCurrPoint.zero();
mDDDq.zero();
mDDq.zero();
mDq.zero();
//
mStepsDesired = stepsDesired;
mBezSeg = (*bezSeg);
}
//-------------------------------------------------------------------------------------------------
void BezFwdIterator::start(void)
{
mStep = 0;
if (mStepsDesired <= 1)
return;
float d = 1.0f / (mStepsDesired - 1);
float d2 = d * d;
float d3 = d * d2;
D3DXVECTOR4 px(mBezSeg.m_controlPoints[0].x, mBezSeg.m_controlPoints[1].x, mBezSeg.m_controlPoints[2].x, mBezSeg.m_controlPoints[3].x);
D3DXVECTOR4 py(mBezSeg.m_controlPoints[0].y, mBezSeg.m_controlPoints[1].y, mBezSeg.m_controlPoints[2].y, mBezSeg.m_controlPoints[3].y);
D3DXVECTOR4 pz(mBezSeg.m_controlPoints[0].z, mBezSeg.m_controlPoints[1].z, mBezSeg.m_controlPoints[2].z, mBezSeg.m_controlPoints[3].z);
D3DXVECTOR4 cVec[3];
D3DXVec4Transform(&cVec[0], &px, &BezierSegment::s_bezBasisMatrix);
D3DXVec4Transform(&cVec[1], &py, &BezierSegment::s_bezBasisMatrix);
D3DXVec4Transform(&cVec[2], &pz, &BezierSegment::s_bezBasisMatrix);
mCurrPoint = mBezSeg.m_controlPoints[0];
int i = 3;
while (i--) {
float a = cVec[i].x;
float b = cVec[i].y;
float c = cVec[i].z;
float *pD, *pDD, *pDDD;
if (i == 2) {
pD = &mDq.z;
pDD = &mDDq.z;
pDDD = &mDDDq.z;
} else if (i == 1) {
pD = &mDq.y;
pDD = &mDDq.y;
pDDD = &mDDDq.y;
} else if (i == 0) {
pD = &mDq.x;
pDD = &mDDq.x;
pDDD = &mDDDq.x;
}
(*pD) = a * d3 + b * d2 + c * d;
(*pDD) = 6 * a * d3 + 2 * b * d2;
(*pDDD) = 6 * a * d3;
}
}
//-------------------------------------------------------------------------------------------------
Bool BezFwdIterator::done(void)
{
return (mStep >= mStepsDesired);
}
//-------------------------------------------------------------------------------------------------
const Coord3D& BezFwdIterator::getCurrent(void) const
{
return mCurrPoint;
}
//-------------------------------------------------------------------------------------------------
void BezFwdIterator::next(void)
{
mCurrPoint.add(&mDq);
mDq.add(&mDDq);
mDDq.add(&mDDDq);
++mStep;
}

View File

@@ -0,0 +1,246 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/BezierSegment.h"
#include "Common/BezFwdIterator.h"
#include <D3DX8Math.h>
//-------------------------------------------------------------------------------------------------
BezierSegment::BezierSegment()
{
for(int i=0; i < 4; i++)
m_controlPoints[i].zero();
}
//-------------------------------------------------------------------------------------------------
BezierSegment::BezierSegment(Real x0, Real y0, Real z0,
Real x1, Real y1, Real z1,
Real x2, Real y2, Real z2,
Real x3, Real y3, Real z3)
{
m_controlPoints[0].x = x0;
m_controlPoints[0].y = y0;
m_controlPoints[0].z = z0;
m_controlPoints[1].x = x1;
m_controlPoints[1].y = y1;
m_controlPoints[1].z = z1;
m_controlPoints[2].x = x2;
m_controlPoints[2].y = y2;
m_controlPoints[2].z = z2;
m_controlPoints[3].x = x3;
m_controlPoints[3].y = y3;
m_controlPoints[3].z = z3;
}
BezierSegment::BezierSegment(Real cp[12])
{
m_controlPoints[0].x = cp[0];
m_controlPoints[0].y = cp[1];
m_controlPoints[0].z = cp[2];
m_controlPoints[1].x = cp[3];
m_controlPoints[1].y = cp[4];
m_controlPoints[1].z = cp[5];
m_controlPoints[2].x = cp[6];
m_controlPoints[2].y = cp[7];
m_controlPoints[2].z = cp[8];
m_controlPoints[3].x = cp[9];
m_controlPoints[3].y = cp[10];
m_controlPoints[3].z = cp[11];
}
BezierSegment::BezierSegment(const Coord3D& cp0, const Coord3D& cp1, const Coord3D& cp2, const Coord3D& cp3)
{
m_controlPoints[0] = cp0;
m_controlPoints[1] = cp1;
m_controlPoints[2] = cp2;
m_controlPoints[3] = cp3;
}
BezierSegment::BezierSegment(Coord3D cp[4])
{
m_controlPoints[0] = cp[0];
m_controlPoints[1] = cp[1];
m_controlPoints[2] = cp[2];
m_controlPoints[3] = cp[3];
}
//-------------------------------------------------------------------------------------------------
void BezierSegment::evaluateBezSegmentAtT(Real tValue, Coord3D *outResult) const
{
if (!outResult)
return;
D3DXVECTOR4 tVec(tValue * tValue * tValue, tValue * tValue, tValue, 1);
D3DXVECTOR4 xCoords(m_controlPoints[0].x, m_controlPoints[1].x, m_controlPoints[2].x, m_controlPoints[3].x);
D3DXVECTOR4 yCoords(m_controlPoints[0].y, m_controlPoints[1].y, m_controlPoints[2].y, m_controlPoints[3].y);
D3DXVECTOR4 zCoords(m_controlPoints[0].z, m_controlPoints[1].z, m_controlPoints[2].z, m_controlPoints[3].z);
D3DXVECTOR4 tResult;
D3DXVec4Transform(&tResult, &tVec, &BezierSegment::s_bezBasisMatrix);
outResult->x = D3DXVec4Dot(&xCoords, &tResult);
outResult->y = D3DXVec4Dot(&yCoords, &tResult);
outResult->z = D3DXVec4Dot(&zCoords, &tResult);
}
//-------------------------------------------------------------------------------------------------
void BezierSegment::getSegmentPoints(Int numSegments, VecCoord3D *outResult) const
{
if (!outResult) {
return;
}
outResult->clear();
outResult->resize(numSegments);
BezFwdIterator iter(numSegments, this);
iter.start();
Int i = 0;
while (!iter.done()) {
(*outResult)[i] = iter.getCurrent();
++i;
iter.next();
}
}
//-------------------------------------------------------------------------------------------------
// This function isn't terribly fast. There are alternatives, and if this is too slow, we can
// take a look at the other approximations.
// There is no known close-form solution to this problem.
Real BezierSegment::getApproximateLength(Real withinTolerance) const
{
/*
How this works:
We can determine the approximate length of a bezier segment by
L0 = |(P0,P1)| + |(P1,P2)| + |(P2,P3)|
L1 = |(P0,P3)|
The length of the segment is approximately 1/2 L0 + 1/2 L1
P1__P2
/ \
P0----P3
The error in this is L1 - L0. If the error is too much, then we subdivide the curve and
try again.
*/
Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
m_controlPoints[1].y - m_controlPoints[0].y,
m_controlPoints[1].z - m_controlPoints[0].z, };
Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
m_controlPoints[2].y - m_controlPoints[1].y,
m_controlPoints[2].z - m_controlPoints[1].z, };
Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
m_controlPoints[3].y - m_controlPoints[2].y,
m_controlPoints[3].z - m_controlPoints[2].z, };
Coord3D p0p3 = { m_controlPoints[3].x - m_controlPoints[0].x,
m_controlPoints[3].y - m_controlPoints[0].y,
m_controlPoints[3].z - m_controlPoints[0].z, };
Real length0 = p0p3.length();
Real length1 = p0p1.length() + p1p2.length() + p2p3.length();
if ((length1 - length0) > withinTolerance) {
BezierSegment seg1, seg2;
splitSegmentAtT(0.5f, seg1, seg2);
return (seg1.getApproximateLength(withinTolerance) + seg2.getApproximateLength(withinTolerance));
}
return (INT_TO_REAL(length0 + length1) / 2.0f);
}
//-------------------------------------------------------------------------------------------------
void BezierSegment::splitSegmentAtT(Real tValue, BezierSegment &outSeg1, BezierSegment &outSeg2) const
{
// I think there are faster ways to do this. Could someone clue me in?
Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
m_controlPoints[1].y - m_controlPoints[0].y,
m_controlPoints[1].z - m_controlPoints[0].z, };
Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
m_controlPoints[2].y - m_controlPoints[1].y,
m_controlPoints[2].z - m_controlPoints[1].z, };
Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
m_controlPoints[3].y - m_controlPoints[2].y,
m_controlPoints[3].z - m_controlPoints[2].z, };
p0p1.scale(tValue);
p1p2.scale(tValue);
p2p3.scale(tValue);
p0p1.add(&m_controlPoints[0]);
p1p2.add(&m_controlPoints[1]);
p2p3.add(&m_controlPoints[2]);
Coord3D triLeft = { p1p2.x - p0p1.x,
p1p2.y - p0p1.y,
p1p2.z - p0p1.z, };
Coord3D triRight = { p2p3.x - p1p2.x,
p2p3.y - p1p2.y,
p2p3.z - p1p2.z, };
triLeft.scale(tValue);
triRight.scale(tValue);
triLeft.add(&p0p1);
triRight.add(&p1p2);
outSeg1.m_controlPoints[0] = m_controlPoints[0];
outSeg1.m_controlPoints[1] = p0p1;
outSeg1.m_controlPoints[2] = triLeft;
evaluateBezSegmentAtT(tValue, &outSeg1.m_controlPoints[3]);
outSeg2.m_controlPoints[0] = outSeg1.m_controlPoints[3];
outSeg2.m_controlPoints[1] = triRight;
outSeg2.m_controlPoints[2] = p2p3;
outSeg2.m_controlPoints[3] = m_controlPoints[3];
}
//-------------------------------------------------------------------------------------------------
// The Basis Matrix for a bezier segment
const D3DXMATRIX BezierSegment::s_bezBasisMatrix(
-1.0f, 3.0f, -3.0f, 1.0f,
3.0f, -6.0f, 3.0f, 0.0f,
-3.0f, 3.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f
);

View File

@@ -0,0 +1,190 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: BitFlags.cpp ///////////////////////////////////////////////////////////
//
// Used to set detail levels of various game systems.
// Steven Johnson, Sept 2002
//
//
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/BitFlags.h"
#include "Common/BitFlagsIO.h"
#include "Common/ModelState.h"
#include "GameLogic/ArmorSet.h"
const char* ModelConditionFlags::s_bitNameList[] =
{
"TOPPLED",
"FRONTCRUSHED",
"BACKCRUSHED",
"DAMAGED",
"REALLYDAMAGED",
"RUBBLE",
"SPECIAL_DAMAGED",
"NIGHT",
"SNOW",
"PARACHUTING",
"GARRISONED",
"ENEMYNEAR",
"WEAPONSET_VETERAN",
"WEAPONSET_ELITE",
"WEAPONSET_HERO",
"WEAPONSET_CRATEUPGRADE_ONE",
"WEAPONSET_CRATEUPGRADE_TWO",
"WEAPONSET_PLAYER_UPGRADE",
"DOOR_1_OPENING",
"DOOR_1_CLOSING",
"DOOR_1_WAITING_OPEN",
"DOOR_1_WAITING_TO_CLOSE",
"DOOR_2_OPENING",
"DOOR_2_CLOSING",
"DOOR_2_WAITING_OPEN",
"DOOR_2_WAITING_TO_CLOSE",
"DOOR_3_OPENING",
"DOOR_3_CLOSING",
"DOOR_3_WAITING_OPEN",
"DOOR_3_WAITING_TO_CLOSE",
"DOOR_4_OPENING",
"DOOR_4_CLOSING",
"DOOR_4_WAITING_OPEN",
"DOOR_4_WAITING_TO_CLOSE",
"ATTACKING",
"PREATTACK_A",
"FIRING_A",
"BETWEEN_FIRING_SHOTS_A",
"RELOADING_A",
"PREATTACK_B",
"FIRING_B",
"BETWEEN_FIRING_SHOTS_B",
"RELOADING_B",
"PREATTACK_C",
"FIRING_C",
"BETWEEN_FIRING_SHOTS_C",
"RELOADING_C",
"TURRET_ROTATE",
"POST_COLLAPSE",
"MOVING",
"DYING",
"AWAITING_CONSTRUCTION",
"PARTIALLY_CONSTRUCTED",
"ACTIVELY_BEING_CONSTRUCTED",
"PRONE",
"FREEFALL",
"ACTIVELY_CONSTRUCTING",
"CONSTRUCTION_COMPLETE",
"RADAR_EXTENDING",
"RADAR_UPGRADED",
"PANICKING", // yes, it's spelled with a "k". look it up.
"AFLAME",
"SMOLDERING",
"BURNED",
"DOCKING",
"DOCKING_BEGINNING",
"DOCKING_ACTIVE",
"DOCKING_ENDING",
"CARRYING",
"FLOODED",
"LOADED",
"JETAFTERBURNER",
"JETEXHAUST",
"PACKING",
"UNPACKING",
"DEPLOYED",
"OVER_WATER",
"POWER_PLANT_UPGRADED",
"CLIMBING",
"SOLD",
#ifdef ALLOW_SURRENDER
"SURRENDER",
#endif
"RAPPELLING",
"ARMED",
"POWER_PLANT_UPGRADING",
"SPECIAL_CHEERING",
"CONTINUOUS_FIRE_SLOW",
"CONTINUOUS_FIRE_MEAN",
"CONTINUOUS_FIRE_FAST",
"RAISING_FLAG",
"CAPTURED",
"EXPLODED_FLAILING",
"EXPLODED_BOUNCING",
"SPLATTED",
"USING_WEAPON_A",
"USING_WEAPON_B",
"USING_WEAPON_C",
"PREORDER",
"CENTER_TO_LEFT",
"LEFT_TO_CENTER",
"CENTER_TO_RIGHT",
"RIGHT_TO_CENTER",
"RIDER1", //Kris: Added these for different combat-bike riders, but feel free to use these for anything.
"RIDER2",
"RIDER3",
"RIDER4",
"RIDER5",
"RIDER6",
"RIDER7",
"RIDER8",
"STUNNED_FLAILING", // Daniel Teh's idea, added by Lorenzen, 5/28/03
"STUNNED",
"SECOND_LIFE",
"JAMMED",
"ARMORSET_CRATEUPGRADE_ONE",
"ARMORSET_CRATEUPGRADE_TWO",
"USER_1",
"USER_2",
"DISGUISED",
NULL
};
const char* ArmorSetFlags::s_bitNameList[] =
{
"VETERAN",
"ELITE",
"HERO",
"PLAYER_UPGRADE",
"WEAK_VERSUS_BASEDEFENSES",
"SECOND_LIFE",
"CRATE_UPGRADE_ONE",
"CRATE_UPGRADE_TWO",
NULL
};

View File

@@ -0,0 +1,273 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CRCDebug.h"
#include "Common/Debug.h"
#include "Common/PerfTimer.h"
#include "GameClient/InGameUI.h"
#include "GameNetwork/IPEnumeration.h"
#include <cstdarg>
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#ifdef DEBUG_CRC
static const Int MaxStrings = 64000;
static char DebugStrings[MaxStrings][1024];
static Int nextDebugString = 0;
static Int numDebugStrings = 0;
//static char DumpStrings[MaxStrings][1024];
//static Int nextDumpString = 0;
//static Int numDumpStrings = 0;
#define IS_FRAME_OK_TO_LOG TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() && !TheDebugIgnoreSyncErrors && \
TheCRCFirstFrameToLog >= 0 && TheCRCFirstFrameToLog <= TheGameLogic->getFrame() \
&& TheGameLogic->getFrame() <= TheCRCLastFrameToLog
CRCVerification::CRCVerification()
{
#ifdef DEBUG_LOGGING
/**/
if (g_verifyClientCRC && (IS_FRAME_OK_TO_LOG))
{
m_startCRC = TheGameLogic->getCRC(CRC_RECALC, (g_clientDeepCRC)?"clientPre.crc":"");
}
else
{
m_startCRC = 0;
}
/**/
#endif
}
CRCVerification::~CRCVerification()
{
#ifdef DEBUG_LOGGING
/**/
UnsignedInt endCRC = 0;
if (g_verifyClientCRC && (IS_FRAME_OK_TO_LOG))
{
endCRC = TheGameLogic->getCRC(CRC_RECALC, (g_clientDeepCRC)?"clientPost.crc":"");
}
DEBUG_ASSERTCRASH(!TheGameLogic->isInGame() || m_startCRC == endCRC, ("GameLogic changed outside of GameLogic::update() on frame %d!", TheGameLogic->getFrame()));
if (TheGameLogic->isInMultiplayerGame() && m_startCRC != endCRC)
{
if (TheInGameUI)
{
TheInGameUI->message(UnicodeString(L"GameLogic changed outside of GameLogic::update() - call Matt (x36804)!"));
}
CRCDEBUG_LOG(("GameLogic changed outside of GameLogic::update()!!!\n"));
}
/**/
#endif
}
static Bool dumped = FALSE;
void outputCRCDebugLines( void )
{
if (dumped)
return;
dumped = TRUE;
IPEnumeration ips;
AsciiString fname;
fname.format("crcDebug%s.txt", ips.getMachineName().str());
FILE *fp = fopen(fname.str(), "wt");
int start = 0;
int end = nextDebugString;
if (numDebugStrings >= MaxStrings)
start = nextDebugString - MaxStrings;
for (Int i=start; i<end; ++i)
{
const char *line = DebugStrings[ (i + MaxStrings) % MaxStrings ];
DEBUG_LOG(("%s\n", line));
if (fp) fprintf(fp, "%s\n", line);
}
if (fp) fclose(fp);
}
void outputCRCDumpLines( void )
{
/*
int start = 0;
int end = nextDumpString;
if (numDumpStrings >= MaxStrings)
start = nextDumpString - MaxStrings;
for (Int i=start; i<end; ++i)
{
const char *line = DumpStrings[ (i + MaxStrings) % MaxStrings ];
DEBUG_LOG(("%s", line));
}
*/
}
static AsciiString getFname(AsciiString path)
{
return path.reverseFind('\\') + 1;
}
Int lastCRCDebugFrame = 0;
Int lastCRCDebugIndex = 0;
extern Bool inCRCGen;
void addCRCDebugLine(const char *fmt, ...)
{
if (dumped)// || inCRCGen /*|| !TheGameLogic->isInGameLogicUpdate()*/)
return;
if (IS_FRAME_OK_TO_LOG)
{
if (lastCRCDebugFrame != TheGameLogic->getFrame())
{
lastCRCDebugFrame = TheGameLogic->getFrame();
lastCRCDebugIndex = 0;
}
sprintf(DebugStrings[nextDebugString], "%d:%d ", TheGameLogic->getFrame(), lastCRCDebugIndex++);
//DebugStrings[nextDebugString][0] = 0;
Int len = strlen(DebugStrings[nextDebugString]);
va_list va;
va_start( va, fmt );
_vsnprintf(DebugStrings[nextDebugString]+len, 1024-len, fmt, va );
DebugStrings[nextDebugString][1023] = 0;
va_end( va );
char *tmp = DebugStrings[nextDebugString];
while (tmp && *tmp)
{
if (*tmp == '\r' || *tmp == '\n')
{
*tmp = ' ';
}
++tmp;
}
//DEBUG_LOG(("%s\n", DebugStrings[nextDebugString]));
++nextDebugString;
++numDebugStrings;
if (nextDebugString == MaxStrings)
nextDebugString = 0;
}
}
void addCRCGenLine(const char *fmt, ...)
{
if (dumped || !(IS_FRAME_OK_TO_LOG))
return;
static char buf[1024];
va_list va;
va_start( va, fmt );
_vsnprintf(buf, 1024, fmt, va );
va_end( va );
buf[1023] = 0;
addCRCDebugLine("%s", buf);
//DEBUG_LOG(("%s", buf));
}
void addCRCDumpLine(const char *fmt, ...)
{
/*
va_list va;
va_start( va, fmt );
_vsnprintf(DumpStrings[nextDumpString], 1024, fmt, va );
DumpStrings[nextDumpString][1023] = 0;
va_end( va );
++nextDumpString;
++numDumpStrings;
if (nextDumpString == MaxStrings)
nextDumpString = 0;
*/
}
void dumpVector3(const Vector3 *v, AsciiString name, AsciiString fname, Int line)
{
if (dumped)
return;
if (!(IS_FRAME_OK_TO_LOG)) return;
fname.toLower();
fname = getFname(fname);
addCRCDebugLine("dumpVector3() %s:%d %s %8.8X %8.8X %8.8X\n",
fname.str(), line, name.str(),
AS_INT(v->X), AS_INT(v->Y), AS_INT(v->Z));
}
void dumpCoord3D(const Coord3D *c, AsciiString name, AsciiString fname, Int line)
{
if (dumped)
return;
if (!(IS_FRAME_OK_TO_LOG)) return;
fname.toLower();
fname = getFname(fname);
addCRCDebugLine("dumpCoord3D() %s:%d %s %8.8X %8.8X %8.8X\n",
fname.str(), line, name.str(),
AS_INT(c->x), AS_INT(c->y), AS_INT(c->z));
}
void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line)
{
if (dumped)
return;
if (!(IS_FRAME_OK_TO_LOG)) return;
fname.toLower();
fname = getFname(fname);
const Real *matrix = (const Real *)m;
addCRCDebugLine("dumpMatrix3D() %s:%d %s\n",
fname.str(), line, name.str());
for (Int i=0; i<3; ++i)
addCRCDebugLine(" 0x%08X 0x%08X 0x%08X 0x%08X\n",
AS_INT(matrix[(i<<2)+0]), AS_INT(matrix[(i<<2)+1]), AS_INT(matrix[(i<<2)+2]), AS_INT(matrix[(i<<2)+3]));
}
void dumpReal(Real r, AsciiString name, AsciiString fname, Int line)
{
if (dumped)
return;
if (!(IS_FRAME_OK_TO_LOG)) return;
fname.toLower();
fname = getFname(fname);
addCRCDebugLine("dumpReal() %s:%d %s %8.8X (%f)\n",
fname.str(), line, name.str(), AS_INT(r), r);
}
#endif // DEBUG_CRC

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,313 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: DamageFX.cpp ///////////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, November 2001
// Desc: DamageFX descriptions
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/INI.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/DamageFX.h"
#include "Common/GameAudio.h"
#include "GameClient/FXList.h"
#include "GameLogic/Damage.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameClient/GameClient.h"
#include "GameClient/InGameUI.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
DamageFXStore *TheDamageFXStore = NULL; ///< the DamageFX store definition
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
DamageFX::DamageFX()
{
// not necessary.
//clear();
}
//-------------------------------------------------------------------------------------------------
void DamageFX::clear()
{
for (Int dt = 0; dt < DAMAGE_NUM_TYPES; ++dt)
{
for (Int v = LEVEL_FIRST; v <= LEVEL_LAST; ++v)
{
m_dfx[dt][v].clear();
}
}
}
//-------------------------------------------------------------------------------------------------
UnsignedInt DamageFX::getDamageFXThrottleTime(DamageType t, const Object* source) const
{
return m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR].m_damageFXThrottleTime;
}
//-------------------------------------------------------------------------------------------------
void DamageFX::doDamageFX(DamageType t, Real damageAmount, const Object* source, const Object* victim) const
{
ConstFXListPtr fx = getDamageFXList(t, damageAmount, source);
// since the victim is receiving the damage, it's the "primary" object.
// the source is the "secondary" object -- unused by most fx, but could be
// useful in some cases.
FXList::doFXObj(fx, victim, source);
}
//-------------------------------------------------------------------------------------------------
ConstFXListPtr DamageFX::getDamageFXList(DamageType t, Real damageAmount, const Object* source) const
{
/*
if damage is zero, never do damage fx. this is by design, since "zero" damage can happen
with some special weapons, like the battleship, which is a "faux" weapon that never does damage.
if you really need to change this for some reason, consider carefully... (srj)
*/
if (damageAmount == 0.0f)
return NULL;
const DFX& dfx = m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR];
ConstFXListPtr fx =
damageAmount >= dfx.m_amountForMajorFX ?
dfx.m_majorDamageFXList :
dfx.m_minorDamageFXList;
return fx;
}
//-------------------------------------------------------------------------------------------------
const FieldParse* DamageFX::getFieldParse() const
{
static const FieldParse myFieldParse[] =
{
{ "AmountForMajorFX", parseAmount, NULL, 0 },
{ "MajorFX", parseMajorFXList, NULL, 0 },
{ "MinorFX", parseMinorFXList, NULL, 0 },
{ "ThrottleTime", parseTime, NULL, 0 },
{ "VeterancyAmountForMajorFX", parseAmount, TheVeterancyNames, 0 },
{ "VeterancyMajorFX", parseMajorFXList, TheVeterancyNames, 0 },
{ "VeterancyMinorFX", parseMinorFXList, TheVeterancyNames, 0 },
{ "VeterancyThrottleTime", parseTime, TheVeterancyNames, 0 },
{ 0, 0, 0,0 }
};
return myFieldParse;
}
//-------------------------------------------------------------------------------------------Static
static void parseCommonStuff(
INI* ini,
ConstCharPtrArray names,
VeterancyLevel& vetFirst,
VeterancyLevel& vetLast,
DamageType& damageFirst,
DamageType& damageLast
)
{
if (names)
{
vetFirst = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), names);
vetLast = vetFirst;
}
else
{
vetFirst = LEVEL_FIRST;
vetLast = LEVEL_LAST;
}
const char* damageName = ini->getNextToken();
if (stricmp(damageName, "Default") == 0)
{
damageFirst = (DamageType)0;
damageLast = (DamageType)(DAMAGE_NUM_TYPES - 1);
}
else
{
damageFirst = (DamageType)DamageTypeFlags::getSingleBitFromName(damageName);
damageLast = damageFirst;
}
}
//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseAmount( INI* ini, void* instance, void* /*store*/, const void* userData )
{
DamageFX* self = (DamageFX*)instance;
ConstCharPtrArray names = (ConstCharPtrArray)userData;
VeterancyLevel vetFirst, vetLast;
DamageType damageFirst, damageLast;
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
Real amt = INI::scanReal(ini->getNextToken());
for (Int dt = damageFirst; dt <= damageLast; ++dt)
{
for (Int v = vetFirst; v <= vetLast; ++v)
{
self->m_dfx[dt][v].m_amountForMajorFX = amt;
}
}
}
//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseMajorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
{
DamageFX* self = (DamageFX*)instance;
ConstCharPtrArray names = (ConstCharPtrArray)userData;
VeterancyLevel vetFirst, vetLast;
DamageType damageFirst, damageLast;
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
ConstFXListPtr fx;
INI::parseFXList(ini, NULL, &fx, NULL);
for (Int dt = damageFirst; dt <= damageLast; ++dt)
{
for (Int v = vetFirst; v <= vetLast; ++v)
{
self->m_dfx[dt][v].m_majorDamageFXList = fx;
}
}
}
//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseMinorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
{
DamageFX* self = (DamageFX*)instance;
ConstCharPtrArray names = (ConstCharPtrArray)userData;
VeterancyLevel vetFirst, vetLast;
DamageType damageFirst, damageLast;
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
ConstFXListPtr fx;
INI::parseFXList(ini, NULL, &fx, NULL);
for (Int dt = damageFirst; dt <= damageLast; ++dt)
{
for (Int v = vetFirst; v <= vetLast; ++v)
{
self->m_dfx[dt][v].m_minorDamageFXList = fx;
}
}
}
//-------------------------------------------------------------------------------------------Static
/*static*/ void DamageFX::parseTime( INI* ini, void* instance, void* /*store*/, const void* userData )
{
DamageFX* self = (DamageFX*)instance;
ConstCharPtrArray names = (ConstCharPtrArray)userData;
VeterancyLevel vetFirst, vetLast;
DamageType damageFirst, damageLast;
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
UnsignedInt t;
INI::parseDurationUnsignedInt(ini, NULL, &t, NULL);
for (Int dt = damageFirst; dt <= damageLast; ++dt)
{
for (Int v = vetFirst; v <= vetLast; ++v)
{
self->m_dfx[dt][v].m_damageFXThrottleTime = t;
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
DamageFXStore::DamageFXStore()
{
m_dfxmap.clear();
}
//-------------------------------------------------------------------------------------------------
DamageFXStore::~DamageFXStore()
{
m_dfxmap.clear();
}
//-------------------------------------------------------------------------------------------------
const DamageFX *DamageFXStore::findDamageFX(AsciiString name) const
{
NameKeyType namekey = TheNameKeyGenerator->nameToKey(name);
DamageFXMap::const_iterator it = m_dfxmap.find(namekey);
if (it == m_dfxmap.end())
{
return NULL;
}
else
{
return &(*it).second;
}
}
//-------------------------------------------------------------------------------------------------
void DamageFXStore::init()
{
}
//-------------------------------------------------------------------------------------------------
void DamageFXStore::reset()
{
}
//-------------------------------------------------------------------------------------------------
void DamageFXStore::update()
{
}
//-------------------------------------------------------------------------------------------------
/*static */ void DamageFXStore::parseDamageFXDefinition(INI* ini)
{
const char *c = ini->getNextToken();
NameKeyType key = TheNameKeyGenerator->nameToKey(c);
DamageFX& dfx = TheDamageFXStore->m_dfxmap[key];
dfx.clear();
ini->initFromINI(&dfx, dfx.getFieldParse());
}

View File

@@ -0,0 +1,550 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Dict.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Dict.cpp
//
// Created: Steven Johnson, November 2001
//
// Desc: General-purpose dictionary class
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/Dict.h"
#include "Common/GameMemory.h"
// -----------------------------------------------------
void Dict::DictPair::copyFrom(DictPair* that)
{
Dict::DataType curType = this->getType();
Dict::DataType newType = that->getType();
if (curType != newType)
{
clear();
}
switch(newType)
{
case DICT_BOOL:
case DICT_INT:
case DICT_REAL:
*this = *that;
break;
case DICT_ASCIISTRING:
this->m_key = that->m_key;
*this->asAsciiString() = *that->asAsciiString();
break;
case DICT_UNICODESTRING:
this->m_key = that->m_key;
*this->asUnicodeString() = *that->asUnicodeString();
break;
}
}
// -----------------------------------------------------
void Dict::DictPair::clear()
{
switch (getType())
{
case DICT_BOOL:
case DICT_INT:
case DICT_REAL:
m_value = 0;
break;
case DICT_ASCIISTRING:
asAsciiString()->clear();
break;
case DICT_UNICODESTRING:
asUnicodeString()->clear();
break;
}
}
// -----------------------------------------------------
void Dict::DictPair::setNameAndType(NameKeyType key, Dict::DataType type)
{
Dict::DataType curType = getType();
if (curType != type)
{
clear();
}
m_key = createKey(key, type);
}
// -----------------------------------------------------
#ifdef _DEBUG
void Dict::validate() const
{
if (!m_data) return;
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated > 0, ("m_numPairsAllocated is zero"));
DEBUG_ASSERTCRASH(m_data->m_numPairsUsed >= 0, ("m_numPairsUsed is neg"));
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated >= m_data->m_numPairsUsed, ("m_numPairsAllocated too small"));
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated < 1024, ("m_numPairsAllocated suspiciously large"));
}
#endif
// -----------------------------------------------------
Dict::DictPair* Dict::findPairByKey(NameKeyType key) const
{
DEBUG_ASSERTCRASH(key != NAMEKEY_INVALID, ("invalid namekey!"));
DEBUG_ASSERTCRASH((UnsignedInt)key < (1L<<23), ("namekey too large!"));
if (!m_data)
return NULL;
DictPair* base = m_data->peek();
Int minIdx = 0;
Int maxIdx = m_data->m_numPairsUsed;
while (minIdx < maxIdx)
{
Int midIdx = (((minIdx + maxIdx) - 1) >> 1);
DictPair* mid = base + midIdx;
NameKeyType midKey = mid->getName();
if (key > midKey)
minIdx = midIdx + 1;
else if (key < midKey)
maxIdx = midIdx;
else
return mid;
}
return NULL;
}
// -----------------------------------------------------
Dict::DictPair *Dict::ensureUnique(int numPairsNeeded, Bool preserveData, DictPair *pairToTranslate)
{
if (m_data &&
m_data->m_refCount == 1 &&
m_data->m_numPairsAllocated >= numPairsNeeded)
{
// no buffer manhandling is needed (it's already large enough, and unique to us)
return pairToTranslate;
}
if (numPairsNeeded > MAX_LEN)
throw ERROR_OUT_OF_MEMORY;
Dict::DictPairData* newData = NULL;
if (numPairsNeeded > 0)
{
int minBytes = sizeof(Dict::DictPairData) + numPairsNeeded*sizeof(Dict::DictPair);
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
// note: be certain to alloc with zero; we'll take advantage of the fact that all-zero
// is a bit-pattern that happens to init all our pairs to legal values:
// type BOOL, key INVALID, value FALSE.
newData = (Dict::DictPairData*)TheDynamicMemoryAllocator->allocateBytes(actualBytes, "Dict::ensureUnique");
newData->m_refCount = 1;
newData->m_numPairsAllocated = (actualBytes - sizeof(Dict::DictPairData))/sizeof(Dict::DictPair);
newData->m_numPairsUsed = 0;
if (preserveData && m_data)
{
Dict::DictPair* src = m_data->peek();
Dict::DictPair* dst = newData->peek();
for (Int i = 0; i < m_data->m_numPairsUsed; ++i, ++src, ++dst)
dst->copyFrom(src);
newData->m_numPairsUsed = m_data->m_numPairsUsed;
}
}
Int delta;
if (pairToTranslate && m_data)
delta = pairToTranslate - m_data->peek();
releaseData();
m_data = newData;
if (pairToTranslate && m_data)
pairToTranslate = m_data->peek() + delta;
return pairToTranslate;
}
// -----------------------------------------------------
void Dict::clear()
{
releaseData();
m_data = NULL;
}
// -----------------------------------------------------
void Dict::releaseData()
{
if (m_data)
{
if (--m_data->m_refCount == 0)
{
Dict::DictPair* src = m_data->peek();
for (Int i = 0; i < m_data->m_numPairsUsed; ++i, ++src)
src->clear();
TheDynamicMemoryAllocator->freeBytes(m_data);
}
m_data = 0;
}
}
// -----------------------------------------------------
Dict::Dict(Int numPairsToPreAllocate) : m_data(0)
{
/*
This class plays some skanky games, in the name of memory and code
efficiency; it assumes all the data types will fit into a pointer.
This is currently true, but if that assumption ever changes, all hell
will break loose. So we do a quick check to assure this...
*/
DEBUG_ASSERTCRASH(sizeof(Bool) <= sizeof(void*) &&
sizeof(Int) <= sizeof(void*) &&
sizeof(Real) <= sizeof(void*) &&
sizeof(AsciiString) <= sizeof(void*) &&
sizeof(UnicodeString) <= sizeof(void*), ("oops, this code needs attention"));
if (numPairsToPreAllocate)
ensureUnique(numPairsToPreAllocate, false, NULL); // will throw on error
}
// -----------------------------------------------------
Dict& Dict::operator=(const Dict& src)
{
validate();
if (&src != this)
{
releaseData();
m_data = src.m_data;
if (m_data)
++m_data->m_refCount;
}
validate();
return *this;
}
// -----------------------------------------------------
Dict::DataType Dict::getType(NameKeyType key) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair)
return pair->getType();
return DICT_NONE;
}
// -----------------------------------------------------
Bool Dict::getBool(NameKeyType key, Bool *exists/*=NULL*/) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair && pair->getType() == DICT_BOOL)
{
if (exists) *exists = true;
return *pair->asBool();
}
DEBUG_ASSERTCRASH(exists != NULL, ("dict key missing, or of wrong type\n")); // only assert if they didn't check result
if (exists) *exists = false;
return false;
}
// -----------------------------------------------------
Int Dict::getInt(NameKeyType key, Bool *exists/*=NULL*/) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair && pair->getType() == DICT_INT)
{
if (exists) *exists = true;
return *pair->asInt();
}
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
if (exists) *exists = false;
return 0;
}
// -----------------------------------------------------
Real Dict::getReal(NameKeyType key, Bool *exists/*=NULL*/) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair && pair->getType() == DICT_REAL)
{
if (exists) *exists = true;
return *pair->asReal();
}
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
if (exists) *exists = false;
return 0.0f;
}
// -----------------------------------------------------
AsciiString Dict::getAsciiString(NameKeyType key, Bool *exists/*=NULL*/) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair && pair->getType() == DICT_ASCIISTRING)
{
if (exists) *exists = true;
return *pair->asAsciiString();
}
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
if (exists) *exists = false;
return AsciiString::TheEmptyString;
}
// -----------------------------------------------------
UnicodeString Dict::getUnicodeString(NameKeyType key, Bool *exists/*=NULL*/) const
{
validate();
DictPair* pair = findPairByKey(key);
if (pair && pair->getType() == DICT_UNICODESTRING)
{
if (exists) *exists = true;
return *pair->asUnicodeString();
}
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
if (exists) *exists = false;
return UnicodeString::TheEmptyString;
}
// -----------------------------------------------------
Bool Dict::getNthBool(Int n) const
{
validate();
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
if (m_data)
{
DictPair* pair = &m_data->peek()[n];
if (pair && pair->getType() == DICT_BOOL)
return *pair->asBool();
}
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
return false;
}
// -----------------------------------------------------
Int Dict::getNthInt(Int n) const
{
validate();
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
if (m_data)
{
DictPair* pair = &m_data->peek()[n];
if (pair && pair->getType() == DICT_INT)
return *pair->asInt();
}
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
return 0;
}
// -----------------------------------------------------
Real Dict::getNthReal(Int n) const
{
validate();
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
if (m_data)
{
DictPair* pair = &m_data->peek()[n];
if (pair && pair->getType() == DICT_REAL)
return *pair->asReal();
}
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
return 0.0f;
}
// -----------------------------------------------------
AsciiString Dict::getNthAsciiString(Int n) const
{
validate();
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
if (m_data)
{
DictPair* pair = &m_data->peek()[n];
if (pair && pair->getType() == DICT_ASCIISTRING)
return *pair->asAsciiString();
}
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
return AsciiString::TheEmptyString;
}
// -----------------------------------------------------
UnicodeString Dict::getNthUnicodeString(Int n) const
{
validate();
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
if (m_data)
{
DictPair* pair = &m_data->peek()[n];
if (pair && pair->getType() == DICT_UNICODESTRING)
return *pair->asUnicodeString();
}
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
return UnicodeString::TheEmptyString;
}
// -----------------------------------------------------
Dict::DictPair *Dict::setPrep(NameKeyType key, Dict::DataType type)
{
DictPair* pair = findPairByKey(key);
Int pairsNeeded = getPairCount();
if (!pair)
++pairsNeeded;
pair = ensureUnique(pairsNeeded, true, pair);
if (!pair)
{
pair = &m_data->peek()[m_data->m_numPairsUsed++];
}
pair->setNameAndType(key, type);
DEBUG_ASSERTCRASH(pair, ("pair must not be null here"));
return pair;
}
// -----------------------------------------------------
void Dict::sortPairs()
{
if (!m_data)
return;
// yer basic shellsort.
for (Int gap = m_data->m_numPairsUsed >> 1; gap > 0; gap >>= 1)
{
for (Int i = gap; i < m_data->m_numPairsUsed; i++)
{
for (Int j = i - gap; j >= 0; j -= gap)
{
DictPair* a = m_data->peek() + j;
DictPair* b = m_data->peek() + j + gap;
if (a->getName() > b->getName())
{
DictPair tmp = *a;
*a = *b;
*b = tmp;
}
else
{
break;
}
}
}
}
}
// -----------------------------------------------------
void Dict::setBool(NameKeyType key, Bool value)
{
validate();
DictPair* pair = setPrep(key, DICT_BOOL);
*pair->asBool() = value;
sortPairs();
validate();
}
// -----------------------------------------------------
void Dict::setInt(NameKeyType key, Int value)
{
validate();
DictPair* pair = setPrep(key, DICT_INT);
*pair->asInt() = value;
sortPairs();
validate();
}
// -----------------------------------------------------
void Dict::setReal(NameKeyType key, Real value)
{
validate();
DictPair* pair = setPrep(key, DICT_REAL);
*pair->asReal() = value;
sortPairs();
validate();
}
// -----------------------------------------------------
void Dict::setAsciiString(NameKeyType key, const AsciiString& value)
{
validate();
DictPair* pair = setPrep(key, DICT_ASCIISTRING);
*pair->asAsciiString() = value;
sortPairs();
validate();
}
// -----------------------------------------------------
void Dict::setUnicodeString(NameKeyType key, const UnicodeString& value)
{
validate();
DictPair* pair = setPrep(key, DICT_UNICODESTRING);
*pair->asUnicodeString() = value;
sortPairs();
validate();
}
// -----------------------------------------------------
Bool Dict::remove(NameKeyType key)
{
validate();
DictPair* pair = findPairByKey(key);
if (pair)
{
pair = ensureUnique(m_data->m_numPairsUsed, true, pair);
pair->setNameAndType((NameKeyType)0x7fffffff, DICT_BOOL);
sortPairs();
--m_data->m_numPairsUsed;
validate();
return true;
}
DEBUG_CRASH(("dict key missing in remove\n"));
return false;
}
// -----------------------------------------------------
void Dict::copyPairFrom(const Dict& that, NameKeyType key)
{
this->validate();
DictPair* thatPair = that.findPairByKey(key);
if (thatPair)
{
DictPair* thisPair = this->setPrep(key, thatPair->getType());
thisPair->copyFrom(thatPair);
this->sortPairs();
}
else
{
if (this->findPairByKey(key))
this->remove(key);
}
this->validate();
}

View File

@@ -0,0 +1,114 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: DiscreteCircle.cpp ////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// EA Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: DiscreteCircle.cpp
//
// Created: John McDonald, September 2002
//
// Desc: ???
//
//-----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/DiscreteCircle.h"
//-------------------------------------------------------------------------------------------------
DiscreteCircle::DiscreteCircle(Int xCenter, Int yCenter, Int radius)
{
m_yPos = yCenter;
m_yPosDoubled = (yCenter << 1);
m_edges.reserve(radius << 1); // largest that it should ever be.
generateEdgePairs(xCenter, yCenter, radius);
removeDuplicates();
}
//-------------------------------------------------------------------------------------------------
void DiscreteCircle::drawCircle(ScanlineDrawFunc functionToDrawWith, void *parmToPass)
{
for (VecHorzLine::const_iterator it = m_edges.begin(); it != m_edges.end(); ++it) {
(functionToDrawWith)(it->xStart, it->xEnd, it->yPos, parmToPass);
if (it->yPos != m_yPos) {
(functionToDrawWith)(it->xStart, it->xEnd, m_yPosDoubled - it->yPos, parmToPass);
}
}
}
//-------------------------------------------------------------------------------------------------
void DiscreteCircle::generateEdgePairs(Int xCenter, Int yCenter, Int radius)
{
// Uses Bresenham to generate points.
Int x = 0;
Int y = radius;
Int d = (1 - radius) << 1;
while (y >= 0) {
HorzLine hl;
hl.xStart = xCenter - x;
hl.xEnd = xCenter + x;
hl.yPos = yCenter + y;
m_edges.push_back(hl);
if (d + y > 0) {
--y;
d -= ((y << 1) - 1);
}
if (x > d) {
++x;
d += ((x << 1) + 1);
}
}
}
//-------------------------------------------------------------------------------------------------
void DiscreteCircle::removeDuplicates()
{
VecHorzLineIt it, nextIt;
for ( it = m_edges.begin(); it != m_edges.end(); /* empty */) {
nextIt = it;
++nextIt;
if (nextIt == m_edges.end()) {
break;
}
if (it->yPos == nextIt->yPos) {
it = m_edges.erase(it);
} else {
++it;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,707 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: GameLOD.cpp ///////////////////////////////////////////////////////////
//
// Used to set detail levels of various game systems.
//
// Author: Mark Wilczynski, Sept 2002
//
//
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/GameLOD.h"
#include "GameClient/TerrainVisual.h"
#include "GameClient/GameClient.h"
#include "Common/UserPreferences.h"
#define DEFINE_PARTICLE_SYSTEM_NAMES
#include "GameClient/ParticleSys.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#define PROFILE_ERROR_LIMIT 0.94f //fraction of profiled result needed to get a match. Allows some room for error/fluctuation.
//Hack to get access to a static method on the W3DDevice side. -MW
extern Bool testMinimumRequirements(ChipsetType *videoChipType, CpuType *cpuType, Int *cpuFreq, Int *numRAM, Real *intBenchIndex, Real *floatBenchIndex, Real *memBenchIndex);
GameLODManager *TheGameLODManager=NULL;
static const FieldParse TheStaticGameLODFieldParseTable[] =
{
{ "MinimumFPS", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minFPS)},
{ "MinimumProcessorFps", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minProcessorFPS)},
{ "SampleCount2D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount2D ) },
{ "SampleCount3D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount3D ) },
{ "StreamCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_streamCount ) },
{ "MaxParticleCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxParticleCount ) },
{ "UseShadowVolumes", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowVolumes ) },
{ "UseShadowDecals", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowDecals ) },
{ "UseCloudMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useCloudMap ) },
{ "UseLightMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useLightMap ) },
{ "ShowSoftWaterEdge", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_showSoftWaterEdge ) },
{ "MaxTankTrackEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackEdges) },
{ "MaxTankTrackOpaqueEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackOpaqueEdges) },
{ "MaxTankTrackFadeDelay", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackFadeDelay) },
{ "UseBuildupScaffolds", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useBuildupScaffolds ) },
{ "UseTreeSway", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useTreeSway ) },
{ "UseEmissiveNightMaterials", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useEmissiveNightMaterials ) },
{ "UseHeatEffects", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useHeatEffects ) },
{ "TextureReductionFactor", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_textureReduction ) },
};
static const char *StaticGameLODNames[]=
{
"Low",
"Medium",
"High",
"Custom"
};
StaticGameLODInfo::StaticGameLODInfo(void)
{
m_minFPS=0;
m_minProcessorFPS=0;
m_sampleCount2D=6;
m_sampleCount3D=24;
m_streamCount=2;
m_maxParticleCount=2500;
m_useShadowVolumes=TRUE;
m_useShadowDecals=TRUE;
m_useCloudMap=TRUE;
m_useLightMap=TRUE;
m_showSoftWaterEdge=TRUE;
m_maxTankTrackEdges=100;
m_maxTankTrackOpaqueEdges=25;
m_maxTankTrackFadeDelay=300000;
m_useBuildupScaffolds=TRUE;
m_useTreeSway=TRUE;
m_useEmissiveNightMaterials=TRUE;
m_useHeatEffects=TRUE;
m_textureReduction = 0; //none
m_useFpsLimit = TRUE;
m_enableDynamicLOD = TRUE;
m_useTrees = TRUE;
}
static const FieldParse TheDynamicGameLODFieldParseTable[] =
{
{ "MinimumFPS", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_minFPS)},
{ "ParticleSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicParticleSkipMask)},
{ "DebrisSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicDebrisSkipMask)},
{ "SlowDeathScale", INI::parseReal, NULL, offsetof( DynamicGameLODInfo, m_slowDeathScale)},
{ "MinParticlePriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticlePriority)},
{ "MinParticleSkipPriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticleSkipPriority)},
};
static const char *DynamicGameLODNames[]=
{
"Low",
"Medium",
"High",
"VeryHigh"
};
DynamicGameLODInfo::DynamicGameLODInfo(void)
{
m_minFPS=0;
m_dynamicParticleSkipMask=0;
m_dynamicDebrisSkipMask=0;
m_slowDeathScale=1.0f;
m_minDynamicParticlePriority = PARTICLE_PRIORITY_LOWEST;
m_minDynamicParticleSkipPriority = PARTICLE_PRIORITY_LOWEST;
};
//Keep this in sync with enum in GameLOD.h
static char *CPUNames[] =
{
"XX","P3", "P4","K7", NULL
};
//Keep this in sync with enum in GameLOD.h
static char *VideoNames[] =
{
"XX","V2","V3","V4","V5","TNT","TNT2","GF2","R100","PS11","GF3","GF4","PS14","R200","PS20","R300", NULL
};
void parseReallyLowMHz(INI* ini)
{
Int mhz;
INI::parseInt(ini,NULL,&mhz,NULL);
if (TheGameLODManager)
{
TheGameLODManager->setReallyLowMHz(mhz);
}
}
void INI::parseBenchProfile( INI* ini)
{
if( TheGameLODManager )
{
BenchProfile *preset = TheGameLODManager->newBenchProfile();
if (preset)
{
INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
INI::parseReal(ini,NULL,&preset->m_intBenchIndex,NULL);
INI::parseReal(ini,NULL,&preset->m_floatBenchIndex,NULL);
INI::parseReal(ini,NULL,&preset->m_memBenchIndex,NULL);
}
}
}
/**Parse a description of all the LOD settings for a given detail level*/
void INI::parseLODPreset( INI* ini )
{
const char *c;
AsciiString name;
// read the name
c = ini->getNextToken();
name.set( c ); //name of detail level - low, medium, high
if( TheGameLODManager )
{
StaticGameLODLevel index = (StaticGameLODLevel)TheGameLODManager->getStaticGameLODIndex(name);
if (index != STATIC_GAME_LOD_UNKNOWN)
{
LODPresetInfo *preset = TheGameLODManager->newLODPreset(index);
if (preset)
{
INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
INI::parseIndexList(ini,NULL,&preset->m_videoType,VideoNames);
INI::parseInt(ini,NULL,&preset->m_memory,NULL);
}
}
}
}
GameLODManager::GameLODManager(void)
{
m_currentStaticLOD = STATIC_GAME_LOD_UNKNOWN;
m_currentDynamicLOD = DYNAMIC_GAME_LOD_HIGH;
m_numParticleGenerations=0;
m_dynamicParticleSkipMask=0;
m_numDebrisGenerations=0;
m_dynamicDebrisSkipMask=0;
m_videoPassed=false;
m_cpuPassed=false;
m_memPassed=false;
m_slowDeathScale=1.0f;
m_idealDetailLevel = STATIC_GAME_LOD_UNKNOWN;
m_videoChipType = DC_MAX;
m_cpuType = XX;
m_numRAM=0;
m_cpuFreq=0;
m_intBenchIndex=0;
m_floatBenchIndex=0;
m_memBenchIndex=0;
m_compositeBenchIndex=0;
m_numBenchProfiles=0;
m_currentTextureReduction=0;
m_reallyLowMHz = 400;
for (Int i=0; i<STATIC_GAME_LOD_CUSTOM; i++)
m_numLevelPresets[i]=0;
};
GameLODManager::~GameLODManager()
{
}
BenchProfile *GameLODManager::newBenchProfile(void)
{
if (m_numBenchProfiles < MAX_BENCH_PROFILES)
{
m_numBenchProfiles++;
return &m_benchProfiles[m_numBenchProfiles-1];
}
DEBUG_CRASH(( "GameLODManager::newBenchProfile - Too many profiles defined\n"));
return NULL;
}
LODPresetInfo *GameLODManager::newLODPreset(StaticGameLODLevel index)
{
if (m_numLevelPresets[index] < MAX_LOD_PRESETS_PER_LEVEL)
{
m_numLevelPresets[index]++;
return &m_lodPresets[index][m_numLevelPresets[index]-1];
}
DEBUG_CRASH(( "GameLODManager::newLODPreset - Too many presets defined for '%s'\n", TheGameLODManager->getStaticGameLODLevelName(index)));
return NULL;
}
void GameLODManager::init(void)
{
INI ini;
//Get Presets for each LOD level.
ini.load( AsciiString( "Data\\INI\\GameLOD.ini" ), INI_LOAD_OVERWRITE, NULL );
//Get presets for each known hardware configuration
ini.load( AsciiString( "Data\\INI\\GameLODPresets.ini"), INI_LOAD_OVERWRITE, NULL);
//Get Presets for custom LOD level by pulling them out of initial globaldata (which should
//have all settings already applied).
refreshCustomStaticLODLevel();
//Override with user preferences
OptionPreferences optionPref;
StaticGameLODLevel userSetDetail=(StaticGameLODLevel)optionPref.getStaticGameDetail();
m_idealDetailLevel=(StaticGameLODLevel)optionPref.getIdealStaticGameDetail();
//always get this data in case we need it later.
testMinimumRequirements(NULL,&m_cpuType,&m_cpuFreq,&m_numRAM,NULL,NULL,NULL);
if ((Real)(m_numRAM)/(Real)(256*1024*1024) >= PROFILE_ERROR_LIMIT)
m_memPassed=TRUE; //check if they have at least 256 MB
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN || TheGlobalData->m_forceBenchmark)
{
if (m_cpuType == XX || TheGlobalData->m_forceBenchmark)
{
//need to run the benchmark
testMinimumRequirements(NULL,NULL,NULL,NULL,&m_intBenchIndex,&m_floatBenchIndex,&m_memBenchIndex);
if (TheGlobalData->m_forceBenchmark)
{ //we want to see the numbers. So dump them to a logfile.
FILE *fp=fopen("Benchmark.txt","w");
if (fp)
{
fprintf(fp,"BenchProfile = %s %d %f %f %f", CPUNames[m_cpuType], m_cpuFreq, m_intBenchIndex, m_floatBenchIndex, m_memBenchIndex);
fclose(fp);
}
}
m_compositeBenchIndex = m_intBenchIndex + m_floatBenchIndex; ///@todo: Need to scale these based on our apps usage of int/float/mem ops.
StaticGameLODLevel currentLevel=STATIC_GAME_LOD_LOW;
BenchProfile *prof=m_benchProfiles;
m_cpuType = P3; //assume lowest spec.
m_cpuFreq = 1000; //assume lowest spec.
for (Int k=0; k<m_numBenchProfiles; k++)
{
//Check if we're within 5% of the performance of this cpu profile.
if (m_intBenchIndex/prof->m_intBenchIndex >= PROFILE_ERROR_LIMIT && m_floatBenchIndex/prof->m_floatBenchIndex >= PROFILE_ERROR_LIMIT && m_memBenchIndex/prof->m_memBenchIndex >= PROFILE_ERROR_LIMIT)
{
for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
{
LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
for (Int j=0; j<m_numLevelPresets[i]; j++)
{
if( prof->m_cpuType == preset->m_cpuType && ((Real)prof->m_mhz/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT))
{ currentLevel = (StaticGameLODLevel)i;
m_cpuType = prof->m_cpuType;
m_cpuFreq = prof->m_mhz;
break;
}
preset++; //skip to next preset
}
if (currentLevel >= i)
break; //we already found a higher level than the remaining presets so no need to keep searching.
}
}
prof++;
}
} //finding equivalent CPU to unkown cpu.
} //find data needed to determine m_idealDetailLevel
if (userSetDetail == STATIC_GAME_LOD_CUSTOM)
{
TheWritableGlobalData->m_textureReductionFactor = optionPref.getTextureReduction();
TheWritableGlobalData->m_useShadowVolumes = optionPref.get3DShadowsEnabled();
TheWritableGlobalData->m_useShadowDecals = optionPref.get2DShadowsEnabled();
TheWritableGlobalData->m_enableBehindBuildingMarkers = optionPref.getBuildingOcclusionEnabled();
TheWritableGlobalData->m_maxParticleCount = optionPref.getParticleCap();
TheWritableGlobalData->m_enableDynamicLOD = optionPref.getDynamicLODEnabled();
TheWritableGlobalData->m_useFpsLimit = optionPref.getFPSLimitEnabled();
TheWritableGlobalData->m_useLightMap = optionPref.getLightmapEnabled();
TheWritableGlobalData->m_useCloudMap = optionPref.getCloudShadowsEnabled();
TheWritableGlobalData->m_showSoftWaterEdge = optionPref.getSmoothWaterEnabled();
TheWritableGlobalData->m_useHeatEffects = optionPref.getUseHeatEffects();
TheWritableGlobalData->m_useDrawModuleLOD = optionPref.getExtraAnimationsDisabled();
TheWritableGlobalData->m_useTreeSway = !TheWritableGlobalData->m_useDrawModuleLOD; //borrow same setting.
TheWritableGlobalData->m_useTrees = optionPref.getTreesEnabled();
}
setStaticLODLevel(userSetDetail);
}
void GameLODManager::refreshCustomStaticLODLevel(void)
{
StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[STATIC_GAME_LOD_CUSTOM];
lodInfo->m_maxParticleCount=TheGlobalData->m_maxParticleCount;
lodInfo->m_useShadowVolumes=TheGlobalData->m_useShadowVolumes;
lodInfo->m_useShadowDecals=TheGlobalData->m_useShadowDecals;
lodInfo->m_useCloudMap=TheGlobalData->m_useCloudMap;
lodInfo->m_useLightMap=TheGlobalData->m_useLightMap;
lodInfo->m_showSoftWaterEdge=TheGlobalData->m_showSoftWaterEdge;
lodInfo->m_maxTankTrackEdges=TheGlobalData->m_maxTankTrackEdges;
lodInfo->m_maxTankTrackOpaqueEdges=TheGlobalData->m_maxTankTrackOpaqueEdges;
lodInfo->m_maxTankTrackFadeDelay=TheGlobalData->m_maxTankTrackFadeDelay;
lodInfo->m_useBuildupScaffolds=!TheGlobalData->m_useDrawModuleLOD;
lodInfo->m_useHeatEffects = TheGlobalData->m_useHeatEffects;
lodInfo->m_useTreeSway=lodInfo->m_useBuildupScaffolds;// Borrow same setting. //TheGlobalData->m_useTreeSway;
lodInfo->m_textureReduction=TheGlobalData->m_textureReductionFactor;
lodInfo->m_useFpsLimit = TheGlobalData->m_useFpsLimit;
lodInfo->m_enableDynamicLOD=TheGlobalData->m_enableDynamicLOD;
lodInfo->m_useTrees = TheGlobalData->m_useTrees;
}
/**Convert LOD name to an index*/
Int GameLODManager::getStaticGameLODIndex(AsciiString name)
{
for (Int i=0; i<STATIC_GAME_LOD_COUNT; ++i)
{
if (name.compareNoCase(StaticGameLODNames[i]) == 0)
return i;
}
DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
return STATIC_GAME_LOD_UNKNOWN;
}
/**Parse a description of all the LOD settings for a given detail level*/
void INI::parseStaticGameLODDefinition( INI* ini )
{
const char *c;
AsciiString name;
// read the name
c = ini->getNextToken();
name.set( c );
if( TheGameLODManager )
{
Int index = TheGameLODManager->getStaticGameLODIndex(name);
if (index != STATIC_GAME_LOD_UNKNOWN)
{
StaticGameLODInfo *lodInfo = &(TheGameLODManager->m_staticGameLODInfo[index]);
// parse the ini definition
ini->initFromINI( lodInfo, TheStaticGameLODFieldParseTable );
}
}
}
/**Parse an LOD level*/
void INI::parseStaticGameLODLevel( INI* ini, void * , void *store, const void*)
{
const char *tok=ini->getNextToken();
for (Int i=0; i<STATIC_GAME_LOD_COUNT; i++)
if( stricmp(tok, StaticGameLODNames[i]) == 0 )
{ *(StaticGameLODLevel*)store = (StaticGameLODLevel)i;
return;
}
DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
throw INI_INVALID_DATA;
}
const char *GameLODManager::getStaticGameLODLevelName(StaticGameLODLevel level)
{
return StaticGameLODNames[level];
}
/**Function which calculates the recommended LOD level for current hardware
configuration.*/
StaticGameLODLevel GameLODManager::findStaticLODLevel(void)
{
//Check if we have never done the test on current system
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
{
//search all our presets for matching hardware
m_idealDetailLevel = STATIC_GAME_LOD_LOW;
//get system configuration - only need vide chip type, got rest in ::init().
testMinimumRequirements(&m_videoChipType,NULL,NULL,NULL,NULL,NULL,NULL);
if (m_videoChipType == DC_UNKNOWN)
m_videoChipType = DC_TNT2; //presume it's at least TNT2 level
Int numMBRam=m_numRAM/(1024*1024);
for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
{
LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
for (Int j=0; j<m_numLevelPresets[i]; j++)
{
if( m_cpuType == preset->m_cpuType &&
((Real)m_cpuFreq/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT) &&//make sure we're within 5% or higher
m_videoChipType >= preset->m_videoType &&
((Real)numMBRam/(Real)preset->m_memory >= PROFILE_ERROR_LIMIT)
)
{ m_idealDetailLevel = (StaticGameLODLevel)i;
break;
}
preset++; //skip to next preset
}
if (m_idealDetailLevel >= i)
break; //we already found a higher level than the remaining presets so no need to keep searching.
}
//Save ideal detail level for future usage
OptionPreferences optionPref;
optionPref["IdealStaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
if (getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) //save for future usage.
optionPref["StaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
optionPref.write();
}
return m_idealDetailLevel;
}
/**Set all game systems to match the desired LOD level.*/
Bool GameLODManager::setStaticLODLevel(StaticGameLODLevel level)
{
if (!TheGlobalData->m_enableStaticLOD)
{ m_currentStaticLOD = STATIC_GAME_LOD_CUSTOM;
return FALSE;
}
if (level == STATIC_GAME_LOD_UNKNOWN || (level != STATIC_GAME_LOD_CUSTOM && m_currentStaticLOD == level))
return FALSE; //level is already applied. Custom levels are always applied since random options could change.
applyStaticLODLevel(level);
m_currentStaticLOD = level;
return TRUE;
}
void GameLODManager::applyStaticLODLevel(StaticGameLODLevel level)
{
///@todo: Still need to implement these settings:
// m_sampleCount2D=6;
// m_sampleCount3D=24;
// m_streamCount=2;
// m_useEmissiveNightMaterials=TRUE;
//save previous info for this level since it may be overwritten by refreshCustomStaticLODLevel().
StaticGameLODInfo prevLodBackup;
if (m_currentStaticLOD != STATIC_GAME_LOD_UNKNOWN)
prevLodBackup=m_staticGameLODInfo[m_currentStaticLOD];
if (level == STATIC_GAME_LOD_CUSTOM)
refreshCustomStaticLODLevel(); //store current settings into custom preset
StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[level];
StaticGameLODInfo *prevLodInfo=&prevLodBackup;
Int requestedTextureReduction = 0;
Bool requestedTrees = m_memPassed; //only use trees if memory requirement passed.
if (level == STATIC_GAME_LOD_CUSTOM)
{ requestedTextureReduction = lodInfo->m_textureReduction;
requestedTrees = lodInfo->m_useTrees;
}
else
if (level >= STATIC_GAME_LOD_LOW)
{ //normal non-custom level gets texture reduction based on recommendation
requestedTextureReduction = getRecommendedTextureReduction();
}
if (TheGlobalData)
{
TheWritableGlobalData->m_maxParticleCount=lodInfo->m_maxParticleCount;
TheWritableGlobalData->m_useShadowVolumes=lodInfo->m_useShadowVolumes;
TheWritableGlobalData->m_useShadowDecals=lodInfo->m_useShadowDecals;
//Check if texture resolution changed. No need to apply when current is unknown because display will do it
if (requestedTextureReduction != m_currentTextureReduction)
{
TheWritableGlobalData->m_textureReductionFactor = requestedTextureReduction;
if (TheGameClient)
TheGameClient->adjustLOD(0); //apply the new setting stored in globaldata
}
//Check if shadow state changed
if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN ||
lodInfo->m_useShadowVolumes != prevLodInfo->m_useShadowVolumes ||
lodInfo->m_useShadowDecals != prevLodInfo->m_useShadowDecals)
{
if (TheGameClient)
{
TheGameClient->releaseShadows(); //free all shadows
TheGameClient->allocateShadows(); //allocate those shadows that are enabled.
}
}
TheWritableGlobalData->m_useCloudMap=lodInfo->m_useCloudMap;
TheWritableGlobalData->m_useLightMap=lodInfo->m_useLightMap;
TheWritableGlobalData->m_showSoftWaterEdge=lodInfo->m_showSoftWaterEdge;
//Check if shoreline blending mode has changed
if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN || lodInfo->m_showSoftWaterEdge != prevLodInfo->m_showSoftWaterEdge)
{
if (TheTerrainVisual)
TheTerrainVisual->setShoreLineDetail();
}
TheWritableGlobalData->m_maxTankTrackEdges=lodInfo->m_maxTankTrackEdges;
TheWritableGlobalData->m_maxTankTrackOpaqueEdges=lodInfo->m_maxTankTrackOpaqueEdges;
TheWritableGlobalData->m_maxTankTrackFadeDelay=lodInfo->m_maxTankTrackFadeDelay;
TheWritableGlobalData->m_useTreeSway=lodInfo->m_useTreeSway;
TheWritableGlobalData->m_useDrawModuleLOD=!lodInfo->m_useBuildupScaffolds;
TheWritableGlobalData->m_useHeatEffects=lodInfo->m_useHeatEffects;
TheWritableGlobalData->m_enableDynamicLOD = lodInfo->m_enableDynamicLOD;
TheWritableGlobalData->m_useFpsLimit = lodInfo->m_useFpsLimit;
TheWritableGlobalData->m_useTrees = requestedTrees;
}
if (!m_memPassed || isReallyLowMHz()) {
TheWritableGlobalData->m_shellMapOn = false;
}
if (TheTerrainVisual)
TheTerrainVisual->setTerrainTracksDetail();
}
/**Parse a description of all the LOD settings for a given detail level*/
void INI::parseDynamicGameLODDefinition( INI* ini )
{
const char *c;
AsciiString name;
// read the name
c = ini->getNextToken();
name.set( c );
if( TheGameLODManager )
{
Int index = TheGameLODManager->getDynamicGameLODIndex(name);
if (index != DYNAMIC_GAME_LOD_UNKNOWN)
{
DynamicGameLODInfo *lodInfo = &(TheGameLODManager->m_dynamicGameLODInfo[index]);
// parse the ini weapon definition
ini->initFromINI( lodInfo, TheDynamicGameLODFieldParseTable );
}
}
}
/**Parse an LOD level*/
void INI::parseDynamicGameLODLevel( INI* ini, void * , void *store, const void*)
{
const char *tok=ini->getNextToken();
for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; i++)
if( stricmp(tok, DynamicGameLODNames[i]) == 0 )
{ *(DynamicGameLODLevel*)store = (DynamicGameLODLevel)i;
return;
}
DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
throw INI_INVALID_DATA;
}
/**Convert LOD name to an index*/
Int GameLODManager::getDynamicGameLODIndex(AsciiString name)
{
for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; ++i)
{
if (name.compareNoCase(DynamicGameLODNames[i]) == 0)
return i;
}
DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
return STATIC_GAME_LOD_UNKNOWN;
}
const char *GameLODManager::getDynamicGameLODLevelName(DynamicGameLODLevel level)
{
return DynamicGameLODNames[level];
}
/**Given an average fps, return the optimal dynamic LOD level that matches this fps.*/
DynamicGameLODLevel GameLODManager::findDynamicLODLevel(Real averageFPS)
{
Int ifps=(Int)(averageFPS); //convert to integer.
for (Int i=DYNAMIC_GAME_LOD_VERY_HIGH; i>=DYNAMIC_GAME_LOD_LOW; i--)
{ //check which of the LOD levels matches our fps
if (m_dynamicGameLODInfo[i].m_minFPS < ifps)
return (DynamicGameLODLevel)i;
}
return DYNAMIC_GAME_LOD_LOW; //none of the low levels were slow enough so pick the lowest.
}
/**Set all game systems to match the desired LOD level.*/
Bool GameLODManager::setDynamicLODLevel(DynamicGameLODLevel level)
{
if (level == DYNAMIC_GAME_LOD_UNKNOWN || m_currentDynamicLOD == level)
return FALSE;
m_currentDynamicLOD = level;
applyDynamicLODLevel(level);
return TRUE;
}
void GameLODManager::applyDynamicLODLevel(DynamicGameLODLevel level)
{
m_numParticleGenerations=0;
m_dynamicParticleSkipMask=m_dynamicGameLODInfo[level].m_dynamicParticleSkipMask;
m_numDebrisGenerations=0;
m_dynamicDebrisSkipMask=m_dynamicGameLODInfo[level].m_dynamicDebrisSkipMask;
m_slowDeathScale=m_dynamicGameLODInfo[level].m_slowDeathScale;
m_minDynamicParticlePriority=m_dynamicGameLODInfo[level].m_minDynamicParticlePriority;
m_minDynamicParticleSkipPriority=m_dynamicGameLODInfo[level].m_minDynamicParticleSkipPriority;
}
Int GameLODManager::getRecommendedTextureReduction(void)
{
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
findStaticLODLevel(); //it was never tested, so test now.
if (!m_memPassed) //if they have < 256 MB, force them to low res textures.
return m_staticGameLODInfo[STATIC_GAME_LOD_LOW].m_textureReduction;
return m_staticGameLODInfo[m_idealDetailLevel].m_textureReduction;
}
Int GameLODManager::getLevelTextureReduction(StaticGameLODLevel level)
{
return m_staticGameLODInfo[level].m_textureReduction;
}
Bool GameLODManager::didMemPass( void )
{
return m_memPassed;
}

View File

@@ -0,0 +1,51 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameMain.cpp
// The main entry point for the game
// Author: Michael S. Booth, April 2001
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/GameEngine.h"
/**
* This is the entry point for the game system.
*/
void GameMain( int argc, char *argv[] )
{
// initialize the game engine using factory function
TheGameEngine = CreateGameEngine();
TheGameEngine->init(argc, argv);
// run it
TheGameEngine->execute();
// since execute() returned, we are exiting the game
delete TheGameEngine;
TheGameEngine = NULL;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIAiData.cpp //////////////////////////////////////////////////////////////////////////
// Author: John Ahlquist, March 2002
// Desc: Parsing AIData INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameLogic/AI.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse GameData entry */
//-------------------------------------------------------------------------------------------------
void INI::parseAIDataDefinition( INI* ini )
{
AI::parseAiDataDefinition(ini);
}

View File

@@ -0,0 +1,88 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIAnimation.cpp /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, July 2002
// Desc: Parsing animation INI entries for 2D image animations
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/Anim2D.h"
//-------------------------------------------------------------------------------------------------
/** Parse animation entry */
//-------------------------------------------------------------------------------------------------
void INI::parseAnim2DDefinition( INI* ini )
{
AsciiString name;
Anim2DTemplate *animTemplate;
// read the name
const char* c = ini->getNextToken();
name.set( c );
//
// find existing item if present, note that we do not support overrides
// in the animations like we do in systems that are more "design" oriented, images
// are assets as they are
//
if( !TheAnim2DCollection )
{
//We don't need it if we're in the builder... which doesn't have this.
return;
} // end if
// find existing animation template if present
animTemplate = TheAnim2DCollection->findTemplate( name );
if( animTemplate == NULL )
{
// item not found, create a new one
animTemplate = TheAnim2DCollection->newTemplate( name );
DEBUG_ASSERTCRASH( animTemplate, ("INI""parseAnim2DDefinition - unable to allocate animation template for '%s'\n",
name.str()) );
} // end if
else
{
// we're loading over an existing animation template ... something is probably wrong
DEBUG_CRASH(( "INI::parseAnim2DDefinition - Animation template '%s' already exists\n",
animTemplate->getName().str() ));
return;
} // end else
// parse the ini definition
ini->initFromINI( animTemplate, animTemplate->getFieldParse() );
} // end parseAnim2DDefinition

View File

@@ -0,0 +1,215 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIAudioEventInfo.cpp ////////////////////////////////////////////////////////////////////////
// Author: Colin Day, July 2002
// Desc: Parsing AudioEvent, MusicTrack and DialogEvent INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/GameAudio.h"
#include "Common/AudioEventInfo.h"
AudioEventInfo::~AudioEventInfo()
{
}
// @todo: Get these functions unified.
//-------------------------------------------------------------------------------------------------
void INI::parseMusicTrackDefinition( INI* ini )
{
AsciiString name;
AudioEventInfo *track;
// read the name
const char* c = ini->getNextToken();
name.set( c );
track = TheAudio->newAudioEventInfo( name );
if (!track) {
return;
}
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultMusicTrack");
if (defaultInfo) {
(*track) = (*defaultInfo);
TheAudio->addTrackName( name );
}
track->m_audioName = name;
track->m_soundType = AT_Music;
// parse the ini definition
ini->initFromINI( track, track->getFieldParse() );
} // end parseMusicTrackDefinition
//-------------------------------------------------------------------------------------------------
void INI::parseAudioEventDefinition( INI* ini )
{
AsciiString name;
AudioEventInfo *track;
// read the name
const char* c = ini->getNextToken();
name.set( c );
track = TheAudio->newAudioEventInfo( name );
if (!track) {
return;
}
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultSoundEffect");
if (defaultInfo) {
(*track) = (*defaultInfo);
}
track->m_audioName = name;
track->m_soundType = AT_SoundEffect;
// parse the ini definition
ini->initFromINI( track, track->getFieldParse() );
} // end parseAudioEventDefinition
//-------------------------------------------------------------------------------------------------
void INI::parseDialogDefinition( INI* ini )
{
AsciiString name;
AudioEventInfo *track;
// read the name
const char* c = ini->getNextToken();
name.set( c );
track = TheAudio->newAudioEventInfo( name );
if (!track) {
return;
}
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultDialog");
if (defaultInfo) {
(*track) = (*defaultInfo);
}
track->m_audioName = name;
track->m_soundType = AT_Streaming;
// parse the ini definition
ini->initFromINI( track, track->getFieldParse() );
} // end parseAudioEventDefinition
//-------------------------------------------------------------------------------------------------
static void parseDelay( INI* ini, void *instance, void *store, const void* /*userData*/ );
static void parsePitchShift( INI* ini, void *instance, void *store, const void* /*userData*/ );
//-------------------------------------------------------------------------------------------------
const FieldParse AudioEventInfo::m_audioEventInfo[] =
{
{ "Filename", INI::parseAsciiString, NULL, offsetof( AudioEventInfo, m_filename) },
{ "Volume", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_volume ) },
{ "VolumeShift", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_volumeShift ) },
{ "MinVolume", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_minVolume ) },
{ "PitchShift", parsePitchShift, NULL, 0 },
{ "Delay", parseDelay, NULL, 0 },
{ "Limit", INI::parseInt, NULL, offsetof( AudioEventInfo, m_limit ) },
{ "LoopCount", INI::parseInt, NULL, offsetof( AudioEventInfo, m_loopCount ) },
{ "Priority", INI::parseIndexList, theAudioPriorityNames, offsetof( AudioEventInfo, m_priority ) },
{ "Type", INI::parseBitString32, theSoundTypeNames, offsetof( AudioEventInfo, m_type ) },
{ "Control", INI::parseBitString32, theAudioControlNames, offsetof( AudioEventInfo, m_control ) },
{ "Sounds", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_sounds ) },
{ "SoundsNight", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsNight ) },
{ "SoundsEvening", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsEvening ) },
{ "SoundsMorning", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsMorning ) },
{ "Attack", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_attackSounds ) },
{ "Decay", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_decaySounds ) },
{ "MinRange", INI::parseReal, NULL, offsetof( AudioEventInfo, m_minDistance) },
{ "MaxRange", INI::parseReal, NULL, offsetof( AudioEventInfo, m_maxDistance) },
{ "LowPassCutoff", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_lowPassFreq) },
};
//-------------------------------------------------------------------------------------------------
static void parseDelay( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
AudioEventInfo *attribs = (AudioEventInfo*) store;
Int min = ini->scanInt(ini->getNextToken());
Int max = ini->scanInt(ini->getNextToken());
DEBUG_ASSERTCRASH( min >= 0 && max >= min, ("Bad delay values for audio event %s", attribs->m_audioName.str()));
attribs->m_delayMax = max;
attribs->m_delayMin = min;
}
//-------------------------------------------------------------------------------------------------
static void parsePitchShift( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
AudioEventInfo *attribs = (AudioEventInfo*) store;
Real min = ini->scanReal(ini->getNextToken());
Real max = ini->scanReal(ini->getNextToken());
DEBUG_ASSERTCRASH( min > -100 && max >= min, ("Bad pitch shift values for audio event %s", attribs->m_audioName.str()));
attribs->m_pitchShiftMin = 1.0f + min/100;
attribs->m_pitchShiftMax = 1.0f + max/100;
}
// STATIC DEFINIITIONS ////////////////////////////////////////////////////////////////////////////
char *theAudioPriorityNames[] =
{
"LOWEST",
"LOW",
"NORMAL",
"HIGH",
"CRITICAL",
NULL
};
char *theSoundTypeNames[] =
{
"UI",
"WORLD",
"SHROUDED",
"GLOBAL",
"VOICE",
"PLAYER",
"ALLIES",
"ENEMIES",
"EVERYONE",
NULL
};
char *theAudioControlNames[] =
{
"LOOP",
"RANDOM",
"ALL",
"POSTDELAY",
"INTERRUPT",
NULL
};

View File

@@ -0,0 +1,94 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INICommandButton.cpp /////////////////////////////////////////////////////////////////////
// Author: Colin Day, March 2002
// Desc: Command buttons are the atomic units we can configure into command sets to then
// display in the context sensitive user interface
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/SpecialPower.h"
#include "GameClient/ControlBar.h"
//-------------------------------------------------------------------------------------------------
/** Parse a command button */
//-------------------------------------------------------------------------------------------------
void INI::parseCommandButtonDefinition( INI *ini )
{
ControlBar::parseCommandButtonDefinition(ini);
}
//-------------------------------------------------------------------------------------------------
/** Parse a command button */
//-------------------------------------------------------------------------------------------------
void ControlBar::parseCommandButtonDefinition( INI *ini )
{
// read the name
AsciiString name = ini->getNextToken();
// find existing item if present
CommandButton *button = TheControlBar->findNonConstCommandButton( name );
if( button == NULL )
{
// allocate a new item
button = TheControlBar->newCommandButton( name );
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
{
button->markAsOverride();
}
} // end if
else if( ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES )
{
DEBUG_CRASH(( "[LINE: %d in '%s'] Duplicate commandbutton %s found!", ini->getLineNum(), ini->getFilename().str(), name.str() ));
}
else
{
button = TheControlBar->newCommandButtonOverride( button );
}
// parse the ini definition
ini->initFromINI( button, button->getFieldParse() );
//Make sure buttons with special power templates also have the appropriate option set.
const SpecialPowerTemplate *spTemplate = button->getSpecialPowerTemplate();
Bool needsTemplate = BitTest( button->getOptions(), NEED_SPECIAL_POWER_SCIENCE );
if( spTemplate && !needsTemplate )
{
DEBUG_CRASH( ("[LINE: %d in '%s'] CommandButton %s has SpecialPower = %s but the button also requires Options = NEED_SPECIAL_POWER_SCIENCE. Failure to do so will cause bugs such as invisible side shortcut buttons",
ini->getLineNum(), ini->getFilename().str(), name.str(), spTemplate->getName().str() ) );
}
else if( !spTemplate && needsTemplate )
{
DEBUG_CRASH( ("[LINE: %d in '%s'] CommandButton %s has Options = NEED_SPECIAL_POWER_SCIENCE but doesn't specify a SpecialPower = xxxx. Please evaluate INI.",
ini->getLineNum(), ini->getFilename().str(), name.str() ) );
}
} // end parseCommandButtonDefinition

View File

@@ -0,0 +1,43 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INICommandSet.cpp ////////////////////////////////////////////////////////////////////////
// Author: Colin Day, March 2002
// Desc: Command sets are a configurable set of CommandButtons, we will use the sets as
// part of the context sensitive user interface
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/ControlBar.h"
//-------------------------------------------------------------------------------------------------
/** Parse a command set */
//-------------------------------------------------------------------------------------------------
void INI::parseCommandSetDefinition( INI *ini )
{
ControlBar::parseCommandSetDefinition(ini);
} // end parseCommandSetDefinition

View File

@@ -0,0 +1,101 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIControlBarScheme.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// created: Apr 2002
//
// Filename: INIControlBarScheme.cpp
//
// author: Chris Huybregts
//
// purpose: Parse a control Bar Scheme
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/ControlBar.h"
#include "GameClient/ControlBarScheme.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/** Parse a ControlBarScheme button */
//-------------------------------------------------------------------------------------------------
void INI::parseControlBarSchemeDefinition( INI *ini )
{
AsciiString name;
ControlBarSchemeManager *CBSchemeManager;
ControlBarScheme *CBScheme;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present
CBSchemeManager = TheControlBar->getControlBarSchemeManager();
DEBUG_ASSERTCRASH( CBSchemeManager, ("parseControlBarSchemeDefinition: Unable to Get CBSchemeManager\n") );
if( !CBSchemeManager )
return;
// If we have a previously allocated control bar, this will return a cleared out pointer to it so we
// can overwrite it
CBScheme = CBSchemeManager->newControlBarScheme( name );
// sanity
DEBUG_ASSERTCRASH( CBScheme, ("parseControlBarSchemeDefinition: Unable to allocate Scheme '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( CBScheme, CBSchemeManager->getFieldParse() );
} // end parseCommandButtonDefinition

View File

@@ -0,0 +1,46 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INICrate.cpp /////////////////////////////////////////////////////////////////////////////////
// Author: Graham Smallwood Feb 2002
// Desc: Just passes the parse to the CrateSystem
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameLogic/CrateSystem.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse Weapon entry */
//-------------------------------------------------------------------------------------------------
void INI::parseCrateTemplateDefinition( INI* ini )
{
CrateSystem::parseCrateTemplateDefinition(ini);
}

View File

@@ -0,0 +1,44 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIDamageFX.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, November 2001
// Desc: Parsing DamageFX INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/DamageFX.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
/*static */ void INI::parseDamageFXDefinition(INI* ini)
{
DamageFXStore::parseDamageFXDefinition(ini);
}

View File

@@ -0,0 +1,91 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIDrawGroupInfo.cpp /////////////////////////////////////////////////////////////////////
// Author: John McDonald, October 2002
// Desc: Parsing DrawGroupInfo INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/DrawGroupInfo.h"
void parseInt( INI* ini, void * /*instance*/, void *store, const void* userData )
{
DrawGroupInfo *dgi = (DrawGroupInfo*) store;
if (userData == 0) {
store = &dgi->m_pixelOffsetX;
dgi->m_usingPixelOffsetX = TRUE;
} else {
store = &dgi->m_pixelOffsetY;
dgi->m_usingPixelOffsetY = TRUE;
}
INI::parseInt(ini, NULL, store, NULL);
}
void parsePercentToReal( INI* ini, void * /*instance*/, void *store, const void* userData )
{
DrawGroupInfo *dgi = (DrawGroupInfo*) store;
if (userData == 0) {
store = &dgi->m_pixelOffsetX;
dgi->m_usingPixelOffsetX = FALSE;
} else {
store = &dgi->m_pixelOffsetY;
dgi->m_usingPixelOffsetY = FALSE;
}
INI::parsePercentToReal(ini, NULL, store, NULL);
}
const FieldParse DrawGroupInfo::s_fieldParseTable[] =
{
{ "UsePlayerColor", INI::parseBool, NULL, offsetof( DrawGroupInfo, m_usePlayerColor) },
{ "ColorForText", INI::parseColorInt, NULL, offsetof( DrawGroupInfo, m_colorForText ) },
{ "ColorForTextDropShadow", INI::parseColorInt, NULL, offsetof( DrawGroupInfo, m_colorForTextDropShadow ) },
{ "FontName", INI::parseQuotedAsciiString, NULL, offsetof( DrawGroupInfo, m_fontName ) },
{ "FontSize", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_fontSize ) },
{ "FontIsBold", INI::parseBool, NULL, offsetof( DrawGroupInfo, m_fontIsBold ) },
{ "DropShadowOffsetX", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_dropShadowOffsetX) },
{ "DropShadowOffsetY", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_dropShadowOffsetY) },
{ "DrawPositionXPixel", parseInt, (void*)0, 0 },
{ "DrawPositionXPercent", parsePercentToReal, (void*)0, 0 },
{ "DrawPositionYPixel", parseInt, (void*)1, 0 },
{ "DrawPositionYPercent", parsePercentToReal, (void*)1, 0 },
{ 0, 0, 0, 0 }
};
/*static */ void INI::parseDrawGroupNumberDefinition(INI* ini)
{
if (!TheDrawGroupInfo) {
throw INI_UNKNOWN_ERROR;
}
ini->initFromINI(TheDrawGroupInfo, TheDrawGroupInfo->getFieldParse());
}

View File

@@ -0,0 +1,52 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIGameData.cpp //////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Parsing GameData INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/GlobalData.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse GameData entry */
//-------------------------------------------------------------------------------------------------
void INI::parseGameDataDefinition( INI* ini )
{
GlobalData::parseGameDataDefinition(ini);
}

View File

@@ -0,0 +1,206 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIMapCache.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Matthew D. Campbell, February 2002
// Desc: Parsing MapCache INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Lib/BaseType.h"
#include "Common/INI.h"
#include "GameClient/MapUtil.h"
#include "GameClient/GameText.h"
#include "GameNetwork/NetworkDefs.h"
#include "Common/NameKeyGenerator.h"
#include "Common/WellKnownKeys.h"
#include "Common/QuotedPrintable.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
class MapMetaDataReader
{
public:
Region3D m_extent;
Int m_numPlayers;
Bool m_isMultiplayer;
AsciiString m_asciiDisplayName;
AsciiString m_asciiNameLookupTag;
Bool m_isOfficial;
WinTimeStamp m_timestamp;
UnsignedInt m_filesize;
UnsignedInt m_CRC;
Coord3D m_waypoints[MAX_SLOTS];
Coord3D m_initialCameraPosition;
Coord3DList m_supplyPositions;
Coord3DList m_techPositions;
static const FieldParse m_mapFieldParseTable[]; ///< the parse table for INI definition
const FieldParse *getFieldParse( void ) const { return m_mapFieldParseTable; }
};
void parseSupplyPositionCoord3D( INI* ini, void * instance, void * /*store*/, const void* /*userData*/ )
{
MapMetaDataReader *mmdr = (MapMetaDataReader *)instance;
Coord3D coord3d;
INI::parseCoord3D(ini, NULL, &coord3d,NULL );
mmdr->m_supplyPositions.push_front(coord3d);
}
void parseTechPositionsCoord3D( INI* ini, void * instance, void * /*store*/, const void* /*userData*/ )
{
MapMetaDataReader *mmdr = (MapMetaDataReader *)instance;
Coord3D coord3d;
INI::parseCoord3D(ini, NULL, &coord3d,NULL );
mmdr->m_techPositions.push_front(coord3d);
}
const FieldParse MapMetaDataReader::m_mapFieldParseTable[] =
{
{ "isOfficial", INI::parseBool, NULL, offsetof( MapMetaDataReader, m_isOfficial ) },
{ "isMultiplayer", INI::parseBool, NULL, offsetof( MapMetaDataReader, m_isMultiplayer ) },
{ "extentMin", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_extent.lo ) },
{ "extentMax", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_extent.hi ) },
{ "numPlayers", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_numPlayers ) },
{ "fileSize", INI::parseUnsignedInt, NULL, offsetof( MapMetaDataReader, m_filesize ) },
{ "fileCRC", INI::parseUnsignedInt, NULL, offsetof( MapMetaDataReader, m_CRC ) },
{ "timestampLo", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_lowTimeStamp ) },
{ "timestampHi", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_highTimeStamp ) },
{ "displayName", INI::parseAsciiString, NULL, offsetof( MapMetaDataReader, m_asciiDisplayName ) },
{ "nameLookupTag", INI::parseAsciiString, NULL, offsetof( MapMetaDataReader, m_asciiNameLookupTag ) },
{ "supplyPosition", parseSupplyPositionCoord3D, NULL, NULL },
{ "techPosition", parseTechPositionsCoord3D, NULL, NULL },
{ "Player_1_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) },
{ "Player_2_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 1 },
{ "Player_3_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 2 },
{ "Player_4_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 3 },
{ "Player_5_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 4 },
{ "Player_6_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 5 },
{ "Player_7_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 6 },
{ "Player_8_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 7 },
{ "InitialCameraPosition", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_initialCameraPosition ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
void INI::parseMapCacheDefinition( INI* ini )
{
const char *c;
AsciiString name;
MapMetaDataReader mdr;
MapMetaData md;
// read the name
c = ini->getNextToken(" \n\r\t");
name.set( c );
name = QuotedPrintableToAsciiString(name);
md.m_waypoints.clear();
ini->initFromINI( &mdr, mdr.getFieldParse() );
md.m_extent = mdr.m_extent;
md.m_isOfficial = mdr.m_isOfficial != 0;
md.m_isMultiplayer = mdr.m_isMultiplayer != 0;
md.m_numPlayers = mdr.m_numPlayers;
md.m_filesize = mdr.m_filesize;
md.m_CRC = mdr.m_CRC;
md.m_timestamp = mdr.m_timestamp;
md.m_waypoints[TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition)] = mdr.m_initialCameraPosition;
// md.m_displayName = QuotedPrintableToUnicodeString(mdr.m_asciiDisplayName);
// this string is never to be used, but we'll leave it in to allow people with an old mapcache.ini to parse it
md.m_nameLookupTag = QuotedPrintableToAsciiString(mdr.m_asciiNameLookupTag);
if (md.m_nameLookupTag.isEmpty())
{
// maps without localized name tags
AsciiString tempdisplayname;
tempdisplayname = name.reverseFind('\\') + 1;
md.m_displayName.translate(tempdisplayname);
if (md.m_numPlayers >= 2)
{
UnicodeString extension;
extension.format(L" (%d)", md.m_numPlayers);
md.m_displayName.concat(extension);
}
}
else
{
// official maps with name tags
md.m_displayName = TheGameText->fetch(md.m_nameLookupTag);
if (md.m_numPlayers >= 2)
{
UnicodeString extension;
extension.format(L" (%d)", md.m_numPlayers);
md.m_displayName.concat(extension);
}
}
AsciiString startingCamName;
for (Int i=0; i<md.m_numPlayers; ++i)
{
startingCamName.format("Player_%d_Start", i+1); // start pos waypoints are 1-based
md.m_waypoints[startingCamName] = mdr.m_waypoints[i];
}
Coord3DList::iterator it = mdr.m_supplyPositions.begin();
while( it != mdr.m_supplyPositions.end())
{
md.m_supplyPositions.push_front(*it);
it++;
}
it = mdr.m_techPositions.begin();
while( it != mdr.m_techPositions.end())
{
md.m_techPositions.push_front(*it);
it++;
}
if(TheMapCache && !md.m_displayName.isEmpty())
{
AsciiString lowerName = name;
lowerName.toLower();
md.m_fileName = lowerName;
// DEBUG_LOG(("INI::parseMapCacheDefinition - adding %s to map cache\n", lowerName.str()));
(*TheMapCache)[lowerName] = md;
}
}

View File

@@ -0,0 +1,50 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIMapData.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Parsing MapData INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse MapData entry */
//-------------------------------------------------------------------------------------------------
void INI::parseMapDataDefinition( INI* ini )
{
} // end parseMapData

View File

@@ -0,0 +1,80 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIMappedImage.cpp ///////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Mapped image INI parsing
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/Image.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse mapped image entry */
//-------------------------------------------------------------------------------------------------
void INI::parseMappedImageDefinition( INI* ini )
{
AsciiString name;
// read the name
const char* c = ini->getNextToken();
name.set( c );
//
// find existing item if present, note that we do not support overrides
// in the images like we do in systems that are more "design" oriented, images
// are assets as they are
//
if( !TheMappedImageCollection )
{
//We don't need it if we're in the builder... which doesn't have this.
return;
}
Image *image = const_cast<Image*>(TheMappedImageCollection->findImageByName( name ));
if(image)
DEBUG_ASSERTCRASH(!image->getRawTextureData(), ("We are trying to parse over an existing image that contains a non-null rawTextureData, you should fix that"));
if( image == NULL )
{
// image not found, create a new one
image = newInstance(Image);
image->setName( name );
TheMappedImageCollection->addImage(image);
DEBUG_ASSERTCRASH( image, ("parseMappedImage: unable to allocate image for '%s'\n",
name.str()) );
} // end if
// parse the ini definition
ini->initFromINI( image, image->getFieldParse());
} // end parseMappedImage

View File

@@ -0,0 +1,78 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/MiscAudio.h"
#include "Common/INI.h"
const FieldParse MiscAudio::m_fieldParseTable[] =
{
{ "RadarNotifyUnitUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarUnitUnderAttackSound ) },
{ "RadarNotifyHarvesterUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarHarvesterUnderAttackSound ) },
{ "RadarNotifyStructureUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarStructureUnderAttackSound ) },
{ "RadarNotifyUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarUnderAttackSound ) },
{ "RadarNotifyInfiltrationSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarInfiltrationSound ) },
{ "RadarNotifyOnlineSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarOnlineSound ) },
{ "RadarNotifyOfflineSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarOfflineSound ) },
{ "DefectorTimerTickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_defectorTimerTickSound ) },
{ "DefectorTimerDingSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_defectorTimerDingSound ) },
{ "LockonTickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_lockonTickSound ) },
{ "AllCheerSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_allCheerSound ) },
{ "BattleCrySound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_battleCrySound ) },
{ "GUIClickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_guiClickSound ) },
{ "NoCanDoSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_noCanDoSound ) },
{ "StealthDiscoveredSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_stealthDiscoveredSound ) },
{ "StealthNeutralizedSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_stealthNeutralizedSound ) },
{ "MoneyDepositSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_moneyDepositSound ) },
{ "MoneyWithdrawSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_moneyWithdrawSound ) },
{ "BuildingDisabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_buildingDisabled ) },
{ "BuildingReenabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_buildingReenabled ) },
{ "VehicleDisabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_vehicleDisabled ) },
{ "VehicleReenabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_vehicleReenabled ) },
{ "SplatterVehiclePilotsBrain", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_splatterVehiclePilotsBrain ) },
{ "TerroristInCarMoveVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarMoveVoice ) },
{ "TerroristInCarAttackVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarAttackVoice ) },
{ "TerroristInCarSelectVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarSelectVoice ) },
{ "CrateHeal", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateHeal ) },
{ "CrateShroud", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateShroud ) },
{ "CrateSalvage", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateSalvage ) },
{ "CrateFreeUnit", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateFreeUnit ) },
{ "CrateMoney", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateMoney ) },
{ "UnitPromoted", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_unitPromoted ) },
{ "RepairSparks", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_repairSparks ) },
{ "SabotageShutDownBuilding", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_sabotageShutDownBuilding ) },
{ "SabotageResetTimeBuilding", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_sabotageResetTimerBuilding ) },
{ "AircraftWheelScreech", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_aircraftWheelScreech ) },
{ 0, 0, 0, 0 }
};
//-------------------------------------------------------------------------------------------------
void INI::parseMiscAudio( INI *ini )
{
ini->initFromINI(TheAudio->friend_getMiscAudio(), MiscAudio::m_fieldParseTable);
}

View File

@@ -0,0 +1,42 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIModel.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Parsing Model INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,109 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIMultiplayer.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Matthew D. Campbell, January 2002
// Desc: Parsing MultiplayerSettings and MultiplayerColor INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/MultiplayerSettings.h"
void INI::parseMultiplayerSettingsDefinition( INI* ini )
{
if( TheMultiplayerSettings )
{
//
// if the type of loading we're doing creates override data, we need to
// be loading into a new override item
//
if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
{
DEBUG_ASSERTCRASH(false, ("Creating an override of MultiplayerSettings!"));
}
} // end if
else
{
// we don't have any multiplayer settings instance at all yet, create one
TheMultiplayerSettings = NEW MultiplayerSettings;
} // end else
// parse the ini definition
ini->initFromINI( TheMultiplayerSettings, TheMultiplayerSettings->getFieldParse() );
}
void INI::parseMultiplayerColorDefinition( INI* ini )
{
const char *c;
AsciiString name;
MultiplayerColorDefinition *multiplayerColorDefinition;
// read the name
c = ini->getNextToken();
name.set( c );
// find existing item if present, but this type does not allow overrides,
//so if it exists just overwrite it.
multiplayerColorDefinition = TheMultiplayerSettings->findMultiplayerColorDefinitionByName( name );
if( multiplayerColorDefinition == NULL )
multiplayerColorDefinition = TheMultiplayerSettings->newMultiplayerColorDefinition( name );
ini->initFromINI( multiplayerColorDefinition, multiplayerColorDefinition->getFieldParse() );
multiplayerColorDefinition->setColor(multiplayerColorDefinition->getRGBValue());
multiplayerColorDefinition->setNightColor(multiplayerColorDefinition->getRGBNightValue());
}
namespace
{
struct MultiplayerStartingMoneySettings
{
Money money;
Bool isDefault;
};
const FieldParse startingMoneyFieldParseTable[] =
{
{ "Value", Money::parseMoneyAmount, NULL, offsetof( MultiplayerStartingMoneySettings, money ) },
{ "Default", INI::parseBool, NULL, offsetof( MultiplayerStartingMoneySettings, isDefault ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
}
void INI::parseMultiplayerStartingMoneyChoiceDefinition( INI* ini )
{
DEBUG_ASSERTCRASH( ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES, ("Overrides not supported for MultiplayerStartingMoneyChoice") );
// Temporary data store
MultiplayerStartingMoneySettings settings;
settings.isDefault = false;
ini->initFromINI( &settings, startingMoneyFieldParseTable );
TheMultiplayerSettings->addStartingMoneyChoice( settings.money, settings.isDefault );
}

View File

@@ -0,0 +1,61 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIObject.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Parsing Object INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/ThingTemplate.h"
#include "Common/ThingFactory.h"
#include "GameLogic/Module/OpenContain.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse Object entry */
//-------------------------------------------------------------------------------------------------
void INI::parseObjectDefinition( INI* ini )
{
AsciiString name = ini->getNextToken();
ThingFactory::parseObjectDefinition(ini, name, AsciiString::TheEmptyString);
}
//-------------------------------------------------------------------------------------------------
/** Parse Object entry */
//-------------------------------------------------------------------------------------------------
void INI::parseObjectReskinDefinition( INI* ini )
{
AsciiString name = ini->getNextToken();
AsciiString reskinFrom = ini->getNextToken();
ThingFactory::parseObjectDefinition(ini, name, reskinFrom);
}

View File

@@ -0,0 +1,57 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIParticleSys.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Michael S. Booth, November 2001
// Desc: Parsing Particle System INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/ParticleSys.h"
/**
* Parse entry
*/
void INI::parseParticleSystemDefinition( INI* ini )
{
AsciiString name;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present
ParticleSystemTemplate *sysTemplate = const_cast<ParticleSystemTemplate*>(TheParticleSystemManager->findTemplate( name ));
if (sysTemplate == NULL)
{
// no item is present, create a new one
sysTemplate = TheParticleSystemManager->newTemplate( name );
}
// parse the ini definition
ini->initFromINI( sysTemplate, sysTemplate->getFieldParse() );
}

View File

@@ -0,0 +1,44 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INISpecialPower.cpp //////////////////////////////////////////////////////////////////////
// Author: Colin Day, April 2002
// Desc: Special Power INI database
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/SpecialPower.h"
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void INI::parseSpecialPowerDefinition( INI *ini )
{
SpecialPowerStore::parseSpecialPowerDefinition(ini);
}

View File

@@ -0,0 +1,62 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INITerrain.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Terrain type INI loading
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/TerrainTypes.h"
//-------------------------------------------------------------------------------------------------
/** Parse Terrain type entry */
//-------------------------------------------------------------------------------------------------
void INI::parseTerrainDefinition( INI* ini )
{
AsciiString name;
TerrainType *terrainType;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present
terrainType = TheTerrainTypes->findTerrain( name );
if( terrainType == NULL )
terrainType = TheTerrainTypes->newTerrain( name );
// sanity
DEBUG_ASSERTCRASH( terrainType, ("Unable to allocate terrain type '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( terrainType, terrainType->getFieldParse() );
} // end parseTerrain

View File

@@ -0,0 +1,74 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INITerrainBridge.cpp /////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Terrain bridge INI loading
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/TerrainRoads.h"
//-------------------------------------------------------------------------------------------------
/** Parse Terrain Bridge entry */
//-------------------------------------------------------------------------------------------------
void INI::parseTerrainBridgeDefinition( INI* ini )
{
AsciiString name;
TerrainRoadType *bridge;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present or allocate new one
bridge = TheTerrainRoads->findBridge( name );
// if item is found it better already be a bridge
if( bridge )
{
// sanity
DEBUG_ASSERTCRASH( bridge->isBridge(), ("Redefining road '%s' as a bridge!\n",
bridge->getName().str()) );
throw INI_INVALID_DATA;
} // end if
if( bridge == NULL )
bridge = TheTerrainRoads->newBridge( name );
DEBUG_ASSERTCRASH( bridge, ("Unable to allcoate bridge '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( bridge, bridge->getBridgeFieldParse() );
} // end parseTerrainBridge

View File

@@ -0,0 +1,74 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INITerrainRoad.cpp ///////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Terrain road INI loading
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/TerrainRoads.h"
//-------------------------------------------------------------------------------------------------
/** Parse Terrain Road entry */
//-------------------------------------------------------------------------------------------------
void INI::parseTerrainRoadDefinition( INI* ini )
{
AsciiString name;
TerrainRoadType *road;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present or allocate new one
road = TheTerrainRoads->findRoad( name );
// if item is found it better not already be a bridge
if( road )
{
// sanity
DEBUG_ASSERTCRASH( road->isBridge() == FALSE, ("Redefining bridge '%s' as a road!\n",
road->getName().str()) );
throw INI_INVALID_DATA;
} // end if
if( road == NULL )
road = TheTerrainRoads->newRoad( name );
DEBUG_ASSERTCRASH( road, ("Unable to allocate road '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( road, road->getRoadFieldParse() );
} // end parseTerrainRoad

View File

@@ -0,0 +1,45 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIUpgrade.cpp ///////////////////////////////////////////////////////////////////////////
// Author: Colin Day, March 2002
// Desc: Upgrade database
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/Upgrade.h"
//-------------------------------------------------------------------------------------------------
/** Parse an upgrade definition */
//-------------------------------------------------------------------------------------------------
void INI::parseUpgradeDefinition( INI *ini )
{
UpgradeCenter::parseUpgradeDefinition(ini);
}

View File

@@ -0,0 +1,61 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIVideo.cpp /////////////////////////////////////////////////////////////////////////////
// Author: John McDonald, February 2002
// Desc: Parsing Video INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameClient/VideoPlayer.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse Music entry */
//-------------------------------------------------------------------------------------------------
void INI::parseVideoDefinition( INI* ini )
{
// read the name
const char* c = ini->getNextToken();
Video video;
video.m_internalName.set( c );
ini->initFromINI(&video, TheVideoPlayer->getFieldParse() );
TheVideoPlayer->addVideo(&video);
} // end parseVideoDefinition

View File

@@ -0,0 +1,144 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIWater.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Water settings
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_TIME_OF_DAY_NAMES
#include "Common/INI.h"
#include "Common/GameType.h"
#include "GameClient/TerrainVisual.h"
#include "GameClient/Water.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Water setting, note that this does not support override situations. As the water
* system becomes more complex we may want to change this */
//-------------------------------------------------------------------------------------------------
void INI::parseWaterSettingDefinition( INI* ini )
{
AsciiString name;
WaterSetting *waterSetting = NULL;
// read the name
const char* token = ini->getNextToken();
name.set( token );
// get the water setting we want to load based on name
char **timeOfDayName = TimeOfDayNames;
Int timeOfDayIndex = 0; // TIME_OF_DAY_INVALID
while( timeOfDayName && *timeOfDayName )
{
if( stricmp( *timeOfDayName, name.str() ) == 0 )
{
waterSetting = &WaterSettings[ timeOfDayIndex ];
break;
} // end if
// next name
timeOfDayName++;
timeOfDayIndex++;
} // end while
// check for no time of day match
if( waterSetting == NULL )
throw INI_INVALID_DATA;
// parse the data
ini->initFromINI( waterSetting, waterSetting->getFieldParse() );
} // end parseWaterSetting
//-------------------------------------------------------------------------------------------------
void INI::parseWaterTransparencyDefinition( INI *ini )
{
if (TheWaterTransparency == NULL) {
TheWaterTransparency = newInstance(WaterTransparencySetting);
} else if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
WaterTransparencySetting* wt = (WaterTransparencySetting*) (TheWaterTransparency.getNonOverloadedPointer());
WaterTransparencySetting* wtOverride = newInstance(WaterTransparencySetting);
*wtOverride = *wt;
// Mark that it is an override.
wtOverride->markAsOverride();
wt->friend_getFinalOverride()->setNextOverride(wtOverride);
} else {
throw INI_INVALID_DATA;
}
WaterTransparencySetting* waterTrans = (WaterTransparencySetting*) (TheWaterTransparency.getNonOverloadedPointer());
waterTrans = (WaterTransparencySetting*) (waterTrans->friend_getFinalOverride());
// parse the data
ini->initFromINI( waterTrans, TheWaterTransparency->getFieldParse() );
// If we overrode any skybox textures, then call the W3D Water stuff.
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
// Check to see if we overrode any skybox textures.
// If we did, then we need to replace them in the model.
// Copy/Paste monkeys PLEASE TAKE NOTE. This technique only works for the skybox because we
// know that there will never be more than one sky box. If you were to use this technique for
// technicals, for instance, it would make all technicals in the level have the same new
// texture.
const WaterTransparencySetting* wtOriginal = TheWaterTransparency.getNonOverloadedPointer();
OVERRIDE<WaterTransparencySetting> wtOverride = TheWaterTransparency;
if (wtOriginal == wtOverride)
return;
const AsciiString *oldTextures[5],*newTextures[5];
//Copy current texture names into arrays
oldTextures[0]=&wtOriginal->m_skyboxTextureN;
newTextures[0]=&wtOverride->m_skyboxTextureN;
oldTextures[1]=&wtOriginal->m_skyboxTextureE;
newTextures[1]=&wtOverride->m_skyboxTextureE;
oldTextures[2]=&wtOriginal->m_skyboxTextureS;
newTextures[2]=&wtOverride->m_skyboxTextureS;
oldTextures[3]=&wtOriginal->m_skyboxTextureW;
newTextures[3]=&wtOverride->m_skyboxTextureW;
oldTextures[4]=&wtOriginal->m_skyboxTextureT;
newTextures[4]=&wtOverride->m_skyboxTextureT;
TheTerrainVisual->replaceSkyboxTextures(oldTextures, newTextures);
}
}

View File

@@ -0,0 +1,48 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIWeapon.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Parsing Weapon INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "GameLogic/Weapon.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse Weapon entry */
//-------------------------------------------------------------------------------------------------
void INI::parseWeaponTemplateDefinition( INI* ini )
{
WeaponStore::parseWeaponTemplateDefinition(ini);
}

View File

@@ -0,0 +1,128 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: INIWebpageURL.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Bryan Cleveland, November 2001
// Desc: Parsing Webpage URL INI entries
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/Registry.h"
#include "GameNetwork/WOLBrowser/WebBrowser.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
AsciiString encodeURL(AsciiString source)
{
if (source.isEmpty())
{
return AsciiString::TheEmptyString;
}
AsciiString target;
AsciiString allowedChars = "$-_.+!*'(),\\";
const char *ptr = source.str();
while (*ptr)
{
if (isalnum(*ptr) || allowedChars.find(*ptr))
{
target.concat(*ptr);
}
else
{
AsciiString tmp;
target.concat('%');
tmp.format("%2.2x", ((int)*ptr));
target.concat(tmp);
}
++ptr;
}
return target;
}
//-------------------------------------------------------------------------------------------------
/** Parse Music entry */
//-------------------------------------------------------------------------------------------------
void INI::parseWebpageURLDefinition( INI* ini )
{
AsciiString tag;
WebBrowserURL *url;
// read the name
const char* c = ini->getNextToken();
tag.set( c );
if (TheWebBrowser != NULL)
{
url = TheWebBrowser->findURL(tag);
if (url == NULL)
{
url = TheWebBrowser->makeNewURL(tag);
}
}
// find existing item if present
// track = TheAudio->Music->getTrack( name );
// if( track == NULL )
// {
// allocate a new track
// track = TheAudio->Music->newMusicTrack( name );
// } // end if
// DEBUG_ASSERTCRASH( track, ("parseMusicTrackDefinition: Unable to allocate track '%s'\n",
// name.str()) );
// parse the ini definition
ini->initFromINI( url, url->getFieldParse() );
if (url->m_url.startsWith("file://"))
{
char cwd[_MAX_PATH] = "\\";
getcwd(cwd, _MAX_PATH);
url->m_url.format("file://%s\\Data\\%s\\%s", encodeURL(cwd).str(), GetRegistryLanguage().str(), url->m_url.str()+7);
DEBUG_LOG(("INI::parseWebpageURLDefinition() - converted URL to [%s]\n", url->m_url.str()));
}
} // end parseMusicTrackDefinition

View File

@@ -0,0 +1,69 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Language.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Language.cpp
//
// Created: Colin Day, June 2001
//
// Desc: For dealing with multiple languages
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Language.h"
// DEFINES ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
LanguageID OurLanguage = LANGUAGE_ID_US;
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: MiniLog.cpp ///////////////////////////////////////////////////////////
// Alternative logging
// Author: Matthew D. Campbell, January 2003
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/MiniLog.h"
#ifdef DEBUG_LOGGING
LogClass::LogClass(const char *fname)
{
char buffer[ _MAX_PATH ];
GetModuleFileName( NULL, buffer, sizeof( buffer ) );
char *pEnd = buffer + strlen( buffer );
while( pEnd != buffer )
{
if( *pEnd == '\\' )
{
*pEnd = 0;
break;
}
pEnd--;
}
AsciiString fullPath;
fullPath.format("%s\\%s", buffer, fname);
m_fp = fopen(fullPath.str(), "wt");
}
LogClass::~LogClass()
{
if (m_fp)
{
fclose(m_fp);
}
}
void LogClass::log(const char *fmt, ...)
{
if (!m_fp)
return;
static char buf[1024];
static Int lastFrame = 0;
static Int lastIndex = 0;
if (lastFrame != TheGameLogic->getFrame())
{
lastFrame = TheGameLogic->getFrame();
lastIndex = 0;
}
va_list va;
va_start( va, fmt );
_vsnprintf(buf, 1024, fmt, va );
buf[1023] = 0;
va_end( va );
char *tmp = buf;
while (tmp && *tmp)
{
if (*tmp == '\r' || *tmp == '\n')
{
*tmp = ' ';
}
++tmp;
}
fprintf(m_fp, "%d:%d %s\n", lastFrame, lastIndex++, buf);
fflush(m_fp);
}
#endif // DEBUG_LOGGING

View File

@@ -0,0 +1,174 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: MultiplayerSettings.cpp ///////////////////////////////////////////////////////////////////////////
// The MultiplayerSettings object
// Author: Matthew D. Campbell, January 2002
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_TERRAIN_LOD_NAMES
#define DEFINE_TIME_OF_DAY_NAMES
#include "Common/MultiplayerSettings.h"
#include "Common/INI.h"
#include "GameNetwork/GameInfo.h" // for PLAYERTEMPLATE_*
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
MultiplayerSettings *TheMultiplayerSettings = NULL; ///< The MultiplayerSettings singleton
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
const FieldParse MultiplayerColorDefinition::m_colorFieldParseTable[] =
{
{ "TooltipName", INI::parseAsciiString, NULL, offsetof( MultiplayerColorDefinition, m_tooltipName ) },
{ "RGBColor", INI::parseRGBColor, NULL, offsetof( MultiplayerColorDefinition, m_rgbValue ) },
{ "RGBNightColor", INI::parseRGBColor, NULL, offsetof( MultiplayerColorDefinition, m_rgbValueNight ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
const FieldParse MultiplayerSettings::m_multiplayerSettingsFieldParseTable[] =
{
{ "StartCountdownTimer", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_startCountdownTimerSeconds ) },
{ "MaxBeaconsPerPlayer", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_maxBeaconsPerPlayer ) },
{ "UseShroud", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_isShroudInMultiplayer ) },
{ "ShowRandomPlayerTemplate", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomPlayerTemplate ) },
{ "ShowRandomStartPos", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomStartPos ) },
{ "ShowRandomColor", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomColor ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
MultiplayerSettings::MultiplayerSettings()
{
m_maxBeaconsPerPlayer = 3;
//
m_startCountdownTimerSeconds = 0;
m_numColors = 0;
m_isShroudInMultiplayer = TRUE;
m_showRandomPlayerTemplate = TRUE;
m_showRandomStartPos = TRUE;
m_showRandomColor = TRUE;
m_observerColor;
m_randomColor;
m_gotDefaultStartingMoney = false;
} // end MultiplayerSettings
MultiplayerColorDefinition::MultiplayerColorDefinition()
{
m_tooltipName.clear();
m_rgbValue.setFromInt(0xFFFFFFFF);
m_rgbValueNight=m_rgbValue;
m_color = 0xFFFFFFFF;
m_colorNight = m_color;
}
MultiplayerColorDefinition * MultiplayerSettings::getColor(Int which)
{
if (which == PLAYERTEMPLATE_RANDOM)
{
return &m_randomColor;
}
else if (which == PLAYERTEMPLATE_OBSERVER)
{
return &m_observerColor;
}
else if (which < 0 || which >= getNumColors())
{
return NULL;
}
return &m_colorList[which];
}
MultiplayerColorDefinition * MultiplayerSettings::findMultiplayerColorDefinitionByName(AsciiString name)
{
MultiplayerColorIter iter = m_colorList.begin();
while (iter != m_colorList.end())
{
if (iter->second.getTooltipName() == name)
return &(iter->second);
++iter;
}
return NULL;
}
MultiplayerColorDefinition * MultiplayerSettings::newMultiplayerColorDefinition(AsciiString name)
{
MultiplayerColorDefinition tmp;
Int numColors = getNumColors();
m_colorList[numColors] = tmp;
m_numColors = m_colorList.size();
return &m_colorList[numColors];
}
void MultiplayerSettings::addStartingMoneyChoice( const Money & money, Bool isDefault )
{
m_startingMoneyList.push_back( money );
if ( isDefault )
{
DEBUG_ASSERTCRASH( !m_gotDefaultStartingMoney, ("Cannot have more than one default MultiplayerStartingMoneyChoice") );
m_defaultStartingMoney = money;
m_gotDefaultStartingMoney = true;
}
}
MultiplayerColorDefinition * MultiplayerColorDefinition::operator =(const MultiplayerColorDefinition& other)
{
m_tooltipName = other.getTooltipName();
m_rgbValue = other.getRGBValue();
m_color = other.getColor();
m_rgbValueNight = other.getRGBNightValue();
m_colorNight = other.getNightColor();
return this;
}
void MultiplayerColorDefinition::setColor( RGBColor rgb )
{
m_color = rgb.getAsInt() | 0xFF << 24;
}
void MultiplayerColorDefinition::setNightColor( RGBColor rgb )
{
m_colorNight = rgb.getAsInt() | 0xFF << 24;
}

View File

@@ -0,0 +1,240 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: NameKeyGenerator.cpp /////////////////////////////////////////////////////////////////////
// Created: Michael Booth, May 2001
// Colin Day, May 2001
// Desc: Name key system to translate between names and unique key ids
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
// Public Data ////////////////////////////////////////////////////////////////////////////////////
NameKeyGenerator *TheNameKeyGenerator = NULL; ///< name key gen. singleton
//-------------------------------------------------------------------------------------------------
NameKeyGenerator::NameKeyGenerator()
{
m_nextID = (UnsignedInt)NAMEKEY_INVALID; // uninitialized system
for (Int i = 0; i < SOCKET_COUNT; ++i)
m_sockets[i] = NULL;
} // end NameKeyGenerator
//-------------------------------------------------------------------------------------------------
NameKeyGenerator::~NameKeyGenerator()
{
// free all system data
freeSockets();
} // end ~NameKeyGenerator
//-------------------------------------------------------------------------------------------------
void NameKeyGenerator::init()
{
DEBUG_ASSERTCRASH(m_nextID == (UnsignedInt)NAMEKEY_INVALID, ("NameKeyGen already inited"));
// start keys at the beginning again
freeSockets();
m_nextID = 1;
} // end init
//-------------------------------------------------------------------------------------------------
void NameKeyGenerator::reset()
{
freeSockets();
m_nextID = 1;
} // end reset
//-------------------------------------------------------------------------------------------------
void NameKeyGenerator::freeSockets()
{
for (Int i = 0; i < SOCKET_COUNT; ++i)
{
Bucket *next;
for (Bucket *b = m_sockets[i]; b; b = next)
{
next = b->m_nextInSocket;
b->deleteInstance();
}
m_sockets[i] = NULL;
}
} // end freeSockets
/* ------------------------------------------------------------------------ */
inline UnsignedInt calcHashForString(const char* p)
{
UnsignedInt result = 0;
Byte *pp = (Byte*)p;
while (*pp)
result = (result << 5) + result + *pp++;
return result;
}
/* ------------------------------------------------------------------------ */
inline UnsignedInt calcHashForLowercaseString(const char* p)
{
UnsignedInt result = 0;
Byte *pp = (Byte*)p;
while (*pp)
result = (result << 5) + result + tolower(*pp++);
return result;
}
//-------------------------------------------------------------------------------------------------
AsciiString NameKeyGenerator::keyToName(NameKeyType key)
{
for (Int i = 0; i < SOCKET_COUNT; ++i)
{
for (Bucket *b = m_sockets[i]; b; b = b->m_nextInSocket)
{
if (key == b->m_key)
return b->m_nameString;
}
}
return AsciiString::TheEmptyString;
}
//-------------------------------------------------------------------------------------------------
NameKeyType NameKeyGenerator::nameToKey(const char* nameString)
{
Bucket *b;
UnsignedInt hash = calcHashForString(nameString) % SOCKET_COUNT;
// hmm, do we have it already?
for (b = m_sockets[hash]; b; b = b->m_nextInSocket)
{
if (strcmp(nameString, b->m_nameString.str()) == 0)
return b->m_key;
}
// nope, guess not. let's allocate it.
b = newInstance(Bucket);
b->m_key = (NameKeyType)m_nextID++;
b->m_nameString = nameString;
b->m_nextInSocket = m_sockets[hash];
m_sockets[hash] = b;
NameKeyType result = b->m_key;
#if defined(_DEBUG) || defined(_INTERNAL)
// reality-check to be sure our hasher isn't going bad.
const Int maxThresh = 3;
Int numOverThresh = 0;
for (Int i = 0; i < SOCKET_COUNT; ++i)
{
Int numInThisSocket = 0;
for (b = m_sockets[i]; b; b = b->m_nextInSocket)
++numInThisSocket;
if (numInThisSocket > maxThresh)
++numOverThresh;
}
// if more than a small percent of the sockets are getting deep, probably want to increase the socket count.
if (numOverThresh > SOCKET_COUNT/20)
{
DEBUG_CRASH(("hmm, might need to increase the number of bucket-sockets for NameKeyGenerator (numOverThresh %d = %f%%)\n",numOverThresh,(Real)numOverThresh/(Real)(SOCKET_COUNT/20)));
}
#endif
return result;
} // end nameToKey
//-------------------------------------------------------------------------------------------------
NameKeyType NameKeyGenerator::nameToLowercaseKey(const char* nameString)
{
Bucket *b;
UnsignedInt hash = calcHashForLowercaseString(nameString) % SOCKET_COUNT;
// hmm, do we have it already?
for (b = m_sockets[hash]; b; b = b->m_nextInSocket)
{
if (_stricmp(nameString, b->m_nameString.str()) == 0)
return b->m_key;
}
// nope, guess not. let's allocate it.
b = newInstance(Bucket);
b->m_key = (NameKeyType)m_nextID++;
b->m_nameString = nameString;
b->m_nextInSocket = m_sockets[hash];
m_sockets[hash] = b;
NameKeyType result = b->m_key;
#if defined(_DEBUG) || defined(_INTERNAL)
// reality-check to be sure our hasher isn't going bad.
const Int maxThresh = 3;
Int numOverThresh = 0;
for (Int i = 0; i < SOCKET_COUNT; ++i)
{
Int numInThisSocket = 0;
for (b = m_sockets[i]; b; b = b->m_nextInSocket)
++numInThisSocket;
if (numInThisSocket > maxThresh)
++numOverThresh;
}
// if more than a small percent of the sockets are getting deep, probably want to increase the socket count.
if (numOverThresh > SOCKET_COUNT/20)
{
DEBUG_CRASH(("hmm, might need to increase the number of bucket-sockets for NameKeyGenerator (numOverThresh %d = %f%%)\n",numOverThresh,(Real)numOverThresh/(Real)(SOCKET_COUNT/20)));
}
#endif
return result;
} // end nameToLowercaseKey
//-------------------------------------------------------------------------------------------------
// Get a string out of the INI. Store it into a NameKeyType
//-------------------------------------------------------------------------------------------------
void NameKeyGenerator::parseStringAsNameKeyType( INI *ini, void *instance, void *store, const void* userData )
{
*(NameKeyType *)store = TheNameKeyGenerator->nameToKey( ini->getNextToken() );
}
//-------------------------------------------------------------------------------------------------
NameKeyType StaticNameKey::key() const
{
if (m_key == NAMEKEY_INVALID)
{
DEBUG_ASSERTCRASH(TheNameKeyGenerator, ("no TheNameKeyGenerator yet"));
if (TheNameKeyGenerator)
m_key = TheNameKeyGenerator->nameToKey(m_name);
}
return m_key;
}

View File

@@ -0,0 +1,126 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PartitionSolver.cpp //////////////////////////////////////////////////////////////////////
/*---------------------------------------------------------------------------*/
/* EA Pacific */
/* Confidential Information */
/* Copyright (C) 2001 - All Rights Reserved */
/* DO NOT DISTRIBUTE */
/*---------------------------------------------------------------------------*/
/* Project: RTS3 */
/* File name: PartitionSolver.cpp */
/* Created: John K. McDonald, Jr., 4/2/2002 */
/* Desc: This contains a general-purpose Partition solver */
/* Revision History: */
/* 4/12/2002 : Initial creation */
/*---------------------------------------------------------------------------*/
/**************************************************************************************************
Some info about partioning problems:
This problem is contained in a very interesting class of problems known as NP complete. The
basic problem is that there is no way to tell whether you have an optimal solution or not.
Worst case, you try out every possible solution and still don't find the optimal solution:
this takes 2^n time to find, where N is the number of elements you are attempting to place.
For this reason, a value near PREFER_FAST_SOLUTION should almost always be chosen. We will use
a flat multiply to determine how many solutions to attempt before giving up and returning our
best attempt. If you want more info, this site contains info on the problem:
http://odysseus.nat.uni-magdeburg.de/~mertens/npp/index.shtml
**************************************************************************************************/
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/PartitionSolver.h"
static Bool greater_than(PairObjectIDAndUInt a, PairObjectIDAndUInt b)
{
return a.second > b.second;
}
PartitionSolver::PartitionSolver(const EntriesVec& elements, const SpacesVec& spaces, SolutionType solveHow)
{
m_data = elements;
m_spacesForData = spaces;
m_howToSolve = solveHow;
//Added By Sadullah Nader
//Initializations inserted
m_currentSolutionLeftovers = 0;
//
}
void PartitionSolver::solve(void)
{
m_bestSolution.clear();
m_currentSolution.clear();
m_currentSolutionLeftovers = 0x7fffffff;
Int minSizeForAllData = 0;
Int slotsAllotted = 0;
Int i, j;
// first, determine whether there is an actual solution, or we're going to have to fudge it.
for (i = 0; i < m_data.size(); ++i) {
minSizeForAllData += m_data[i].second;
}
for (i = 0; i < m_spacesForData.size(); ++i) {
slotsAllotted += m_spacesForData[i].second;
}
// we want to attempt to place the largest things first. This allows us to throw
// out whole classes of solutions
std::sort(m_data.begin(), m_data.end(), greater_than);
// Also make the largest partition first.
std::sort(m_spacesForData.begin(), m_spacesForData.end(), greater_than);
// work in our temporary vector.
SpacesVec spacesStillAvailable = m_spacesForData;
if (m_howToSolve == PREFER_FAST_SOLUTION)
{
// we prefer the fast, but not necessarily correct solution
// simply start placing the stuff. Skip things you can't place.
for (i = 0; i < m_data.size(); ++i)
{
for (j = 0; j < spacesStillAvailable.size(); ++j)
{
if (m_data[i].second <= spacesStillAvailable[j].second)
{
spacesStillAvailable[j].second -= m_data[i].second;
m_bestSolution.push_back(std::make_pair(m_data[i].first, spacesStillAvailable[j].first));
break;
}
}
}
} else {
DEBUG_CRASH(("PREFER_CORRECT_SOLUTION @todo impl"));
}
}
const SolutionVec& PartitionSolver::getSolution( void ) const
{
return m_bestSolution;
}

View File

@@ -0,0 +1,682 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PerfTimer.cpp ///////////////////////////////////////////////////////////////////////////
// Author:
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/PerfTimer.h"
#include "Common/GlobalData.h"
#include "GameClient/DebugDisplay.h"
#include "GameClient/Display.h"
#include "GameClient/GraphDraw.h"
__forceinline void ProfileGetTime(__int64 &t)
{
_asm
{
mov ecx,[t]
push eax
push edx
rdtsc
mov [ecx],eax
mov [ecx+4],edx
pop edx
pop eax
};
}
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
#if defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
//-------------------------------------------------------------------------------------------------
static Int64 s_ticksPerSec = 0;
static double s_ticksPerMSec = 0;
static double s_ticksPerUSec = 0;
//-------------------------------------------------------------------------------------------------
void GetPrecisionTimerTicksPerSec(Int64* t)
{
*t = s_ticksPerSec;
}
//Kris: Plugged in Martin's code to optimize timer setup.
#define HOFFESOMMER_REPLACEMENT_CODE
//-------------------------------------------------------------------------------------------------
void InitPrecisionTimer()
{
#ifdef HOFFESOMMER_REPLACEMENT_CODE
// measure clock cycles 3 times for 20 msec each
// then take the 2 counts that are closest, average
_int64 n[ 3 ];
for( int k = 0; k < 3; k++ )
{
// wait for end of current tick
unsigned timeEnd = timeGetTime() + 2;
while( timeGetTime() < timeEnd ); //do nothing
// get cycles
_int64 start, startQPC, endQPC;
QueryPerformanceCounter( (LARGE_INTEGER *)&startQPC );
ProfileGetTime( start );
timeEnd += 20;
while( timeGetTime() < timeEnd ); //do nothing
ProfileGetTime( n[ k ] );
n[ k ] -= start;
// convert to 1 second
if( QueryPerformanceCounter( (LARGE_INTEGER*)&endQPC ) )
{
QueryPerformanceFrequency( (LARGE_INTEGER*)&s_ticksPerSec );
n[ k ] = ( n[ k ] * s_ticksPerSec ) / ( endQPC - startQPC );
}
else
{
n[ k ] = ( n[ k ] * 1000 ) / 20;
}
}
// find two closest values
_int64 d01 = n[ 1 ] - n[ 0 ];
_int64 d02 = n[ 2 ] - n[ 0 ];
_int64 d12 = n[ 2 ] - n[ 1 ];
if( d01 < 0 )
{
d01 = -d01;
}
if( d02 < 0 )
{
d02 = -d02;
}
if( d12 < 0 )
{
d12 = -d12;
}
_int64 avg;
if( d01 < d02 )
{
avg = d01 < d12 ? n[ 0 ] + n[ 1 ] : n[ 1 ] + n[ 2 ];
}
else
{
avg = d02 < d12 ? n[ 0 ] + n[ 2 ] : n[ 1 ] + n[ 2 ];
}
//s_ticksPerMSec = 1.0 * TotalTicks / totalTime;
s_ticksPerMSec = avg / 2000.0f;
s_ticksPerSec = s_ticksPerMSec * 1000.0f;
s_ticksPerUSec = s_ticksPerSec / 1000000.0f;
#else
//Kris: With total disrespect, this code costs 5 real seconds of init time
//whenever we fire up the game.
#ifdef USE_QPF
QueryPerformanceFrequency((LARGE_INTEGER*)&s_ticksPerSec);
#else
// Init the precision timers
Int64 totalTime = 0;
Int64 TotalTicks = 0;
static int TESTS = 5;
for (int i = 0; i < TESTS; ++i)
{
int TimeStart;
int TimeStop;
Int64 StartTicks;
Int64 EndTicks;
TimeStart = timeGetTime();
GetPrecisionTimer(&StartTicks);
for(;;)
{
TimeStop = timeGetTime();
if ((TimeStop - TimeStart) > 1000)
{
GetPrecisionTimer(&EndTicks);
break;
}
}
TotalTicks += (EndTicks - StartTicks);
totalTime += (TimeStop - TimeStart);
}
s_ticksPerMSec = 1.0 * TotalTicks / totalTime;
s_ticksPerSec = s_ticksPerMSec * 1000.0f;
#endif
s_ticksPerMSec = s_ticksPerSec / 1000.0f;
s_ticksPerUSec = s_ticksPerSec / 1000000.0f;
#ifdef NOT_IN_USE
Int64 bogus[8];
GetPrecisionTimer(&start);
for (Int ii = 0; ii < ITERS; ++ii)
{
GetPrecisionTimer(&bogus[0]);
GetPrecisionTimer(&bogus[1]);
GetPrecisionTimer(&bogus[2]);
GetPrecisionTimer(&bogus[3]);
GetPrecisionTimer(&bogus[4]);
GetPrecisionTimer(&bogus[5]);
GetPrecisionTimer(&bogus[6]);
GetPrecisionTimer(&bogus[7]);
}
TheTicksToGetTicks = (bogus[7] - start) / (ITERS*8);
DEBUG_LOG(("TheTicksToGetTicks is %d (%f usec)\n",(int)TheTicksToGetTicks,TheTicksToGetTicks/s_ticksPerUSec));
#endif
#endif
}
#endif // defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
#ifdef PERF_TIMERS
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/*static*/ Bool AutoPerfGatherIgnore::s_ignoring = false;
//-------------------------------------------------------------------------------------------------
typedef std::vector< std::pair< AsciiString, AsciiString > > StringPairVec;
//-------------------------------------------------------------------------------------------------
// PerfMetrics class. Basically, request a handle with your name and it will return. We use a vector
// of pairs of asciistrings for this
class PerfMetricsOutput
{
private:
StringPairVec m_outputStats;
public:
AsciiString& getStatsString(const AsciiString& id)
{
for (int i = 0; i < m_outputStats.size(); ++i)
{
if (m_outputStats[i].first == id)
return m_outputStats[i].second;
}
std::pair<AsciiString, AsciiString> newPair;
newPair.first = id;
m_outputStats.push_back(newPair);
return m_outputStats.back().second;
}
void clearStatsString(const AsciiString& id)
{
for (int i = 0; i < m_outputStats.size(); ++i)
{
if (m_outputStats[i].first == id)
{
m_outputStats.erase(&m_outputStats[i]);
return;
}
}
}
StringPairVec& friend_getAllStatsStrings() { return m_outputStats; }
};
//-------------------------------------------------------------------------------------------------
static PerfMetricsOutput s_output;
static FILE* s_perfStatsFile = NULL;
static Int s_perfDumpOptions = 0;
static UnsignedInt s_lastDumpedFrame = 0;
static char s_buf[256] = "";
PerfGather* PerfGather::m_active[MAX_ACTIVE_STACK] = { 0 };
PerfGather** PerfGather::m_activeHead = &PerfGather::m_active[0];
Int64 PerfGather::s_stopStartOverhead = -1;
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/*static*/ PerfGather*& PerfGather::getHeadPtr()
{
// funky technique for order-of-init problem. trust me. (srj)
static PerfGather* s_head = NULL;
return s_head;
}
//-------------------------------------------------------------------------------------------------
void PerfGather::addToList()
{
PerfGather*& head = getHeadPtr();
this->m_next = head;
if (head)
head->m_prev = this;
head = this;
}
//-------------------------------------------------------------------------------------------------
void PerfGather::removeFromList()
{
PerfGather*& head = getHeadPtr();
if (this->m_next)
this->m_next->m_prev = this->m_prev;
if (this->m_prev)
this->m_prev->m_next = this->m_next;
else
head = this->m_next;
this->m_prev = 0;
this->m_next = 0;
}
//-------------------------------------------------------------------------------------------------
PerfGather::PerfGather(const char *identifier) :
m_identifier(identifier),
m_startTime(0),
m_runningTimeGross(0),
m_runningTimeNet(0),
m_callCount(0),
m_next(0),
m_prev(0)
{
//Added By Sadullah Nader
//Initializations inserted
m_ignore = FALSE;
//
DEBUG_ASSERTCRASH(strchr(m_identifier, ',') == NULL, ("PerfGather names must not contain commas"));
addToList();
}
//-------------------------------------------------------------------------------------------------
PerfGather::~PerfGather()
{
removeFromList();
}
//-------------------------------------------------------------------------------------------------
void PerfGather::reset()
{
m_startTime = 0;
m_runningTimeGross = 0;
m_runningTimeNet = 0;
m_callCount = 0;
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PerfGather::resetAll()
{
for (PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
head->reset();
}
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PerfGather::initPerfDump(const char* fname, Int options)
{
PerfGather::termPerfDump();
strcpy(s_buf, fname);
char tmp[256];
strcpy(tmp, s_buf);
strcat(tmp, ".csv");
s_perfStatsFile = fopen(tmp, "w");
s_perfDumpOptions = options;
if (s_perfStatsFile == NULL)
{
DEBUG_CRASH(("could not open/create perf file %s -- is it open in another app?",s_buf));
return;
}
if (s_stopStartOverhead == -1)
{
const Int ITERS = 100000;
Int64 start, end;
PerfGather pf("timer");
GetPrecisionTimer(&start);
for (Int ii = 0; ii < ITERS; ++ii)
{
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
pf.startTimer(); pf.stopTimer();
}
GetPrecisionTimer(&end);
s_stopStartOverhead = (end - start) / (ITERS*8);
DEBUG_LOG(("s_stopStartOverhead is %d (%f usec)\n",(int)s_stopStartOverhead,s_stopStartOverhead/s_ticksPerUSec));
}
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PerfGather::dumpAll(UnsignedInt frame)
{
if (frame < s_lastDumpedFrame)
{
// must have reset or started a new game.
termPerfDump();
initPerfDump(s_buf, s_perfDumpOptions);
}
if (!s_perfStatsFile)
{
DEBUG_CRASH(("not inited"));
return;
}
if (frame >= 1 && frame <= 30)
{
// always skip the first second or so, since it loads everything and skews the results horribly
}
else
{
if (s_lastDumpedFrame == 0)
{
fprintf(s_perfStatsFile, "Frame");
if (s_perfDumpOptions & PERF_GROSSTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
fprintf(s_perfStatsFile, ",Gross:%s", head->m_identifier);
}
}
if (s_perfDumpOptions & PERF_NETTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
fprintf(s_perfStatsFile, ",Net:%s", head->m_identifier);
}
}
if (s_perfDumpOptions & PERF_CALLCOUNT)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
fprintf(s_perfStatsFile, ",Count:%s", head->m_identifier);
}
}
fprintf(s_perfStatsFile, "\n");
}
// a strange value so we can find it in the dump, if necessary.
// there's nothing magic about this value, it's purely determined from sample dumps...
// const Real CLIP_BIG_SPIKES = 1e10f;
const Real CLIP_BIG_SPIKES = 100000.0f;
// make this a nonnumeric thing so Excel won't try to graph it...
fprintf(s_perfStatsFile, "Frame%08d", frame);
if (s_perfDumpOptions & PERF_GROSSTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
double t = head->m_runningTimeGross;
t /= s_ticksPerUSec;
if (t > CLIP_BIG_SPIKES)
t = CLIP_BIG_SPIKES;
fprintf(s_perfStatsFile, ",%f", t);
}
}
if (s_perfDumpOptions & PERF_NETTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
double t = head->m_runningTimeNet;
t /= s_ticksPerUSec;
if (t > CLIP_BIG_SPIKES)
t = CLIP_BIG_SPIKES;
fprintf(s_perfStatsFile, ",%f", t);
}
}
if (s_perfDumpOptions & PERF_CALLCOUNT)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
fprintf(s_perfStatsFile, ",%d", head->m_callCount);
}
}
fprintf(s_perfStatsFile, "\n");
fflush(s_perfStatsFile);
s_lastDumpedFrame = frame;
}
}
//-------------------------------------------------------------------------------------------------
// This function will queue up stuff to draw on the next frame. We also need to adjust the
// perf timers to not include time spent paused by the script engine.
/*static*/ void PerfGather::displayGraph(UnsignedInt frame)
{
if (!TheGraphDraw) {
return;
}
if (frame >= 1 && frame <= 30)
{
// always skip the first second or so, since it loads everything and skews the results horribly
}
else
{
const Real CLIP_BIG_SPIKES = 100000.0f;
if (s_perfDumpOptions & PERF_GROSSTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
Real t = head->m_runningTimeGross;
t /= s_ticksPerUSec;
if (t > CLIP_BIG_SPIKES)
t = CLIP_BIG_SPIKES;
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
}
}
if (s_perfDumpOptions & PERF_NETTIME)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
Real t = head->m_runningTimeNet;
t /= s_ticksPerUSec;
if (t > CLIP_BIG_SPIKES)
t = CLIP_BIG_SPIKES;
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
}
}
if (s_perfDumpOptions & PERF_CALLCOUNT)
{
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
{
Real t = head->m_callCount;
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
}
}
}
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PerfGather::termPerfDump()
{
if (s_perfStatsFile)
{
fflush(s_perfStatsFile);
fclose(s_perfStatsFile);
s_perfStatsFile = NULL;
}
s_lastDumpedFrame = 0;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
PerfTimer::PerfTimer( const char *identifier, Bool crashWithInfo, Int startFrame, Int endFrame) :
m_identifier(identifier),
m_crashWithInfo(crashWithInfo),
m_startFrame(startFrame),
m_endFrame(endFrame),
m_callCount(0),
m_runningTime(0),
m_outputInfo(true),
//Added By Sadullah Nader
//Intializations inserted
m_lastFrame(-1)
{
}
//-------------------------------------------------------------------------------------------------
PerfTimer::~PerfTimer( )
{
if (m_endFrame == -1) {
outputInfo();
}
}
//-------------------------------------------------------------------------------------------------
void PerfTimer::outputInfo( void )
{
if (TheGlobalData->m_showMetrics) {
return;
}
if (m_outputInfo && !TheGlobalData->m_showMetrics) {
m_outputInfo = false;
} else {
return;
}
if (!s_ticksPerSec) {
// DEBUG_CRASH here
return;
}
#if defined(_DEBUG) || defined(_INTERNAL)
double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
double avgTimePerCall = totalTimeInMS / m_callCount;
#endif
if (m_crashWithInfo) {
DEBUG_CRASH(("%s\n"
"Average Time (per call): %.4f ms\n"
"Average Time (per frame): %.4f ms\n"
"Average calls per frame: %.2f\n"
"Number of calls: %d\n"
"Max possible FPS: %.4f\n",
m_identifier,
avgTimePerCall,
avgTimePerFrame,
1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
m_callCount,
1000.0f / avgTimePerFrame));
} else {
DEBUG_LOG(("%s\n"
"Average Time (per call): %.4f ms\n"
"Average Time (per frame): %.4f ms\n"
"Average calls per frame: %.2f\n"
"Number of calls: %d\n"
"Max possible FPS: %.4f\n",
m_identifier,
avgTimePerCall,
avgTimePerFrame,
1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
m_callCount,
1000.0f / avgTimePerFrame));
}
}
//-------------------------------------------------------------------------------------------------
void PerfTimer::showMetrics( void )
{
#if defined(_DEBUG) || defined(_INTERNAL)
double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
double avgTimePerCall = totalTimeInMS / m_callCount;
#endif
// we want to work on the thing in the array, so just store a reference.
AsciiString &outputStats = s_output.getStatsString(m_identifier);
outputStats.format("%s: %.2fms / call, %.2fms / frame \n",
m_identifier,
avgTimePerCall,
avgTimePerFrame);
m_callCount = 0;
m_runningTime = 0;
UnsignedInt frm = (TheGameLogic ? TheGameLogic->getFrame() : m_startFrame);
m_startFrame = frm + 1;
m_endFrame = m_startFrame + PERFMETRICS_BETWEEN_METRICS;
}
//-------------------------------------------------------------------------------StatMetricsDisplay
void StatMetricsDisplay( DebugDisplayInterface *dd, void *, FILE *fp )
{
dd->printf("Performance Metrics: \n");
// no copies will take place because we are storing a reference to the thing
StringPairVec &stats = s_output.friend_getAllStatsStrings();
for (int i = 0; i < stats.size(); ++i) {
dd->printf("%s", stats[i].second.str());
}
}
#endif // PERF_TIMERS

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,304 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Energy.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Energy.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Energy.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
Energy::Energy()
{
m_energyProduction = 0;
m_energyConsumption = 0;
m_owner = NULL;
m_powerSabotagedTillFrame = 0;
}
//-----------------------------------------------------------------------------
Int Energy::getProduction() const
{
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power.
return 0;
}
return m_energyProduction;
}
//-----------------------------------------------------------------------------
Real Energy::getEnergySupplyRatio() const
{
DEBUG_ASSERTCRASH(m_energyProduction >= 0 && m_energyConsumption >= 0, ("neg Energy numbers\n"));
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power, no ratio.
return 0.0f;
}
if (m_energyConsumption == 0)
return (Real)m_energyProduction;
return (Real)m_energyProduction / (Real)m_energyConsumption;
}
//-------------------------------------------------------------------------------------------------
Bool Energy::hasSufficientPower(void) const
{
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power.
return FALSE;
}
return m_energyProduction >= m_energyConsumption;
}
//-------------------------------------------------------------------------------------------------
void Energy::adjustPower(Int powerDelta, Bool adding)
{
if (powerDelta == 0) {
return;
}
if (powerDelta > 0) {
if (adding) {
addProduction(powerDelta);
} else {
addProduction(-powerDelta);
}
} else {
// Seems a little odd, however, consumption is reversed. Negative power is positive consumption.
if (adding) {
addConsumption(-powerDelta);
} else {
addConsumption(powerDelta);
}
}
}
//-------------------------------------------------------------------------------------------------
/** new 'obj' will now add/subtract from this energy construct */
//-------------------------------------------------------------------------------------------------
void Energy::objectEnteringInfluence( Object *obj )
{
// sanity
if( obj == NULL )
return;
// get the amount of energy this object produces or consumes
Int energy = obj->getTemplate()->getEnergyProduction();
// adjust energy
if( energy < 0 )
addConsumption( -energy );
else if( energy > 0 )
addProduction( energy );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
} // end objectEnteringInfluence
//-------------------------------------------------------------------------------------------------
/** 'obj' will now no longer add/subtrack from this energy construct */
//-------------------------------------------------------------------------------------------------
void Energy::objectLeavingInfluence( Object *obj )
{
// sanity
if( obj == NULL )
return;
// get the amount of energy this object produces or consumes
Int energy = obj->getTemplate()->getEnergyProduction();
// adjust energy
if( energy < 0 )
addConsumption( energy );
else if( energy > 0 )
addProduction( -energy );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
}
//-------------------------------------------------------------------------------------------------
/** Adds an energy bonus to the player's pool of energy when the "Control Rods" upgrade
is made to the American Cold Fusion Plant */
//-------------------------------------------------------------------------------------------------
void Energy::addPowerBonus( Object *obj )
{
// sanity
if( obj == NULL )
return;
addProduction(obj->getTemplate()->getEnergyBonus());
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
}
// ------------------------------------------------------------------------------------------------
/** Removed an energy bonus */
// ------------------------------------------------------------------------------------------------
void Energy::removePowerBonus( Object *obj )
{
// sanity
if( obj == NULL )
return;
addProduction( -obj->getTemplate()->getEnergyBonus() );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
} // end removePowerBonus
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Private functions
// ------------------------------------------------------------------------------------------------
void Energy::addProduction(Int amt)
{
m_energyProduction += amt;
if( m_owner == NULL )
return;
// A repeated Brownout signal does nothing bad, and we need to handle more than just edge cases.
// Like low power, now even more low power, refresh disable.
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
}
// ------------------------------------------------------------------------------------------------
void Energy::addConsumption(Int amt)
{
m_energyConsumption += amt;
if( m_owner == NULL )
return;
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void Energy::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Energy::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 3;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// It is actually incorrect to save these, as they are reconstructed when the buildings are loaded
// I need to version though so old games will load wrong rather than crashing
// production
if( version < 2 )
xfer->xferInt( &m_energyProduction );
// consumption
if( version < 2 )
xfer->xferInt( &m_energyConsumption );
// owning player index
Int owningPlayerIndex;
if( xfer->getXferMode() == XFER_SAVE )
owningPlayerIndex = m_owner->getPlayerIndex();
xfer->xferInt( &owningPlayerIndex );
m_owner = ThePlayerList->getNthPlayer( owningPlayerIndex );
//Sabotage
if( version >= 3 )
{
xfer->xferUnsignedInt( &m_powerSabotagedTillFrame );
}
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void Energy::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,126 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Handicap.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Handicap.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Handicap.h"
#include "Common/Player.h"
#include "Common/Dict.h"
#include "Common/ThingTemplate.h"
//-----------------------------------------------------------------------------
Handicap::Handicap()
{
init();
}
//-----------------------------------------------------------------------------
void Handicap::init()
{
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
m_handicaps[i][j] = 1.0f;
}
//-----------------------------------------------------------------------------
void Handicap::readFromDict(const Dict* d)
{
// this isn't very efficient, but is only called at load times,
// so it probably doesn't really matter.
const char* htNames[HANDICAP_TYPE_COUNT] =
{
"BUILDCOST",
"BUILDTIME",
// "FIREPOWER",
// "ARMOR",
// "GROUNDSPEED",
// "AIRSPEED",
// "INCOME"
};
const char* ttNames[THING_TYPE_COUNT] =
{
"GENERIC",
"BUILDINGS",
};
// no, you should NOT call init() here.
//init();
AsciiString c;
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
{
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
{
c.clear();
c.set("HANDICAP_");
c.concat(htNames[i]);
c.concat("_");
c.concat(ttNames[j]);
NameKeyType k = TheNameKeyGenerator->nameToKey(c);
Bool exists;
Real r = d->getReal(k, &exists);
if (exists)
m_handicaps[i][j] = r;
}
}
}
//-----------------------------------------------------------------------------
/*static*/ Handicap::ThingType Handicap::getBestThingType(const ThingTemplate *tmpl)
{
/// if this ends up being too slow, cache the information in the object
if (tmpl->isKindOf(KINDOF_STRUCTURE))
return BUILDINGS;
return GENERIC;
}
//-----------------------------------------------------------------------------
Real Handicap::getHandicap(HandicapType ht, const ThingTemplate *tmpl) const
{
ThingType tt = getBestThingType(tmpl);
return m_handicaps[ht][tt];
}

View File

@@ -0,0 +1,113 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: MissionStats.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: MissionStats.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/MissionStats.h"
#include "Common/Player.h"
#include "Common/Xfer.h"
//-----------------------------------------------------------------------------
MissionStats::MissionStats()
{
init();
}
//-----------------------------------------------------------------------------
void MissionStats::init()
{
Int i;
for (i = 0; i < MAX_PLAYER_COUNT; ++i)
{
m_unitsKilled[i] = 0;
m_buildingsKilled[i] = 0;
}
m_unitsLost = 0;
m_buildingsLost = 0;
//m_whoLastHurtMe = PLAYER_NONE;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void MissionStats::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info;
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void MissionStats::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// units killed
xfer->xferUser( m_unitsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
// units lost
xfer->xferInt( &m_unitsLost );
// buidings killed
xfer->xferUser( m_buildingsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
// buildings lost
xfer->xferInt( &m_buildingsLost );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void MissionStats::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,145 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Money.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Money.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Money.h"
#include "Common/GameAudio.h"
#include "Common/MiscAudio.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/Xfer.h"
// ------------------------------------------------------------------------------------------------
UnsignedInt Money::withdraw(UnsignedInt amountToWithdraw, Bool playSound)
{
if (amountToWithdraw > m_money)
amountToWithdraw = m_money;
if (amountToWithdraw == 0)
return amountToWithdraw;
//@todo: Do we do this frequently enough that it is a performance hit?
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyWithdrawSound;
event.setPlayerIndex(m_playerIndex);
// Play a sound
if (playSound)
TheAudio->addAudioEvent(&event);
m_money -= amountToWithdraw;
return amountToWithdraw;
}
// ------------------------------------------------------------------------------------------------
void Money::deposit(UnsignedInt amountToDeposit, Bool playSound)
{
if (amountToDeposit == 0)
return;
//@todo: Do we do this frequently enough that it is a performance hit?
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyDepositSound;
event.setPlayerIndex(m_playerIndex);
// Play a sound
if (playSound)
TheAudio->addAudioEvent(&event);
m_money += amountToDeposit;
if( amountToDeposit > 0 )
{
Player *player = ThePlayerList->getNthPlayer( m_playerIndex );
if( player )
{
player->getAcademyStats()->recordIncome();
}
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void Money::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Money::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// money value
xfer->xferUnsignedInt( &m_money );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void Money::loadPostProcess( void )
{
} // end loadPostProcess
// ------------------------------------------------------------------------------------------------
/** Parse a money amount for the ini file. E.g. DefaultStartingMoney = 10000 */
// ------------------------------------------------------------------------------------------------
void Money::parseMoneyAmount( INI *ini, void *instance, void *store, const void* userData )
{
// Someday, maybe, have mulitple fields like Gold:10000 Wood:1000 Tiberian:10
Money * money = (Money *)store;
INI::parseUnsignedInt( ini, instance, &money->m_money, userData );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,493 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PlayerList.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: PlayerList.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Errors.h"
#include "Common/DataChunk.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/PlayerTemplate.h"
#include "Common/Team.h"
#include "Common/WellKnownKeys.h"
#include "Common/Xfer.h"
#ifdef _DEBUG
#include "GameLogic/Object.h"
#endif
#include "GameLogic/SidesList.h"
#include "GameNetwork/NetworkDefs.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
/*extern*/ PlayerList *ThePlayerList = NULL;
//-----------------------------------------------------------------------------
PlayerList::PlayerList() :
m_local(NULL),
m_playerCount(0)
{
// we only allocate a few of these, so don't bother pooling 'em
for (Int i = 0; i < MAX_PLAYER_COUNT; i++)
m_players[ i ] = NEW Player( i );
init();
}
//-----------------------------------------------------------------------------
PlayerList::~PlayerList()
{
try {
// the world is happier if we reinit things before destroying them,
// to avoid debug warnings
init();
} catch (...) {
// nothing
}
for( Int i = 0; i < MAX_PLAYER_COUNT; ++i )
delete m_players[ i ];
}
//-----------------------------------------------------------------------------
Player *PlayerList::getNthPlayer(Int i)
{
if( i < 0 || i >= MAX_PLAYER_COUNT )
{
// DEBUG_CRASH( ("Illegal player index\n") );
return NULL;
}
return m_players[i];
}
//-----------------------------------------------------------------------------
Player *PlayerList::findPlayerWithNameKey(NameKeyType key)
{
for (Int i = 0; i < m_playerCount; i++)
{
if (m_players[i]->getPlayerNameKey() == key)
{
return m_players[i];
}
}
return NULL;
}
//-----------------------------------------------------------------------------
void PlayerList::reset()
{
TheTeamFactory->clear(); // cleans up energy, among other things
init();
}
//-----------------------------------------------------------------------------
void PlayerList::newGame()
{
Int i;
DEBUG_ASSERTCRASH(this != NULL, ("null this"));
TheTeamFactory->clear(); // cleans up energy, among other things
// first, re-init ourselves.
init();
// ok, now create the rest of players we need.
Bool setLocal = false;
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Dict *d = TheSidesList->getSideInfo(i)->getDict();
AsciiString pname = d->getAsciiString(TheKey_playerName);
if (pname.isEmpty())
continue; // it's neutral, which we've already done, so skip it.
/// @todo The Player class should have a reset() method, instead of directly calling initFromDict() (MSB)
Player* p = m_players[m_playerCount++];
p->initFromDict(d);
// Multiplayer override
Bool exists; // throwaway, since we don't care if it exists
if (d->getBool(TheKey_multiplayerIsLocal, &exists))
{
DEBUG_LOG(("Player %s is multiplayer local\n", pname.str()));
setLocalPlayer(p);
setLocal = true;
}
if (!setLocal && !TheNetwork && d->getBool(TheKey_playerIsHuman))
{
setLocalPlayer(p);
setLocal = true;
}
// Set the build list.
p->setBuildList(TheSidesList->getSideInfo(i)->getBuildList());
// Build list is attached to player now, so release it from the side info.
TheSidesList->getSideInfo(i)->releaseBuildList();
}
if (!setLocal)
{
DEBUG_ASSERTCRASH(TheNetwork, ("*** Map has no human player... picking first nonneutral player for control\n"));
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Player* p = getNthPlayer(i);
if (p != getNeutralPlayer())
{
p->setPlayerType(PLAYER_HUMAN, false);
setLocalPlayer(p);
setLocal = true;
break;
}
}
}
// must reset teams *after* creating players.
TheTeamFactory->initFromSides(TheSidesList);
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Dict *d = TheSidesList->getSideInfo(i)->getDict();
Player* p = findPlayerWithNameKey(NAMEKEY(d->getAsciiString(TheKey_playerName)));
AsciiString tok;
AsciiString enemies = d->getAsciiString(TheKey_playerEnemies);
while (enemies.nextToken(&tok))
{
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
if (p2)
{
p->setPlayerRelationship(p2, ENEMIES);
}
else
{
DEBUG_LOG(("unknown enemy %s\n",tok.str()));
}
}
AsciiString allies = d->getAsciiString(TheKey_playerAllies);
while (allies.nextToken(&tok))
{
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
if (p2)
{
p->setPlayerRelationship(p2, ALLIES);
}
else
{
DEBUG_LOG(("unknown ally %s\n",tok.str()));
}
}
// finally, make sure self & neutral are correct.
p->setPlayerRelationship(p, ALLIES);
if (p != getNeutralPlayer())
p->setPlayerRelationship(getNeutralPlayer(), NEUTRAL);
p->setDefaultTeam();
}
}
//-----------------------------------------------------------------------------
void PlayerList::init()
{
m_playerCount = 1;
m_players[0]->init(NULL);
for (int i = 1; i < MAX_PLAYER_COUNT; i++)
m_players[i]->init(NULL);
// call setLocalPlayer so that becomingLocalPlayer() gets called appropriately
setLocalPlayer(m_players[0]);
}
//-----------------------------------------------------------------------------
void PlayerList::update()
{
// update all players
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->update();
} // end for i
}
//-----------------------------------------------------------------------------
void PlayerList::newMap()
{
// update all players
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->newMap();
} // end for i
}
// ------------------------------------------------------------------------
void PlayerList::teamAboutToBeDeleted(Team* team)
{
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->removeTeamRelationship(team);
}
}
//=============================================================================
void PlayerList::updateTeamStates(void)
{
// Clear team flags for all players.
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->updateTeamStates();
} // end for i
}
//-----------------------------------------------------------------------------
Team *PlayerList::validateTeam( AsciiString owner )
{
// owner could be a player or team. first, check team names.
Team *t = TheTeamFactory->findTeam(owner);
if (t)
{
//DEBUG_LOG(("assigned obj %08lx to team %s\n",obj,owner.str()));
}
else
{
DEBUG_CRASH(("no team or player named %s could be found!\n", owner.str()));
t = getNeutralPlayer()->getDefaultTeam();
}
return t;
}
//-----------------------------------------------------------------------------
void PlayerList::setLocalPlayer(Player *player)
{
// can't set local player to null -- if you try, you get neutral.
if (player == NULL)
{
DEBUG_CRASH(("local player may not be null"));
player = getNeutralPlayer();
}
if (player != m_local)
{
// m_local can be null the very first time we call this.
if (m_local)
m_local->becomingLocalPlayer(false);
m_local = player;
player->becomingLocalPlayer(true);
}
#ifdef INTENSE_DEBUG
if (player)
{
DEBUG_LOG(("\n----------\n"));
// did you know? you can use "%ls" to print a doublebyte string, even in a single-byte printf...
DEBUG_LOG(("Switching local players. The new player is named '%ls' (%s) and owns the following objects:\n",
player->getPlayerDisplayName().str(),
TheNameKeyGenerator->keyToName(player->getPlayerNameKey()).str()
));
for (Object *obj = player->getFirstOwnedObject(); obj; obj = obj->getNextOwnedObject())
{
DEBUG_LOG(("Obj %08lx is of type %s",obj,obj->getTemplate()->getName().str()));
if (!player->canBuild(obj->getTemplate()))
{
DEBUG_LOG((" (NOT BUILDABLE)"));
}
DEBUG_LOG(("\n"));
}
DEBUG_LOG(("\n----------\n"));
}
#endif
}
//-----------------------------------------------------------------------------
Player *PlayerList::getPlayerFromMask( PlayerMaskType mask )
{
Player *player = NULL;
Int i;
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
{
player = getNthPlayer( i );
if( player && player->getPlayerMask() == mask )
return player;
} // end for i
DEBUG_CRASH( ("Player does not exist for mask\n") );
return NULL; // mask not found
} // end getPlayerFromMask
//-----------------------------------------------------------------------------
Player *PlayerList::getEachPlayerFromMask( PlayerMaskType& maskToAdjust )
{
Player *player = NULL;
Int i;
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
{
player = getNthPlayer( i );
if ( player && BitTest(player->getPlayerMask(), maskToAdjust ))
{
maskToAdjust &= (~player->getPlayerMask());
return player;
}
} // end for i
DEBUG_CRASH( ("No players found that contain any matching masks.\n") );
maskToAdjust = 0;
return NULL; // mask not found
}
//-------------------------------------------------------------------------------------------------
PlayerMaskType PlayerList::getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships )
{
PlayerMaskType retVal = 0;
if (allowedRelationships == 0)
return retVal;
Player *srcPlayer = getNthPlayer(srcPlayerIndex);
if (!srcPlayer)
return retVal;
if (BitTest(allowedRelationships, ALLOW_SAME_PLAYER))
BitSet(retVal, srcPlayer->getPlayerMask());
for ( Int i = 0; i < getPlayerCount(); ++i )
{
Player *player = getNthPlayer(i);
if (!player)
continue;
if (player == srcPlayer)
continue;
switch (srcPlayer->getRelationship(player->getDefaultTeam()))
{
case ENEMIES:
if (BitTest(allowedRelationships, ALLOW_ENEMIES))
BitSet(retVal, player->getPlayerMask());
break;
case ALLIES:
if (BitTest(allowedRelationships, ALLOW_ALLIES))
BitSet(retVal, player->getPlayerMask());
break;
case NEUTRAL:
if (BitTest(allowedRelationships, ALLOW_NEUTRAL))
BitSet(retVal, player->getPlayerMask());
break;
}
}
return retVal;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void PlayerList::crc( Xfer *xfer )
{
xfer->xferInt( &m_playerCount );
for( Int i = 0; i < m_playerCount; ++i )
xfer->xferSnapshot( m_players[ i ] );
}
// ------------------------------------------------------------------------------------------------
/** Xfer Method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void PlayerList::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// xfer the player count
Int playerCount = m_playerCount;
xfer->xferInt( &playerCount );
//
// sanity, the player count read from the file should match our player count that
// was setup from the bare bones map load since that data can't change during run time
//
if( playerCount != m_playerCount )
{
DEBUG_CRASH(( "Invalid player count '%d', should be '%d'\n", playerCount, m_playerCount ));
throw SC_INVALID_DATA;
} // end if
// xfer each of the player data
for( Int i = 0; i < playerCount; ++i )
xfer->xferSnapshot( m_players[ i ] );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void PlayerList::loadPostProcess( void )
{
} // end postProcessLoad

View File

@@ -0,0 +1,418 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PlayerTemplate.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: PlayerTemplate.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
#include "Common/GameCommon.h"
#include "Common/PlayerTemplate.h"
#include "Common/Player.h"
#include "Common/INI.h"
#include "Common/Science.h"
#include "GameClient/Image.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/*static*/ const FieldParse* PlayerTemplate::getFieldParse()
{
static const FieldParse TheFieldParseTable[] =
{
{ "Side", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_side ) },
{ "BaseSide", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_baseSide ) },
{ "PlayableSide", INI::parseBool, NULL, offsetof( PlayerTemplate, m_playableSide ) },
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( PlayerTemplate, m_displayName) },
{ "StartMoney", PlayerTemplate::parseStartMoney, NULL, offsetof( PlayerTemplate, m_money ) },
{ "PreferredColor", INI::parseRGBColor, NULL, offsetof( PlayerTemplate, m_preferredColor ) },
{ "StartingBuilding", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingBuilding ) },
{ "StartingUnit0", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[0] ) },
{ "StartingUnit1", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[1] ) },
{ "StartingUnit2", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[2] ) },
{ "StartingUnit3", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[3] ) },
{ "StartingUnit4", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[4] ) },
{ "StartingUnit5", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[5] ) },
{ "StartingUnit6", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[6] ) },
{ "StartingUnit7", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[7] ) },
{ "StartingUnit8", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[8] ) },
{ "StartingUnit9", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[9] ) },
{ "ProductionCostChange", PlayerTemplate::parseProductionCostChange, NULL, 0 },
{ "ProductionTimeChange", PlayerTemplate::parseProductionTimeChange, NULL, 0 },
{ "ProductionVeterancyLevel", PlayerTemplate::parseProductionVeterancyLevel, NULL, 0 },
{ "IntrinsicSciences", INI::parseScienceVector, NULL, offsetof( PlayerTemplate, m_intrinsicSciences ) },
{ "PurchaseScienceCommandSetRank1",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank1 ) },
{ "PurchaseScienceCommandSetRank3",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank3 ) },
{ "PurchaseScienceCommandSetRank8",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank8 ) },
{ "SpecialPowerShortcutCommandSet",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutCommandSet ) },
{ "SpecialPowerShortcutWinName" ,INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutWinName) },
{ "SpecialPowerShortcutButtonCount",INI::parseInt, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutButtonCount ) },
{ "IsObserver", INI::parseBool, NULL, offsetof( PlayerTemplate, m_observer ) },
{ "OldFaction", INI::parseBool, NULL, offsetof( PlayerTemplate, m_oldFaction ) },
{ "IntrinsicSciencePurchasePoints", INI::parseInt, NULL, offsetof( PlayerTemplate, m_intrinsicSPP ) },
{ "ScoreScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_scoreScreenImage ) },
{ "LoadScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenImage ) },
{ "LoadScreenMusic", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenMusic ) },
{ "ScoreScreenMusic", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_scoreScreenMusic ) },
{ "HeadWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_headWaterMark ) },
{ "FlagWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_flagWaterMark ) },
{ "EnabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_enabledImage ) },
//{ "DisabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_disabledImage ) },
//{ "HiliteImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_hiliteImage ) },
//{ "PushedImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_pushedImage ) },
{ "SideIconImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_sideIconImage ) },
{ "GeneralImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_generalImage ) },
{ "BeaconName", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_beaconTemplate ) },
{ "ArmyTooltip", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_tooltip ) },
{ "Features", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strGeneralFeatures ) },
{ "MedallionRegular", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionNormal ) },
{ "MedallionHilite", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionHilite ) },
{ "MedallionSelect", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionSelected ) },
{ NULL, NULL, NULL, 0 },
};
return TheFieldParseTable;
}
AsciiString PlayerTemplate::getStartingUnit( Int i ) const
{
if (i<0 || i>= MAX_MP_STARTING_UNITS)
return AsciiString::TheEmptyString;
return m_startingUnits[i];
}
//-------------------------------------------------------------------------------------------------
// This is is a Template, and a percent change to the cost of producing it.
/*static*/ void PlayerTemplate::parseProductionCostChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
self->m_productionCostChanges[buildTemplateKey] = percentChange;
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseProductionTimeChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
self->m_productionTimeChanges[buildTemplateKey] = percentChange;
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseProductionVeterancyLevel( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
// Format is ThingTemplatename VeterancyName
AsciiString HACK = AsciiString(ini->getNextToken());
NameKeyType buildTemplateKey = NAMEKEY(HACK.str());
VeterancyLevel startLevel = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), TheVeterancyNames);
self->m_productionVeterancyLevels[buildTemplateKey] = startLevel;
}
//-------------------------------------------------------------------------------------------------
/** Parse integer money and deposit in the m_money */
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseStartMoney( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
Int money = 0;
// parse the money as a regular "FIELD = <integer>"
INI::parseInt( ini, instance, &money, NULL );
// assign the money into the 'Money' (m_money) pointed to at 'store'
Money *theMoney = (Money *)store;
theMoney->init();
theMoney->deposit( money );
} // end parseStartMoney
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
PlayerTemplate::PlayerTemplate() :
m_nameKey(NAMEKEY_INVALID),
m_observer(false),
m_playableSide(false),
m_oldFaction(false),
m_intrinsicSPP(0),
m_specialPowerShortcutButtonCount(0)
{
m_preferredColor.red = m_preferredColor.green = m_preferredColor.blue = 0.0f;
m_beaconTemplate.clear();
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getHeadWaterMarkImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_headWaterMark);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getFlagWaterMarkImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_flagWaterMark);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getSideIconImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_sideIconImage);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getGeneralImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_generalImage);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getEnabledImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_enabledImage);
}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getDisabledImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_disabledImage);
//}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getHiliteImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_hiliteImage);
//}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getPushedImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_pushedImage);
//}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/*extern*/ PlayerTemplateStore *ThePlayerTemplateStore = NULL;
//-----------------------------------------------------------------------------
PlayerTemplateStore::PlayerTemplateStore()
{
// nothing
}
//-----------------------------------------------------------------------------
PlayerTemplateStore::~PlayerTemplateStore()
{
// nothing
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::init()
{
m_playerTemplates.clear();
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::reset()
{
// don't reset this list here; we want to retain this info.
// m_playerTemplates.clear();
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::update()
{
// nothing
}
Int PlayerTemplateStore::getTemplateNumByName(AsciiString name) const
{
for (Int num = 0; num < m_playerTemplates.size(); num++)
{
if (m_playerTemplates[num].getName().compareNoCase(name.str()) == 0)
return num;
}
DEBUG_ASSERTCRASH(NULL, ("Template doesn't exist for given name"));
return -1;
}
//-----------------------------------------------------------------------------
const PlayerTemplate* PlayerTemplateStore::findPlayerTemplate(NameKeyType namekey) const
{
// begin ugly, hokey code to quietly load old maps...
static NameKeyType a0 = NAMEKEY("FactionAmerica");
static NameKeyType a1 = NAMEKEY("FactionAmericaChooseAGeneral");
static NameKeyType a2 = NAMEKEY("FactionAmericaTankCommand");
static NameKeyType a3 = NAMEKEY("FactionAmericaSpecialForces");
static NameKeyType a4 = NAMEKEY("FactionAmericaAirForce");
static NameKeyType c0 = NAMEKEY("FactionChina");
static NameKeyType c1 = NAMEKEY("FactionChinaChooseAGeneral");
static NameKeyType c2 = NAMEKEY("FactionChinaRedArmy");
static NameKeyType c3 = NAMEKEY("FactionChinaSpecialWeapons");
static NameKeyType c4 = NAMEKEY("FactionChinaSecretPolice");
static NameKeyType g0 = NAMEKEY("FactionGLA");
static NameKeyType g1 = NAMEKEY("FactionGLAChooseAGeneral");
static NameKeyType g2 = NAMEKEY("FactionGLATerrorCell");
static NameKeyType g3 = NAMEKEY("FactionGLABiowarCommand");
static NameKeyType g4 = NAMEKEY("FactionGLAWarlordCommand");
if (namekey == a1 || namekey == a2 || namekey == a3 || namekey == a4)
namekey = a0;
else if (namekey == c1 || namekey == c2 || namekey == c3 || namekey == c4)
namekey = c0;
else if (namekey == g1 || namekey == g2 || namekey == g3 || namekey == g4)
namekey = g0;
// end ugly, hokey code to quietly load old maps...
#ifdef _DEBUG
AsciiString nn = KEYNAME(namekey);
#endif
for (PlayerTemplateVector::const_iterator it = m_playerTemplates.begin(); it != m_playerTemplates.end(); ++it)
{
#ifdef _DEBUG
AsciiString n = KEYNAME((*it).getNameKey());
#endif
if ((*it).getNameKey() == namekey)
return &(*it);
}
return NULL;
}
//-----------------------------------------------------------------------------
const PlayerTemplate* PlayerTemplateStore::getNthPlayerTemplate(Int i) const
{
if (i >= 0 && i < m_playerTemplates.size())
return &m_playerTemplates[i];
return NULL;
}
//-------------------------------------------------------------------------------------------------
// @todo: PERF_EVALUATE Get a perf timer on this.
// If this function is called frequently, there are some relatively trivial changes we could make to
// have it run a lot faster.
void PlayerTemplateStore::getAllSideStrings(AsciiStringList *outStringList)
{
if (!outStringList)
return;
// should outStringList be cleared first? If so, that would go here
AsciiStringList tmpList;
Int numTemplates = getPlayerTemplateCount();
for ( Int i = 0; i < numTemplates; ++i )
{
const PlayerTemplate *pt = getNthPlayerTemplate(i);
// Sanity
if (!pt)
continue;
if (std::find(tmpList.begin(), tmpList.end(), pt->getSide()) == tmpList.end())
tmpList.push_back(pt->getSide());
}
// tmpList is now filled with all unique sides found in the player templates.
// splice is a constant-time function which takes all elements from tmpList and
// inserts them before outStringList->end(), and also removes them from tmpList
outStringList->splice(outStringList->end(), tmpList);
// all done
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplateStore::parsePlayerTemplateDefinition( INI* ini )
{
const char* c = ini->getNextToken();
NameKeyType namekey = NAMEKEY(c);
PlayerTemplate* pt = const_cast<PlayerTemplate*>(ThePlayerTemplateStore->findPlayerTemplate(namekey));
if (pt)
{
ini->initFromINI(pt, pt->getFieldParse() );
pt->setNameKey(namekey);
}
else
{
PlayerTemplate npt;
ini->initFromINI( &npt, npt.getFieldParse() );
npt.setNameKey(namekey);
ThePlayerTemplateStore->m_playerTemplates.push_back(npt);
}
}
//-------------------------------------------------------------------------------------------------
void INI::parsePlayerTemplateDefinition( INI* ini )
{
PlayerTemplateStore::parsePlayerTemplateDefinition(ini);
}

View File

@@ -0,0 +1,322 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ProductionPrerequisite.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: ProductionPrerequisite.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/ProductionPrerequisite.h"
#include "Common/Player.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "GameLogic/Object.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameText.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
ProductionPrerequisite::ProductionPrerequisite()
{
init();
}
//-----------------------------------------------------------------------------
ProductionPrerequisite::~ProductionPrerequisite()
{
}
//-----------------------------------------------------------------------------
void ProductionPrerequisite::init()
{
m_prereqUnits.clear();
m_prereqSciences.clear();
}
//=============================================================================
void ProductionPrerequisite::resolveNames()
{
for (Int i = 0; i < m_prereqUnits.size(); i++)
{
//
// note that this will find the template at the "top most" level (not override
// sub-temlates), which is what we want ... we conceptually only have one
// template for any given thing, it's only the *data* that is overridden
//
if( m_prereqUnits[ i ].name.isNotEmpty() )
{
m_prereqUnits[i].unit = TheThingFactory->findTemplate(m_prereqUnits[i].name); // might be null
/** @todo for now removing this assert until we can completely remove
the GDF stuff, the problem is that some INI files refer to GDF names, and they
aren't yet loaded in the world builder but will all go away later anyway etc */
DEBUG_ASSERTCRASH(m_prereqUnits[i].unit,("could not find prereq %s\n",m_prereqUnits[i].name.str()));
m_prereqUnits[i].name.clear(); // we're done with it
}
}
}
//-----------------------------------------------------------------------------
Int ProductionPrerequisite::calcNumPrereqUnitsOwned(const Player *player, Int counts[MAX_PREREQ]) const
{
const ThingTemplate *tmpls[MAX_PREREQ];
Int cnt = m_prereqUnits.size();
if (cnt > MAX_PREREQ)
cnt = MAX_PREREQ;
for (int i = 0; i < cnt; i++)
tmpls[i] = m_prereqUnits[i].unit;
player->countObjectsByThingTemplate(cnt, tmpls, false, counts);
return cnt;
}
//-----------------------------------------------------------------------------
Int ProductionPrerequisite::getAllPossibleBuildFacilityTemplates(const ThingTemplate* tmpls[], Int maxtmpls) const
{
Int count = 0;
for (int i = 0; i < m_prereqUnits.size(); i++)
{
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
break;
if (count >= maxtmpls)
break;
tmpls[count++] = m_prereqUnits[i].unit;
}
return count;
}
//-----------------------------------------------------------------------------
const ThingTemplate *ProductionPrerequisite::getExistingBuildFacilityTemplate( const Player *player ) const
{
DEBUG_ASSERTCRASH(player, ("player may not be null"));
if (m_prereqUnits.size())
{
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
for (int i = 0; i < cnt; i++)
{
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
break;
if (ownCount[i])
return m_prereqUnits[i].unit;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
Bool ProductionPrerequisite::isSatisfied(const Player *player) const
{
Int i;
if (!player)
return false;
// gotta have all the prereq sciences.
for (i = 0; i < m_prereqSciences.size(); i++)
{
if (!player->hasScience(m_prereqSciences[i]))
return false;
}
// the player must have at least one instance of each prereq unit.
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
// fix up the "or" cases. (start at 1!)
for (i = 1; i < cnt; i++)
{
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
{
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
ownCount[i-1] = -1; // flag for "ignore me"
}
}
for (i = 0; i < cnt; i++)
{
if (ownCount[i] == -1) // the magic "ignore me" flag
continue;
if (ownCount[i] == 0) // everything not ignored, is required
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
* to be an alternate prereq to the previously added unit, otherwise this becomes
* a new 'block' and is required in ADDDITION to other entries.
* Return FALSE if no space left to add unit */
//-------------------------------------------------------------------------------------------------
void ProductionPrerequisite::addUnitPrereq( AsciiString unit, Bool orUnitWithPrevious )
{
PrereqUnitRec info;
info.name = unit;
info.flags = orUnitWithPrevious ? UNIT_OR_WITH_PREV : 0;
info.unit = NULL;
m_prereqUnits.push_back(info);
} // end addUnitPrereq
//-------------------------------------------------------------------------------------------------
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
* to be an alternate prereq to the previously added unit, otherwise this becomes
* a new 'block' and is required in ADDDITION to other entries.
* Return FALSE if no space left to add unit */
//-------------------------------------------------------------------------------------------------
void ProductionPrerequisite::addUnitPrereq( const std::vector<AsciiString>& units )
{
Bool orWithPrevious = false;
for (int i = 0; i < units.size(); ++i)
{
addUnitPrereq(units[i], orWithPrevious);
orWithPrevious = true;
}
} // end addUnitPrereq
//-------------------------------------------------------------------------------------------------
// returns an asciistring which is a list of all the prerequisites
// not satisfied yet
UnicodeString ProductionPrerequisite::getRequiresList(const Player *player) const
{
// if player is invalid, return empty string
if (!player)
return UnicodeString::TheEmptyString;
UnicodeString requiresList = UnicodeString::TheEmptyString;
// check the prerequired units
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
Int i;
Bool orRequirements[MAX_PREREQ];
//Added for fix below in getRequiresList
//By Sadullah Nader
//Initializes the OR_WITH_PREV structures
for (i = 0; i < MAX_PREREQ; i++)
{
orRequirements[i] = FALSE;
}
//
// account for the "or" unit cases, start for loop at 1
for (i = 1; i < cnt; i++)
{
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
{
orRequirements[i] = TRUE; // set the flag for this unit to be "ored" with previous
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
ownCount[i-1] = -1; // flag for "ignore me"
}
}
// check to see if anything is required
const ThingTemplate *unit;
UnicodeString unitName;
Bool firstRequirement = true;
for (i = 0; i < cnt; i++)
{
// we have an unfulfilled requirement
if (ownCount[i] == 0) {
if(orRequirements[i])
{
unit = m_prereqUnits[i-1].unit;
unitName = unit->getDisplayName();
unitName.concat( L" " );
unitName.concat(TheGameText->fetch("CONTROLBAR:OrRequirement", NULL));
unitName.concat( L" " );
requiresList.concat(unitName);
}
// get the requirement and then its name
unit = m_prereqUnits[i].unit;
unitName = unit->getDisplayName();
// gets command button, and then modifies unitName
//CommandButton *cmdButton = TheControlBar->findCommandButton(unit->getName());
//if (cmdButton)
//unitName.translate(TheGameText->fetch(cmdButton->m_textLabel.str()));
// format name appropriately with 'returns' if necessary
if (firstRequirement)
firstRequirement = false;
else
unitName.concat(L"\n");
// add it to the list
requiresList.concat(unitName);
}
}
Bool hasSciences = TRUE;
// gotta have all the prereq sciences.
for (i = 0; i < m_prereqSciences.size(); i++)
{
if (!player->hasScience(m_prereqSciences[i]))
hasSciences = FALSE;
}
if (hasSciences == FALSE) {
if (firstRequirement) {
firstRequirement = false;
} else {
unitName.concat(L"\n");
}
requiresList.concat(TheGameText->fetch("CONTROLBAR:GeneralsPromotion", NULL));
}
// return final list
return requiresList;
}

View File

@@ -0,0 +1,291 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ResourceGatheringManager.cpp ///////////////////////////////////////////////////////////
// The part of a Player's brain that keeps track of all Resource type Objects and makes
// gathering type decisions based on them.
// Author: Graham Smallwood, January, 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/ResourceGatheringManager.h"
#include "Common/ActionManager.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Module/SupplyTruckAIUpdate.h"
#include "GameLogic/Module/SupplyCenterDockUpdate.h"
#include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
#include "GameLogic/Module/UpdateModule.h"
ResourceGatheringManager::ResourceGatheringManager()
{
}
ResourceGatheringManager::~ResourceGatheringManager()
{
m_supplyWarehouses.erase( m_supplyWarehouses.begin(), m_supplyWarehouses.end() );
m_supplyCenters.erase( m_supplyCenters.begin(), m_supplyCenters.end() );
}
void ResourceGatheringManager::addSupplyCenter( Object *newCenter )
{
if( newCenter == NULL )
return;
m_supplyCenters.push_back( newCenter->getID() );
}
void ResourceGatheringManager::removeSupplyCenter( Object *oldCenter )
{
if( oldCenter == NULL )
return;
ObjectID targetID = oldCenter->getID();
objectIDListIterator iterator = m_supplyCenters.begin();
while( iterator != m_supplyCenters.end() )
{
if( targetID == *iterator )
{
iterator = m_supplyCenters.erase( iterator );
}
else
iterator++;
}
}
void ResourceGatheringManager::addSupplyWarehouse( Object *newWarehouse )
{
if( newWarehouse == NULL )
return;
m_supplyWarehouses.push_back( newWarehouse->getID() );
}
void ResourceGatheringManager::removeSupplyWarehouse( Object *oldWarehouse )
{
if( oldWarehouse == NULL )
return;
ObjectID targetID = oldWarehouse->getID();
objectIDListIterator iterator = m_supplyWarehouses.begin();
while( iterator != m_supplyWarehouses.end() )
{
if( targetID == *iterator )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
iterator++;
}
}
static Real computeRelativeCost( Object *queryObject, Object *destObject, Real *pureDistanceSquared )
{
/** @todo This gets filled with Pathfinding computations, analysis of Boxes remaining,
Threat calculations, paths of other trucks, and other fancy stuff.
*/
//A good score is a very small number.
if( queryObject == NULL || destObject == NULL )
return FLT_MAX;
if( !TheActionManager->canTransferSuppliesAt(queryObject, destObject) )
return FLT_MAX;// Handles emptyness and alliances
DockUpdateInterface *dockInterface = destObject->getDockUpdateInterface();
if( !dockInterface->isClearToApproach( queryObject ) )
return FLT_MAX;
// since we don't care about the distance as a distance per se, but rather as
// a goodness-factor, save some time by getting the dist-sqr (srj)
Real distSquared = ThePartitionManager->getDistanceSquared(queryObject, destObject, FROM_CENTER_3D);
// I need the distance, but I don't want to count on the coincidence that
// the abstract 'cost' this function returns happens to be just the distance, since it could
// become more complicated
if( pureDistanceSquared )
*pureDistanceSquared = distSquared;
return distSquared;
}
Object *ResourceGatheringManager::findBestSupplyWarehouse( Object *queryObject )
{
Object *bestWarehouse = NULL;
Real maxDistanceSquared = 100000;
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
return NULL;
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
if( supplyTruckAI )
{
// Check for a dock override being set.
ObjectID dockID = supplyTruckAI->getPreferredDockID();
Object *dock = TheGameLogic->findObjectByID(dockID);
if( dock )
{
static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_warehouseUpdate );
//If remotely okay, let User win.
if( warehouseModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
return dock;
}
// Please note, there is not a separate Warehouse and Center memory by Design. Because
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
// practical realization has been made that you do not want separate memory.
// Design wants a harvester to give up and return to base if it is "too far" to the warehouse.
// Note, the "PreferedDock" will override this, and there is no distance max on Centers.
maxDistanceSquared = supplyTruckAI->getWarehouseScanDistance() * supplyTruckAI->getWarehouseScanDistance();
}
//Otherwise, search for a good one.
Real bestCost = FLT_MAX;
objectIDListIterator iterator = m_supplyWarehouses.begin();
while( iterator != m_supplyWarehouses.end() )
{
ObjectID currentID = *iterator;
Object *currentWarehouse =TheGameLogic->findObjectByID(currentID);
if( currentWarehouse == NULL )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
{
Real distanceSquared;
Real currentCost = computeRelativeCost( queryObject, currentWarehouse, &distanceSquared );
if( (currentCost < bestCost) && (distanceSquared < maxDistanceSquared) )
{
bestWarehouse = currentWarehouse;
bestCost = currentCost;
}
iterator++;
}
}
return bestWarehouse;
}
Object *ResourceGatheringManager::findBestSupplyCenter( Object *queryObject )
{
Object *bestCenter = NULL;
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
return NULL;
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
if( supplyTruckAI )
{
// Check for a dock override being set.
ObjectID dockID = supplyTruckAI->getPreferredDockID();
Object *dock = TheGameLogic->findObjectByID(dockID);
if( dock )
{
static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
SupplyWarehouseDockUpdate *centerModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_centerUpdate );
//If remotely okay, let User win.
if( centerModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
return dock;
}
// Please note, there is not a separate Warehouse and Center memory by Design. Because
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
// practical realization has been made that you do not want separate memory.
}
//Otherwise, search for a good one.
Real bestCost = FLT_MAX;
objectIDListIterator iterator = m_supplyCenters.begin();
while( iterator != m_supplyCenters.end() )
{
ObjectID currentID = *iterator;
Object *currentCenter =TheGameLogic->findObjectByID(currentID);
if( currentCenter == NULL )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
{
Real currentCost = computeRelativeCost( queryObject, currentCenter, NULL );
if( currentCost < bestCost )
{
bestCenter = currentCenter;
bestCost = currentCost;
}
iterator++;
}
}
return bestCenter;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// supply warehouses
xfer->xferSTLObjectIDList( &m_supplyWarehouses );
// supply centers
xfer->xferSTLObjectIDList( &m_supplyCenters );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,383 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Science.cpp /////////////////////////////////////////////////////////
// Created: Steven Johnson, October 2001
// Desc: @todo
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/Player.h"
#include "Common/Science.h"
ScienceStore* TheScienceStore = NULL;
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
void ScienceStore::init()
{
DEBUG_ASSERTCRASH(m_sciences.empty(), ("Hmm"));
m_sciences.clear();
}
//-----------------------------------------------------------------------------
ScienceStore::~ScienceStore()
{
// nope.
//m_sciences.clear();
// go through all sciences and delete any overrides
for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/)
{
ScienceInfo* si = *it;
++it;
if (si) {
si->deleteInstance();
}
}
}
//-----------------------------------------------------------------------------
void ScienceStore::reset()
{
// nope.
//m_sciences.clear();
// go through all sciences and delete any overrides
for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/)
{
ScienceInfo* si = *it;
Overridable* temp = si->deleteOverrides();
if (!temp)
{
it = m_sciences.erase(it);
}
else
{
++it;
}
}
}
//-----------------------------------------------------------------------------
ScienceType ScienceStore::getScienceFromInternalName(const AsciiString& name) const
{
if (name.isEmpty())
return SCIENCE_INVALID;
NameKeyType nkt = TheNameKeyGenerator->nameToKey(name);
ScienceType st = (ScienceType)nkt;
return st;
}
//-----------------------------------------------------------------------------
AsciiString ScienceStore::getInternalNameForScience(ScienceType science) const
{
if (science == SCIENCE_INVALID)
return AsciiString::TheEmptyString;
NameKeyType nk = (NameKeyType)(science);
return TheNameKeyGenerator->keyToName(nk);
}
//-----------------------------------------------------------------------------
// return a vector of all the currently-known science names
// NOTE: this is really only for use by WorldBuilder! Please
// do not use it in RTS!
std::vector<AsciiString> ScienceStore::friend_getScienceNames() const
{
std::vector<AsciiString> v;
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
NameKeyType nk = (NameKeyType)(si->m_science);
v.push_back(TheNameKeyGenerator->keyToName(nk));
}
return v;
}
//-----------------------------------------------------------------------------
void ScienceInfo::addRootSciences(ScienceVec& v) const
{
if (m_prereqSciences.empty())
{
// we're a root. add ourselves.
if (std::find(v.begin(), v.end(), m_science) == v.end())
v.push_back(m_science);
}
else
{
// we're not a root. add the roots of all our prereqs.
for (ScienceVec::const_iterator it = m_prereqSciences.begin(); it != m_prereqSciences.end(); ++it)
{
const ScienceInfo* si = TheScienceStore->findScienceInfo(*it);
if (si)
si->addRootSciences(v);
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
const ScienceInfo* ScienceStore::findScienceInfo(ScienceType st) const
{
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
if (si->m_science == st)
{
return si;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
/*static*/ void ScienceStore::friend_parseScienceDefinition( INI* ini )
{
const char* c = ini->getNextToken();
NameKeyType nkt = NAMEKEY(c);
ScienceType st = (ScienceType)nkt;
if (TheScienceStore)
{
static const FieldParse myFieldParse[] =
{
{ "PrerequisiteSciences", INI::parseScienceVector, NULL, offsetof( ScienceInfo, m_prereqSciences ) },
{ "SciencePurchasePointCost", INI::parseInt, NULL, offsetof( ScienceInfo, m_sciencePurchasePointCost ) },
{ "IsGrantable", INI::parseBool, NULL, offsetof( ScienceInfo, m_grantable ) },
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_name) },
{ "Description", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_description) },
{ 0, 0, 0, 0 }
};
ScienceInfo* info = NULL;
// see if the science already exists. (can't use findScienceInfo() since it is const and should remain so.)
for (ScienceInfoVec::iterator it = TheScienceStore->m_sciences.begin(); it != TheScienceStore->m_sciences.end(); ++it)
{
// note that we don't use getFinalOverride here. this is correct and as-desired.
if ((*it)->m_science == st)
{
info = *it;
break;
}
}
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
{
ScienceInfo* newInfo = newInstance(ScienceInfo);
if (info == NULL)
{
// only add if it's not overriding an existing one.
info = newInfo;
info->markAsOverride(); // yep, so we will get cleared on reset()
TheScienceStore->m_sciences.push_back(info);
}
else
{
// copy data from final override to 'newInfo' as a set of initial default values
info = (ScienceInfo*)(info->friend_getFinalOverride());
*newInfo = *info;
info->setNextOverride(newInfo);
newInfo->markAsOverride(); // must do AFTER the copy
// use the newly created override for us to set values with etc
info = newInfo;
//TheScienceStore->m_sciences.push_back(info); // NO, BAD, WRONG -- don't add in this case.
}
}
else
{
if (info != NULL)
{
DEBUG_CRASH(("duplicate science %s!\n",c));
throw INI_INVALID_DATA;
}
info = newInstance(ScienceInfo);
TheScienceStore->m_sciences.push_back(info);
}
ini->initFromINI(info, myFieldParse);
info->m_science = st;
info->addRootSciences(info->m_rootSciences);
}
}
//-----------------------------------------------------------------------------
Int ScienceStore::getSciencePurchaseCost(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
return si->m_sciencePurchasePointCost;
}
else
{
return 0;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::isScienceGrantable(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
return si->m_grantable;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::getNameAndDescription(ScienceType st, UnicodeString& name, UnicodeString& description) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
name = si->m_name;
description = si->m_description;
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::playerHasPrereqsForScience(const Player* player, ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
for (ScienceVec::const_iterator it2 = si->m_prereqSciences.begin(); it2 != si->m_prereqSciences.end(); ++it2)
{
if (!player->hasScience(*it2))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::playerHasRootPrereqsForScience(const Player* player, ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
for (ScienceVec::const_iterator it2 = si->m_rootSciences.begin(); it2 != si->m_rootSciences.end(); ++it2)
{
if (!player->hasScience(*it2))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
/** return a list of the sciences the given player can purchase now, and a list he might be able to purchase in the future,
but currently lacks prereqs or points for. (either might be an empty list) */
void ScienceStore::getPurchasableSciences(const Player* player, ScienceVec& purchasable, ScienceVec& potentiallyPurchasable) const
{
purchasable.clear();
potentiallyPurchasable.clear();
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
if (si->m_sciencePurchasePointCost == 0)
{
// 0 means "cannot be purchased"
continue;
}
if (player->hasScience(si->m_science))
{
continue;
}
if (playerHasPrereqsForScience(player, si->m_science))
{
purchasable.push_back(si->m_science);
}
else if (playerHasRootPrereqsForScience(player, si->m_science))
{
potentiallyPurchasable.push_back(si->m_science);
}
}
}
//-----------------------------------------------------------------------------
// this is intended ONLY for use by INI::scanScience.
// Don't use it anywhere else. In particular, never, ever, ever
// call this with a hardcoded science name. (srj)
ScienceType ScienceStore::friend_lookupScience(const char* scienceName) const
{
NameKeyType nkt = NAMEKEY(scienceName);
ScienceType st = (ScienceType)nkt;
if (!isValidScience(st))
{
DEBUG_CRASH(("Science name %s not known! (Did you define it in Science.ini?)",scienceName));
throw INI_INVALID_DATA;
}
return st;
}
//-----------------------------------------------------------------------------
Bool ScienceStore::isValidScience(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
return si != NULL;
}
//-----------------------------------------------------------------------------
void INI::parseScienceDefinition( INI* ini )
{
ScienceStore::friend_parseScienceDefinition(ini);
}

View File

@@ -0,0 +1,568 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ScoreKeeper.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// created: Jun 2002
//
// Filename: ScoreKeeper.cpp
//
// author: Chris Huybregts
//
// purpose: Score Keeper class will be an object attached to each player
// that will maintain accurate counts for the various stats we
// want to show on the score screen. The information in here
// could also be used for the observer screen
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameState.h"
#include "Common/KindOf.h"
#include "Common/Player.h"
#include "Common/ScoreKeeper.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/Object.h"
#include "GameLogic/GameLogic.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
ScoreKeeper::ScoreKeeper( void )
{
reset(0);
}
ScoreKeeper::~ScoreKeeper( void )
{
}
static KindOfMaskType scoringBuildingMask;
static KindOfMaskType scoringBuildingDestroyMask;
static KindOfMaskType scoringBuildingCreateMask;
void ScoreKeeper::reset( Int playerIdx )
{
scoringBuildingMask.set(KINDOF_STRUCTURE);
scoringBuildingMask.set(KINDOF_SCORE);
scoringBuildingCreateMask.set(KINDOF_STRUCTURE);
scoringBuildingCreateMask.set(KINDOF_SCORE_CREATE);
scoringBuildingDestroyMask.set(KINDOF_STRUCTURE);
scoringBuildingDestroyMask.set(KINDOF_SCORE_DESTROY);
m_totalMoneyEarned = m_totalMoneySpent = 0;
m_totalUnitsLost = m_totalUnitsBuilt = 0;
m_totalBuildingsLost = m_totalBuildingsBuilt = 0;
//Added By Sadullah Nader
//Initializtion(s) inserted
m_totalFactionBuildingsCaptured = m_totalTechBuildingsCaptured = 0;
//
m_currentScore = 0;
m_objectsBuilt.clear();
m_objectsCaptured.clear();
m_objectsLost.clear();
for(int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
m_objectsDestroyed[i].clear();
m_totalBuildingsDestroyed[i] = m_totalUnitsDestroyed[i] = 0;
}
m_myPlayerIdx = playerIdx;
}
void ScoreKeeper::addObjectBuilt( const Object *o)
{
Bool addToCount = FALSE;
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
++m_totalBuildingsBuilt;
addToCount = TRUE;
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
{
++m_totalBuildingsBuilt;
addToCount = TRUE;
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
{
++m_totalUnitsBuilt;
addToCount = TRUE;
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
if (it != m_objectsBuilt.end())
existingCount = it->second;
m_objectsBuilt[o->getTemplate()] = existingCount + 1;
}
}
Int ScoreKeeper::getTotalUnitsBuilt( KindOfMaskType validMask, KindOfMaskType invalidMask )
{
Int count = 0;
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
{
const ThingTemplate *theTemplate = it->first;
Int numBuilt = it->second;
if (theTemplate && theTemplate->isKindOfMulti(validMask, invalidMask))
count += numBuilt;
}
return count;
}
Int ScoreKeeper::getTotalObjectsBuilt( const ThingTemplate *pTemplate )
{
Int count = 0;
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
{
const ThingTemplate *theTemplate = it->first;
if (theTemplate->isEquivalentTo(pTemplate))
++count;
}
return count;
}
void ScoreKeeper::removeObjectBuilt( const Object *o)
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool removeFromCount = FALSE;
if (o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
--m_totalBuildingsBuilt;
removeFromCount = TRUE;
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
{
--m_totalBuildingsBuilt;
removeFromCount = TRUE;
}
else if (o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
{
--m_totalUnitsBuilt;
removeFromCount = TRUE;
}
}
if (removeFromCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
if (it != m_objectsBuilt.end())
existingCount = it->second;
m_objectsBuilt[o->getTemplate()] = existingCount - 1;
}
}
void ScoreKeeper::addObjectCaptured( const Object *o )
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOf(KINDOF_STRUCTURE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE))
{
++m_totalFactionBuildingsCaptured;
}
else
{
++m_totalTechBuildingsCaptured;
}
addToCount = TRUE;
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsCaptured.find(o->getTemplate());
if (it != m_objectsCaptured.end())
existingCount = it->second;
m_objectsCaptured[o->getTemplate()] = existingCount + 1;
}
}
void ScoreKeeper::addObjectDestroyed( const Object *o)
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Int playerIdx = o->getControllingPlayer()->getPlayerIndex();
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsDestroyed[playerIdx];
addToCount = TRUE;
}
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsDestroyed[playerIdx];
addToCount = TRUE;
}
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
m_totalUnitsDestroyed[playerIdx]++;
addToCount = TRUE;
}
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsDestroyed[playerIdx].find(o->getTemplate());
if (it != m_objectsDestroyed[playerIdx].end())
existingCount = it->second;
m_objectsDestroyed[playerIdx][o->getTemplate()] = existingCount + 1;
}
}
void ScoreKeeper::addObjectLost( const Object *o )
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsLost;
addToCount = TRUE;
}
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsLost;
addToCount = TRUE;
}
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalUnitsLost;
addToCount = TRUE;
}
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsLost.find(o->getTemplate());
if (it != m_objectsLost.end())
existingCount = it->second;
m_objectsLost[o->getTemplate()] = existingCount + 1;
}
}
Int ScoreKeeper::calculateScore( void )
{
Int score = 0;
score += m_totalUnitsBuilt * 100;
score += m_totalMoneyEarned;
score += m_totalBuildingsBuilt * 100;
for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
if(i == m_myPlayerIdx)
continue;
score += m_totalUnitsDestroyed[i] * 100;
score += m_totalBuildingsDestroyed[i] * 100;
}
m_currentScore = score;
return m_currentScore;
}
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
Int ScoreKeeper::getTotalBuildingsDestroyed( void )
{
int count = 0;
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
{
// Design change, display even if we killed our own
// if(i == m_myPlayerIdx)
// continue;
count += m_totalBuildingsDestroyed[i];
//for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
// {
//
// count += it->second;
// }
}
return count;
}
Int ScoreKeeper::getTotalUnitsDestroyed( void )
{
int count = 0;
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
{
// Design change, display even if we killed our own
// if(i == m_myPlayerIdx)
// continue;
count += m_totalUnitsDestroyed[i];
// for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
// {
// }
}
return count;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::crc( Xfer *xfer )
{
} // end ScoreKeeper
// ------------------------------------------------------------------------------------------------
/** Xfer of an object count map
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::xferObjectCountMap( Xfer *xfer, ObjectCountMap *map )
{
// sanity
if( map == NULL )
{
DEBUG_CRASH(( "xferObjectCountMap - Invalid map parameter\n" ));
throw SC_INVALID_DATA;
} // end if
// version info
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// size of the map
UnsignedShort mapSize = map->size();
xfer->xferUnsignedShort( &mapSize );
// map data
Int count;
const ThingTemplate *thingTemplate;
AsciiString thingTemplateName;
if( xfer->getXferMode() == XFER_SAVE )
{
ObjectCountMapIt it;
// save all entries
for( it = map->begin(); it != map->end(); ++it )
{
// thing template
thingTemplate = it->first;
thingTemplateName = thingTemplate->getName();
xfer->xferAsciiString( &thingTemplateName );
// the count
count = it->second;
xfer->xferInt( &count );
} // end for, it
} // end if, save
else
{
// read all entries
for( UnsignedShort i = 0; i < mapSize; ++i )
{
// read thing template name
xfer->xferAsciiString( &thingTemplateName );
thingTemplate = TheThingFactory->findTemplate( thingTemplateName );
if( thingTemplate == NULL )
{
DEBUG_CRASH(( "xferObjectCountMap - Unknown thing template '%s'\n", thingTemplateName.str() ));
throw SC_INVALID_DATA;
} // end if
// read count
xfer->xferInt( &count );
// add to map
(*map)[ thingTemplate ] = count;
} // end for, i
} // end else
} // end xferObjectCountMap
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// money earned
xfer->xferInt( &m_totalMoneyEarned );
// money spent
xfer->xferInt( &m_totalMoneySpent );
// units destroyed
xfer->xferUser( m_totalUnitsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
// units built
xfer->xferInt( &m_totalUnitsBuilt );
// units lost
xfer->xferInt( &m_totalUnitsLost );
// buildings destroyed
xfer->xferUser( m_totalBuildingsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
// buildings built
xfer->xferInt( &m_totalBuildingsBuilt );
// buildings lost
xfer->xferInt( &m_totalBuildingsLost );
// tech buildings captured
xfer->xferInt( &m_totalTechBuildingsCaptured );
// faction buildings captured
xfer->xferInt( &m_totalFactionBuildingsCaptured );
// current score
xfer->xferInt( &m_currentScore );
// player index
xfer->xferInt( &m_myPlayerIdx );
// objects built
xferObjectCountMap( xfer, &m_objectsBuilt );
// objects destroyed
UnsignedShort destroyedArraySize = MAX_PLAYER_COUNT;
xfer->xferUnsignedShort( &destroyedArraySize );
if( destroyedArraySize != MAX_PLAYER_COUNT )
{
DEBUG_CRASH(( "ScoreKeeper::xfer - size of objects destroyed array has changed\n" ));
throw SC_INVALID_DATA;
} // end if
for( UnsignedShort i = 0; i < destroyedArraySize; ++i )
{
// xfer map data
xferObjectCountMap( xfer, &m_objectsDestroyed[ i ] );
} // end for i
// objects lost
xferObjectCountMap( xfer, &m_objectsLost );
// objects captured
xferObjectCountMap( xfer, &m_objectsCaptured );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,381 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: SpecialPower.cpp /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, April 2002
// Desc: Special power templates and the system that holds them
// Edited: Kris Morness -- July 2002 (added BitFlag system)
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Player.h"
#include "Common/Science.h"
#include "Common/SpecialPower.h"
#include "GameLogic/Object.h"
#include "Common/BitFlagsIO.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// GLOBAL /////////////////////////////////////////////////////////////////////////////////////////
SpecialPowerStore *TheSpecialPowerStore = NULL;
#define DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT (LOGICFRAMES_PER_SECOND * 10)
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Externs ////////////////////////////////////////////////////////////////////////////////////////
const char* SpecialPowerMaskType::s_bitNameList[] =
{
"SPECIAL_INVALID",
//Superweapons
"SPECIAL_DAISY_CUTTER",
"SPECIAL_PARADROP_AMERICA",
"SPECIAL_CARPET_BOMB",
"SPECIAL_CLUSTER_MINES",
"SPECIAL_EMP_PULSE",
"SPECIAL_NAPALM_STRIKE",
"SPECIAL_CASH_HACK",
"SPECIAL_NEUTRON_MISSILE",
"SPECIAL_SPY_SATELLITE",
"SPECIAL_DEFECTOR",
"SPECIAL_TERROR_CELL",
"SPECIAL_AMBUSH",
"SPECIAL_BLACK_MARKET_NUKE",
"SPECIAL_ANTHRAX_BOMB",
"SPECIAL_SCUD_STORM",
#ifdef ALLOW_DEMORALIZE
"SPECIAL_DEMORALIZE",
#else
"SPECIAL_DEMORALIZE_OBSOLETE",
#endif
"SPECIAL_CRATE_DROP",
"SPECIAL_A10_THUNDERBOLT_STRIKE",
"SPECIAL_DETONATE_DIRTY_NUKE",
"SPECIAL_ARTILLERY_BARRAGE",
//Special abilities
"SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES",
"SPECIAL_REMOTE_CHARGES",
"SPECIAL_TIMED_CHARGES",
"SPECIAL_HELIX_NAPALM_BOMB",
"SPECIAL_HACKER_DISABLE_BUILDING",
"SPECIAL_TANKHUNTER_TNT_ATTACK",
"SPECIAL_BLACKLOTUS_CAPTURE_BUILDING",
"SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK",
"SPECIAL_BLACKLOTUS_STEAL_CASH_HACK",
"SPECIAL_INFANTRY_CAPTURE_BUILDING",
"SPECIAL_RADAR_VAN_SCAN",
"SPECIAL_SPY_DRONE",
"SPECIAL_DISGUISE_AS_VEHICLE",
"SPECIAL_BOOBY_TRAP",
"SPECIAL_REPAIR_VEHICLES",
"SPECIAL_PARTICLE_UPLINK_CANNON",
"SPECIAL_CASH_BOUNTY",
"SPECIAL_CHANGE_BATTLE_PLANS",
"SPECIAL_CIA_INTELLIGENCE",
"SPECIAL_CLEANUP_AREA",
"SPECIAL_LAUNCH_BAIKONUR_ROCKET",
"SPECIAL_SPECTRE_GUNSHIP",
"SPECIAL_GPS_SCRAMBLER",
"SPECIAL_FRENZY",
"SPECIAL_SNEAK_ATTACK",
"SPECIAL_CHINA_CARPET_BOMB",
"EARLY_SPECIAL_CHINA_CARPET_BOMB",
"SPECIAL_LEAFLET_DROP",
"EARLY_SPECIAL_LEAFLET_DROP",
"EARLY_SPECIAL_FRENZY",
"SPECIAL_COMMUNICATIONS_DOWNLOAD",
"EARLY_SPECIAL_REPAIR_VEHICLES",
"SPECIAL_TANK_PARADROP",
"SUPW_SPECIAL_PARTICLE_UPLINK_CANNON",
"AIRF_SPECIAL_DAISY_CUTTER",
"NUKE_SPECIAL_CLUSTER_MINES",
"NUKE_SPECIAL_NEUTRON_MISSILE",
"AIRF_SPECIAL_A10_THUNDERBOLT_STRIKE",
"AIRF_SPECIAL_SPECTRE_GUNSHIP",
"INFA_SPECIAL_PARADROP_AMERICA",
"SLTH_SPECIAL_GPS_SCRAMBLER",
"AIRF_SPECIAL_CARPET_BOMB",
"SUPR_SPECIAL_CRUISE_MISSILE",
"LAZR_SPECIAL_PARTICLE_UPLINK_CANNON",
"SUPW_SPECIAL_NEUTRON_MISSILE",
"SPECIAL_BATTLESHIP_BOMBARDMENT",
NULL
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void SpecialPowerStore::parseSpecialPowerDefinition( INI *ini )
{
// read the name
AsciiString name = ini->getNextToken();
SpecialPowerTemplate* specialPower = TheSpecialPowerStore->findSpecialPowerTemplatePrivate( name );
if ( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
{
if (specialPower)
{
SpecialPowerTemplate* child = (SpecialPowerTemplate*)specialPower->friend_getFinalOverride();
specialPower = newInstance(SpecialPowerTemplate);
*specialPower = *child;
child->setNextOverride(specialPower);
specialPower->markAsOverride();
//TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower); // nope, do NOT do this
}
else
{
specialPower = newInstance(SpecialPowerTemplate);
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
if( defaultTemplate )
*specialPower = *defaultTemplate;
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
specialPower->markAsOverride();
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
}
}
else
{
if (specialPower)
{
throw INI_INVALID_DATA;
}
else
{
specialPower = newInstance(SpecialPowerTemplate);
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
if( defaultTemplate )
*specialPower = *defaultTemplate;
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
}
}
// parse the ini definition
if (specialPower)
ini->initFromINI( specialPower, specialPower->getFieldParse() );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/* static */ const FieldParse SpecialPowerTemplate::m_specialPowerFieldParse[] =
{
{ "ReloadTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_reloadTime ) },
{ "RequiredScience", INI::parseScience, NULL, offsetof( SpecialPowerTemplate, m_requiredScience ) },
{ "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateSound ) },
{ "InitiateAtLocationSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateAtLocationSound ) },
{ "PublicTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_publicTimer ) },
{ "Enum", INI::parseIndexList, SpecialPowerMaskType::getBitNames(), offsetof( SpecialPowerTemplate, m_type ) },
{ "DetectionTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_detectionTime ) },
{ "SharedSyncedTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_sharedNSync ) },
{ "ViewObjectDuration", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_viewObjectDuration ) },
{ "ViewObjectRange", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_viewObjectRange ) },
{ "RadiusCursorRadius", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_radiusCursorRadius ) },
{ "ShortcutPower", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_shortcutPower ) },
{ "AcademyClassify", INI::parseIndexList, TheAcademyClassificationTypeNames, offsetof( SpecialPowerTemplate, m_academyClassificationType ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate::SpecialPowerTemplate()
{
m_id = 0;
m_type = SPECIAL_INVALID;
m_reloadTime = 0;
m_requiredScience = SCIENCE_INVALID;
m_publicTimer = FALSE;
m_detectionTime = DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT;
m_sharedNSync = FALSE;
m_viewObjectDuration = 0;
m_viewObjectRange = 0;
m_radiusCursorRadius = 0;
m_shortcutPower = FALSE;
} // end SpecialPowerTemplate
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate::~SpecialPowerTemplate()
{
} // end ~SpecialPowerTemplate
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerStore::SpecialPowerStore( void )
{
m_nextSpecialPowerID = 0;
} // end SpecialPowerStore
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerStore::~SpecialPowerStore( void )
{
// delete all templates
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
m_specialPowerTemplates[ i ]->deleteInstance();
// erase the list
m_specialPowerTemplates.clear();
// set our count to zero
m_nextSpecialPowerID = 0;
} // end ~SpecialPowerStore
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate* SpecialPowerStore::findSpecialPowerTemplatePrivate( AsciiString name )
{
// search the template list for matching name
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
if( m_specialPowerTemplates[ i ]->getName() == name )
return m_specialPowerTemplates[ i ];
return NULL; // not found
}
//-------------------------------------------------------------------------------------------------
/** Find a special power template given unique ID */
//-------------------------------------------------------------------------------------------------
const SpecialPowerTemplate *SpecialPowerStore::findSpecialPowerTemplateByID( UnsignedInt id )
{
// search the template list for matching name
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
if( m_specialPowerTemplates[ i ]->getID() == id )
return m_specialPowerTemplates[ i ];
return NULL; // not found
}
//-------------------------------------------------------------------------------------------------
/** Find a special power template given index (WB) */
//-------------------------------------------------------------------------------------------------
const SpecialPowerTemplate *SpecialPowerStore::getSpecialPowerTemplateByIndex( UnsignedInt index )
{
if (index >= 0 && index < m_specialPowerTemplates.size())
return m_specialPowerTemplates[ index ];
return NULL; // not found
} // end getSpecialPowerTemplateByIndex
//-------------------------------------------------------------------------------------------------
/** Return the size of the store (WB) */
//-------------------------------------------------------------------------------------------------
Int SpecialPowerStore::getNumSpecialPowers( void )
{
return m_specialPowerTemplates.size();
} // end getNumSpecialPowers
//-------------------------------------------------------------------------------------------------
/** does the object (and therefore the player) meet all the requirements to use this power */
//-------------------------------------------------------------------------------------------------
Bool SpecialPowerStore::canUseSpecialPower( Object *obj, const SpecialPowerTemplate *specialPowerTemplate )
{
// sanity
if( obj == NULL || specialPowerTemplate == NULL )
return FALSE;
// as a first sanity check, the object must have a module capable of executing the power
if( obj->getSpecialPowerModule( specialPowerTemplate ) == NULL )
return FALSE;
//
// in order to execute the special powers we have attached special power modules to the objects
// that can use them. However, just because an object has a module that is capable of
// doing the power, does not mean the object and the player can actually execute the
// power because some powers require a specialized science that the player must select and
// they cannot have all of them.
//
// check for requried science
ScienceType requiredScience = specialPowerTemplate->getRequiredScience();
if( requiredScience != SCIENCE_INVALID )
{
Player *player = obj->getControllingPlayer();
if( player->hasScience( requiredScience ) == FALSE )
return FALSE;
} // end if
// I THINK THIS IS WHERE WE BAIL OUT IF A DIFFERENT CONYARD IS ALREADY CHARGIN THIS SPECIAL RIGHT NOW //LORENZEN
// all is well
return TRUE;
} // end canUseSpecialPower
//-------------------------------------------------------------------------------------------------
/** Reset */
//-------------------------------------------------------------------------------------------------
void SpecialPowerStore::reset( void )
{
for (SpecialPowerTemplatePtrVector::iterator it = m_specialPowerTemplates.begin(); it != m_specialPowerTemplates.end(); /*++it*/)
{
SpecialPowerTemplate* si = *it;
Overridable* temp = si->deleteOverrides();
if (temp == NULL)
{
it = m_specialPowerTemplates.erase(it);
}
else
{
++it;
}
}
} // end reset

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,412 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: TunnelTracker.cpp ///////////////////////////////////////////////////////////
// The part of a Player's brain that holds the communal Passenger list of all tunnels.
// Author: Graham Smallwood, March, 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/KindOf.h"
#include "Common/TunnelTracker.h"
#include "Common/Xfer.h"
#include "GameClient/ControlBar.h"
#include "GameClient/Drawable.h"
#include "GameLogic/AI.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/TunnelContain.h"
// ------------------------------------------------------------------------
TunnelTracker::TunnelTracker()
{
m_tunnelCount = 0;
m_containListSize = 0;
m_curNemesisID = INVALID_ID;
m_nemesisTimestamp = 0;
}
// ------------------------------------------------------------------------
TunnelTracker::~TunnelTracker()
{
m_tunnelIDs.clear();
}
// ------------------------------------------------------------------------
void TunnelTracker::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
{
if (reverse)
{
// note that this has to be smart enough to handle items in the list being deleted
// via the callback function.
for(ContainedItemsList::reverse_iterator it = m_containList.rbegin(); it != m_containList.rend(); )
{
// save the obj...
Object* obj = *it;
// incr the iterator BEFORE calling the func (if the func removes the obj,
// the iterator becomes invalid)
++it;
// call it
(*func)( obj, userData );
}
}
else
{
// note that this has to be smart enough to handle items in the list being deleted
// via the callback function.
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
{
// save the obj...
Object* obj = *it;
// incr the iterator BEFORE calling the func (if the func removes the obj,
// the iterator becomes invalid)
++it;
// call it
(*func)( obj, userData );
}
}
}
// ------------------------------------------------------------------------
Int TunnelTracker::getContainMax() const
{
return TheGlobalData->m_maxTunnelCapacity;
}
// ------------------------------------------------------------------------
void TunnelTracker::updateNemesis(const Object *target)
{
if (getCurNemesis()==NULL) {
if (target) {
if (target->isKindOf(KINDOF_VEHICLE) || target->isKindOf(KINDOF_STRUCTURE) ||
target->isKindOf(KINDOF_INFANTRY) || target->isKindOf(KINDOF_AIRCRAFT)) {
m_curNemesisID = target->getID();
m_nemesisTimestamp = TheGameLogic->getFrame();
}
}
} else if (getCurNemesis()==target) {
m_nemesisTimestamp = TheGameLogic->getFrame();
}
}
// ------------------------------------------------------------------------
Object *TunnelTracker::getCurNemesis(void)
{
if (m_curNemesisID == INVALID_ID) {
return NULL;
}
if (m_nemesisTimestamp + 4*LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame()) {
m_curNemesisID = INVALID_ID;
return NULL;
}
Object *target = TheGameLogic->findObjectByID(m_curNemesisID);
if (target) {
//If the enemy unit is stealthed and not detected, then we can't attack it!
if( target->testStatus( OBJECT_STATUS_STEALTHED ) &&
!target->testStatus( OBJECT_STATUS_DETECTED ) &&
!target->testStatus( OBJECT_STATUS_DISGUISED ) )
{
target = NULL;
}
}
if (target && target->isEffectivelyDead()) {
target = NULL;
}
if (target == NULL) {
m_curNemesisID = INVALID_ID;
}
return target;
}
// ------------------------------------------------------------------------
Bool TunnelTracker::isValidContainerFor(const Object* obj, Bool checkCapacity) const
{
//October 11, 2002 -- Kris : Dustin wants ALL units to be able to use tunnels!
// srj sez: um, except aircraft.
if (obj && !obj->isKindOf(KINDOF_AIRCRAFT))
{
if (checkCapacity)
{
Int containMax = getContainMax();
Int containCount = getContainCount();
return ( containCount < containMax );
}
else
{
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
void TunnelTracker::addToContainList( Object *obj )
{
m_containList.push_back(obj);
++m_containListSize;
}
// ------------------------------------------------------------------------
void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
{
ContainedItemsList::iterator it = std::find(m_containList.begin(), m_containList.end(), obj);
if (it != m_containList.end())
{
// note that this invalidates the iterator!
m_containList.erase(it);
--m_containListSize;
}
}
// ------------------------------------------------------------------------
Bool TunnelTracker::isInContainer( Object *obj )
{
return (std::find(m_containList.begin(), m_containList.end(), obj) != m_containList.end()) ;
}
// ------------------------------------------------------------------------
void TunnelTracker::onTunnelCreated( const Object *newTunnel )
{
m_tunnelCount++;
m_tunnelIDs.push_back( newTunnel->getID() );
}
// ------------------------------------------------------------------------
void TunnelTracker::onTunnelDestroyed( const Object *deadTunnel )
{
m_tunnelCount--;
m_tunnelIDs.remove( deadTunnel->getID() );
if( m_tunnelCount == 0 )
{
// Kill everyone in our contain list. Cave in!
iterateContained( destroyObject, NULL, FALSE );
m_containList.clear();
m_containListSize = 0;
}
else
{
Object *validTunnel = TheGameLogic->findObjectByID( m_tunnelIDs.front() );
// Otherwise, make sure nobody inside remembers the dead tunnel as the one they entered
// (scripts need to use so there must be something valid here)
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
{
Object* obj = *it;
++it;
if( obj->getContainedBy() == deadTunnel )
obj->onContainedBy( validTunnel );
}
}
}
// ------------------------------------------------------------------------
void TunnelTracker::destroyObject( Object *obj, void * )
{
// Now that tunnels consider ContainedBy to be "the tunnel you entered", I need to say goodbye
// llike other contain types so they don't look us up on their deletion and crash
obj->onRemovedFrom( obj->getContainedBy() );
TheGameLogic->destroyObject( obj );
}
// ------------------------------------------------------------------------
// heal all the objects within the tunnel system using the iterateContained function
void TunnelTracker::healObjects(Real frames)
{
iterateContained(healObject, &frames, FALSE);
}
// ------------------------------------------------------------------------
// heal one object within the tunnel network system
void TunnelTracker::healObject( Object *obj, void *frames)
{
//get the number of frames to heal
Real *framesForFullHeal = (Real*)frames;
// setup the healing damageInfo structure with all but the amount
DamageInfo healInfo;
healInfo.in.m_damageType = DAMAGE_HEALING;
healInfo.in.m_deathType = DEATH_NONE;
//healInfo.in.m_sourceID = getObject()->getID();
// get body module of the thing to heal
BodyModuleInterface *body = obj->getBodyModule();
// if we've been in here long enough ... set our health to max
if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= *framesForFullHeal )
{
// set the amount to max just to be sure we're at the top
healInfo.in.m_amount = body->getMaxHealth();
// set max health
body->attemptHealing( &healInfo );
} // end if
else
{
//
// given the *whole* time it would take to heal this object, lets pretend that the
// object is at zero health ... and give it a sliver of health as if it were at 0 health
// and would be fully healed at 'framesForFullHeal'
//
healInfo.in.m_amount = body->getMaxHealth() / *framesForFullHeal;
// do the healing
body->attemptHealing( &healInfo );
} // end else
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// tunnel object id list
xfer->xferSTLObjectIDList( &m_tunnelIDs );
// contain list count
xfer->xferInt( &m_containListSize );
// contain list data
ObjectID objectID;
if( xfer->getXferMode() == XFER_SAVE )
{
ContainedItemsList::const_iterator it;
for( it = m_containList.begin(); it != m_containList.end(); ++it )
{
objectID = (*it)->getID();
xfer->xferObjectID( &objectID );
} // end for, it
} // end if, save
else
{
for( UnsignedShort i = 0; i < m_containListSize; ++i )
{
xfer->xferObjectID( &objectID );
m_xferContainList.push_back( objectID );
} // end for, i
} // end else, load
// tunnel count
xfer->xferUnsignedInt( &m_tunnelCount );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::loadPostProcess( void )
{
// sanity, the contain list should be empty until we post process the id list
if( m_containList.size() != 0 )
{
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - m_containList should be empty but is not\n" ));
throw SC_INVALID_DATA;
} // end if
// translate each object ids on the xferContainList into real object pointers in the contain list
Object *obj;
std::list< ObjectID >::const_iterator it;
for( it = m_xferContainList.begin(); it != m_xferContainList.end(); ++it )
{
obj = TheGameLogic->findObjectByID( *it );
if( obj == NULL )
{
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - Unable to find object ID '%d'\n", *it ));
throw SC_INVALID_DATA;
} // end if
// push on the back of the contain list
m_containList.push_back( obj );
// Crap. This is in OpenContain as a fix, but not here.
{
// remove object from its group (if any)
obj->leaveGroup();
// remove rider from partition manager
ThePartitionManager->unRegisterObject( obj );
// hide the drawable associated with rider
if( obj->getDrawable() )
obj->getDrawable()->setDrawableHidden( true );
// remove object from pathfind map
if( TheAI )
TheAI->pathfinder()->removeObjectFromPathfindMap( obj );
}
} // end for, it
// we're done with the xfer contain list now
m_xferContainList.clear();
} // end loadPostProcess

View File

@@ -0,0 +1,441 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// RandomValue.cpp
// Pseudo-random number generators
// Author: Michael S. Booth, January 1998
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Lib/BaseType.h"
#include "Common/RandomValue.h"
#include "Common/CRC.h"
#include "Common/Debug.h"
#include "GameLogic/GameLogic.h"
//#define DETERMINISTIC // to allow repetition for debugging
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#undef DEBUG_RANDOM_CLIENT
#undef DEBUG_RANDOM_LOGIC
#undef DEBUG_RANDOM_AUDIO
//#define DEBUG_RANDOM_CLIENT
//#define DEBUG_RANDOM_LOGIC
//#define DEBUG_RANDOM_AUDIO
static const Real theMultFactor = 1.0f / (powf(2, 8 * sizeof(UnsignedInt)) - 1.0f);
// Initial seed values.
static UnsignedInt theGameClientSeed[6] =
{
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
};
static UnsignedInt theGameAudioSeed[6] =
{
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
};
static UnsignedInt theGameLogicBaseSeed = 0;
static UnsignedInt theGameLogicSeed[6] =
{
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
};
// Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one.
#define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B)))
static UnsignedInt randomValue(UnsignedInt *seed)
{
UnsignedInt ax;
UnsignedInt c = 0;
ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */
/* add ax,seed+16 */
seed[4] = ax; /* mov seed+8,ax */
ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */
seed[3] = ax; /* mov seed+12,ax */
ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */
seed[2] = ax; /* mov seed+8,ax */
ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */
seed[1] = ax; /* mov seed+4,ax */
ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */
seed[0] = ax; /* mov seed+0,ax */
/* Increment seed array, bubbling up the carries. */
if (!++seed[5])
{
if (!++seed[4])
{
if (!++seed[3])
{
if (!++seed[2])
{
if (!++seed[1])
{
++seed[0];
++ax;
}
}
}
}
}
return(ax);
}
static void seedRandom(UnsignedInt SEED, UnsignedInt *seed)
{
UnsignedInt ax;
ax = SEED; /* mov eax,SEED */
ax += 0xf22d0e56; /* add eax,0f22d0e56h */
seed[0] = ax; /* mov seed,eax */
ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */
seed[1] = ax; /* mov seed+4,eax */
ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */
seed[2] = ax; /* mov seed+8,eax */
ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */
seed[3] = ax; /* mov seed+12,eax */
ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */
seed[4] = ax; /* mov seed+16,eax */
ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */
seed[5] = ax; /* mov seed+20,eax */
}
//
// It is necessary to separate the GameClient and GameLogic usage of random
// values to ensure that the GameLogic remains deterministic, regardless
// of the effects displayed on the GameClient.
//
UnsignedInt GetGameLogicRandomSeed( void )
{
return theGameLogicBaseSeed;
}
UnsignedInt GetGameLogicRandomSeedCRC( void )
{
CRC c;
c.computeCRC(theGameLogicSeed, 6*sizeof(UnsignedInt));
return c.get();
}
void InitRandom( void )
{
#ifdef DETERMINISTIC
// needs to be the same every time
seedRandom(0, theGameClientSeed);
seedRandom(0, theGameAudioSeed);
seedRandom(0, theGameLogicSeed);
theGameLogicBaseSeed = 0;
#else
time_t seconds = time( NULL );
seedRandom(seconds, theGameAudioSeed);
seedRandom(seconds, theGameClientSeed);
seedRandom(seconds, theGameLogicSeed);
theGameLogicBaseSeed = seconds;
#endif
}
void InitRandom( UnsignedInt seed )
{
seedRandom(seed, theGameAudioSeed);
seedRandom(seed, theGameClientSeed);
seedRandom(seed, theGameLogicSeed);
theGameLogicBaseSeed = seed;
#ifdef DEBUG_RANDOM_LOGIC
DEBUG_LOG(( "InitRandom %08lx\n",seed));
#endif
}
void InitGameLogicRandom( UnsignedInt seed )
{
#ifdef DETERMINISTIC
// needs to be the same every time
seedRandom(0, theGameLogicSeed);
theGameLogicBaseSeed = 0;
#else
seedRandom(seed, theGameLogicSeed);
theGameLogicBaseSeed = seed;
#endif
#ifdef DEBUG_RANDOM_LOGIC
DEBUG_LOG(( "InitRandom Logic %08lx\n",seed));
#endif
}
//
// Integer random value
//
Int GetGameLogicRandomValue( int lo, int hi, char *file, int line )
{
//Int delta = hi - lo + 1;
//Int rval;
//if (delta == 0)
//return hi;
//rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
UnsignedInt delta = hi - lo + 1;
//UnsignedInt temp;
Int rval;
if (delta == 0)
return hi;
rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
//temp = randomValue(theGameLogicSeed);
//temp = temp % delta;
//rval = temp + lo;
/**/
#ifdef DEBUG_RANDOM_LOGIC
DEBUG_LOG(( "%d: GetGameLogicRandomValue = %d (%d - %d), %s line %d\n",
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
#endif
/**/
return rval;
}
//
// Integer random value
//
Int GetGameClientRandomValue( int lo, int hi, char *file, int line )
{
UnsignedInt delta = hi - lo + 1;
Int rval;
if (delta == 0)
return hi;
rval = ((Int)(randomValue(theGameClientSeed) % delta)) + lo;
/**/
#ifdef DEBUG_RANDOM_CLIENT
DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n",
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
#endif
/**/
return rval;
}
//
// Integer random value
//
Int GetGameAudioRandomValue( int lo, int hi, char *file, int line )
{
UnsignedInt delta = hi - lo + 1;
Int rval;
if (delta == 0)
return hi;
rval = ((Int)(randomValue(theGameAudioSeed) % delta)) + lo;
/**/
#ifdef DEBUG_RANDOM_AUDIO
DEBUG_LOG(( "%d: GetGameAudioRandomValue = %d (%d - %d), %s line %d\n",
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
#endif
/**/
return rval;
}
//
// Real valued random value
//
Real GetGameLogicRandomValueReal( Real lo, Real hi, char *file, int line )
{
Real delta = hi - lo;
Real rval;
if (delta <= 0.0f)
return hi;
rval = ((Real)(randomValue(theGameLogicSeed)) * theMultFactor ) * delta + lo;
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
/**/
#ifdef DEBUG_RANDOM_LOGIC
DEBUG_LOG(( "%d: GetGameLogicRandomValueReal = %f, %s line %d\n",
TheGameLogic->getFrame(), rval, file, line ));
#endif
/**/
return rval;
}
//
// Real valued random value
//
Real GetGameClientRandomValueReal( Real lo, Real hi, char *file, int line )
{
Real delta = hi - lo;
Real rval;
if (delta <= 0.0f)
return hi;
rval = ((Real)(randomValue(theGameClientSeed)) * theMultFactor ) * delta + lo;
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
/**/
#ifdef DEBUG_RANDOM_CLIENT
DEBUG_LOG(( "%d: GetGameClientRandomValueReal = %f, %s line %d\n",
TheGameLogic->getFrame(), rval, file, line ));
#endif
/**/
return rval;
}
//
// Real valued random value
//
Real GetGameAudioRandomValueReal( Real lo, Real hi, char *file, int line )
{
Real delta = hi - lo;
Real rval;
if (delta <= 0.0f)
return hi;
rval = ((Real)(randomValue(theGameAudioSeed)) * theMultFactor ) * delta + lo;
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
/**/
#ifdef DEBUG_RANDOM_AUDIO
DEBUG_LOG(( "%d: GetGameAudioRandomValueReal = %f, %s line %d\n",
TheGameLogic->getFrame(), rval, file, line ));
#endif
/**/
return rval;
}
//--------------------------------------------------------------------------------------------------------------
// GameClientRandomVariable
//
/*static*/ const char *GameClientRandomVariable::DistributionTypeNames[] =
{
"CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
};
/**
define the range of random values, and the distribution of values
*/
void GameClientRandomVariable::setRange( Real low, Real high, DistributionType type )
{
DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameClientRandomVariables should have low == high"));
m_low = low;
m_high = high;
m_type = type;
}
/**
* Return a value from the random distribution
*/
Real GameClientRandomVariable::getValue( void ) const
{
switch( m_type )
{
case CONSTANT:
DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameClientRandomVariable\n"));
if (m_low == m_high) {
return m_low;
} // else return as though a UNIFORM.
case UNIFORM:
return GameClientRandomValueReal( m_low, m_high );
default:
/// @todo fill in support for nonuniform GameClientRandomVariables.
DEBUG_CRASH(("unsupported DistributionType in GameClientRandomVariable::getValue\n"));
return 0.0f;
}
}
//--------------------------------------------------------------------------------------------------------------
// GameLogicRandomVariable
//
/*static*/ const char *GameLogicRandomVariable::DistributionTypeNames[] =
{
"CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
};
/**
define the range of random values, and the distribution of values
*/
void GameLogicRandomVariable::setRange( Real low, Real high, DistributionType type )
{
DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameLogicRandomVariables should have low == high"));
m_low = low;
m_high = high;
m_type = type;
}
/**
* Return a value from the random distribution
*/
Real GameLogicRandomVariable::getValue( void ) const
{
switch( m_type )
{
case CONSTANT:
DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameLogicRandomVariable"));
if (m_low == m_high) {
return m_low;
} // else return as though a UNIFORM.
case UNIFORM:
return GameLogicRandomValueReal( m_low, m_high );
default:
/// @todo fill in support for nonuniform GameLogicRandomVariables.
DEBUG_CRASH(("unsupported DistributionType in GameLogicRandomVariable::getValue\n"));
return 0.0f;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// FILE: SkirmishBattleHonors.cpp
// Author: Matthew D. Campbell, April 2002
// Description: Saving/Loading of skirmish battle honors info
///////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "Common/UserPreferences.h"
#include "Common/SkirmishBattleHonors.h"
#include "Common/Player.h"
#include "Common/PlayerTemplate.h"
#include "Common/QuotedPrintable.h"
#include "Common/MultiplayerSettings.h"
#include "GameClient/MapUtil.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PRIVATE TYPES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PRIVATE DATA ///////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PUBLIC DATA ////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SkirmishBattleHonors base class
//-----------------------------------------------------------------------------
SkirmishBattleHonors::SkirmishBattleHonors()
{
load("SkirmishStats.ini");
}
SkirmishBattleHonors::~SkirmishBattleHonors()
{
}
void SkirmishBattleHonors::setWins(Int val)
{
setInt("Wins", val);
}
Int SkirmishBattleHonors::getWins(void) const
{
return getInt("Wins", 0);
}
void SkirmishBattleHonors::setLosses(Int val)
{
setInt("Losses", val);
}
Int SkirmishBattleHonors::getLosses(void) const
{
return getInt("Losses", 0);
}
void SkirmishBattleHonors::setWinStreak(Int val)
{
setInt("WinStreak", val);
}
Int SkirmishBattleHonors::getWinStreak(void) const
{
return getInt("WinStreak", 0);
}
void SkirmishBattleHonors::setBestWinStreak(Int val)
{
setInt("BestWinStreak", val);
}
Int SkirmishBattleHonors::getBestWinStreak(void) const
{
return getInt("BestWinStreak", 0);
}
void SkirmishBattleHonors::setChallengeMedals(Int val)
{
setInt("Challenge", val);
}
Int SkirmishBattleHonors::getChallengeMedals(void) const
{
return getInt("Challenge", 0);
}
void SkirmishBattleHonors::setBuiltSCUD(void)
{
setBool("SCUD", TRUE);
}
Bool SkirmishBattleHonors::builtSCUD(void) const
{
return getBool("SCUD", FALSE);
}
void SkirmishBattleHonors::setBuiltParticleCannon(void)
{
setBool("PPC", TRUE);
}
Bool SkirmishBattleHonors::builtParticleCannon(void) const
{
return getBool("PPC", FALSE);
}
void SkirmishBattleHonors::setBuiltNuke(void)
{
setBool("Nuke", TRUE);
}
Bool SkirmishBattleHonors::builtNuke(void) const
{
return getBool("Nuke", FALSE);
}
void SkirmishBattleHonors::setHonors(Int which)
{
Int honors = getInt("Honors", 0);
setInt("Honors", honors | which);
}
Int SkirmishBattleHonors::getHonors(void) const
{
return getInt("Honors", 0);
}
void SkirmishBattleHonors::setEnduranceMedal(AsciiString mapName, Int difficulty, int numAIs)
{
AsciiString key;
key.format("%s_%d", mapName.str(), difficulty);
setInt(key, numAIs);
}
Int SkirmishBattleHonors::getEnduranceMedal(AsciiString mapName, Int difficulty) const
{
AsciiString key;
key.format("%s_%d", mapName.str(), difficulty);
return getInt(key, 0);
}
void SkirmishBattleHonors::setLastGeneral(AsciiString val)
{
setAsciiString("LastHouse", val);
}
AsciiString SkirmishBattleHonors::getLastGeneral(void) const
{
return getAsciiString("LastHouse", AsciiString::TheEmptyString);
}
void SkirmishBattleHonors::setNumGamesLoyal(Int val)
{
setInt("LoyalGames", val);
}
Int SkirmishBattleHonors::getNumGamesLoyal(void) const
{
return getInt("LoyalGames", 0);
}
void SkirmishBattleHonors::setUSACampaignComplete(GameDifficulty difficulty)
{
AsciiString key;
key.format("USACampaign_%d", (int)difficulty);
setInt(key, 1);
}
Bool SkirmishBattleHonors::getUSACampaignComplete(GameDifficulty difficulty) const
{
AsciiString key;
key.format("USACampaign_%d", (int)difficulty);
return (getInt(key, 0) != 0);
}
void SkirmishBattleHonors::setCHINACampaignComplete(GameDifficulty difficulty)
{
AsciiString key;
key.format("CHINACampaign_%d", (int)difficulty);
setInt(key, 1);
}
Bool SkirmishBattleHonors::getCHINACampaignComplete(GameDifficulty difficulty) const
{
AsciiString key;
key.format("CHINACampaign_%d",(int)difficulty);
return (getInt(key, 0) != 0);
}
void SkirmishBattleHonors::setGLACampaignComplete(GameDifficulty difficulty)
{
AsciiString key;
key.format("GLACampaign_%d", (int)difficulty);
setInt(key, 1);
}
Bool SkirmishBattleHonors::getGLACampaignComplete(GameDifficulty difficulty) const
{
AsciiString key;
key.format("GLACampaign_%d", (int)difficulty);
return (getInt(key, 0) != 0);
}
void SkirmishBattleHonors::setChallengeCampaignComplete(Int challenge, GameDifficulty difficulty)
{
AsciiString key;
key.format("ChallengeCampaign%d_%d", challenge, (int)difficulty);
setInt(key, 1);
}
Bool SkirmishBattleHonors::getChallengeCampaignComplete(Int challenge, GameDifficulty difficulty) const
{
AsciiString key;
key.format("ChallengeCampaign%d_%d", challenge, (int)difficulty);
return (getInt(key, 0) != 0);
}

View File

@@ -0,0 +1,901 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// StateMachine.cpp
// Implementation of basic state machine
// Author: Michael S. Booth, January 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Errors.h"
#include "Common/StateMachine.h"
#include "Common/ThingTemplate.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//------------------------------------------------------------------------------ Performance Timers
//#include "Common/PerfMetrics.h"
//#include "Common/PerfTimer.h"
//static PerfTimer s_stateMachineTimer("StateMachine::update", false, PERFMETRICS_LOGIC_STARTFRAME, PERFMETRICS_LOGIC_STOPFRAME);
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/**
* Constructor
*/
State::State( StateMachine *machine, AsciiString name )
#ifdef STATE_MACHINE_DEBUG
: m_name(name)
#endif
{
m_ID = INVALID_STATE_ID;
m_successStateID = INVALID_STATE_ID;
m_failureStateID = INVALID_STATE_ID;
m_machine = machine;
}
//-----------------------------------------------------------------------------
/**
* Add another state transition condition for this state
*/
void State::friend_onCondition( StateTransFuncPtr test, StateID toStateID, void* userData, const char* description )
{
m_transitions.push_back(TransitionInfo(test, toStateID, userData, description));
}
//-----------------------------------------------------------------------------
class StIncrementer
{
private:
Int& num;
public:
StIncrementer(Int& n) : num(n)
{
++num;
}
~StIncrementer()
{
--num;
}
};
#ifdef STATE_MACHINE_DEBUG
//-----------------------------------------------------------------------------
std::vector<StateID> * State::getTransitions( void )
{
std::vector<StateID> *ids = new std::vector<StateID>;
ids->push_back(m_successStateID);
ids->push_back(m_failureStateID);
// check transition condition list
if (!m_transitions.empty())
{
for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
{
ids->push_back(it->toStateID);
}
}
return ids;
}
#endif
//-----------------------------------------------------------------------------
/**
* Given a return code, handle state transitions
*/
StateReturnType State::friend_checkForTransitions( StateReturnType status )
{
static Int checkfortransitionsnum = 0;
StIncrementer inc(checkfortransitionsnum);
if (checkfortransitionsnum >= 20)
{
DEBUG_CRASH(("checkfortransitionsnum is > 20"));
return STATE_FAILURE;
}
DEBUG_ASSERTCRASH(!IS_STATE_SLEEP(status), ("Please handle sleep states prior to this"));
// handle transitions
switch( status )
{
case STATE_SUCCESS:
// check if machine should exit
if (m_successStateID == EXIT_MACHINE_WITH_SUCCESS)
{
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
return STATE_SUCCESS;
}
else if (m_successStateID == EXIT_MACHINE_WITH_FAILURE)
{
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
return STATE_FAILURE;
}
// move to new state
return getMachine()->internalSetState( m_successStateID );
case STATE_FAILURE:
// check if machine should exit
if (m_failureStateID == EXIT_MACHINE_WITH_SUCCESS)
{
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
return STATE_SUCCESS;
}
else if (m_failureStateID == EXIT_MACHINE_WITH_FAILURE)
{
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
return STATE_FAILURE;
}
// move to new state
return getMachine()->internalSetState( m_failureStateID );
case STATE_CONTINUE:
// check transition condition list
if (!m_transitions.empty())
{
for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
{
if (it->test( this, it->userData ))
{
// test returned true, change to associated state
#ifdef STATE_MACHINE_DEBUG
if (getMachine()->getWantsDebugOutput())
{
DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
getMachine()->getName().str(), it->description ? it->description : "[no description]"));
}
#endif
// check if machine should exit
if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
{
return STATE_SUCCESS;
}
else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
{
return STATE_FAILURE;//Lorenzen wants to know why...
}
// move to new state
return getMachine()->internalSetState( it->toStateID );
}
}
}
break;
}
// the machine keeps running
return STATE_CONTINUE;
}
//-----------------------------------------------------------------------------
/**
* Given a return code, handle state transitions
*/
StateReturnType State::friend_checkForSleepTransitions( StateReturnType status )
{
static Int checkfortransitionsnum = 0;
StIncrementer inc(checkfortransitionsnum);
if (checkfortransitionsnum >= 20)
{
DEBUG_CRASH(("checkforsleeptransitionsnum is > 20"));
return STATE_FAILURE;
}
DEBUG_ASSERTCRASH(IS_STATE_SLEEP(status), ("Please only pass sleep states here"));
// check transition condition list
if (m_transitions.empty())
return status;
for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
{
if (!it->test( this, it->userData ))
continue;
// test returned true, change to associated state
#ifdef STATE_MACHINE_DEBUG
if (getMachine()->getWantsDebugOutput())
{
DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
getMachine()->getName().str(), it->description ? it->description : "[no description]"));
}
#endif
// check if machine should exit
if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
{
return STATE_SUCCESS;
}
else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
{
return STATE_FAILURE;
}
else
{
// move to new state
return getMachine()->internalSetState( it->toStateID );
}
}
return status;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/**
* Constructor
*/
StateMachine::StateMachine( Object *owner, AsciiString name )
{
m_owner = owner;
m_sleepTill = 0;
m_defaultStateID = INVALID_STATE_ID;
m_defaultStateInited = false;
m_currentState = NULL;
m_locked = false;
#ifdef STATE_MACHINE_DEBUG
m_name = name;
m_debugOutput = false;
m_lockedby = NULL;
#endif
internalClear();
}
//-----------------------------------------------------------------------------
/**
* Destructor. Destroy any states attached to this machine.
*/
StateMachine::~StateMachine()
{
// do not allow current state to exit
if (m_currentState)
m_currentState->onExit( EXIT_RESET );
std::map<StateID, State *>::iterator i;
// delete all states in the mapping
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
{
if ((*i).second)
(*i).second->deleteInstance();
}
}
//-----------------------------------------------------------------------------
#ifdef STATE_MACHINE_DEBUG
Bool StateMachine::getWantsDebugOutput() const
{
if (m_debugOutput)
{
return true;
}
if (TheGlobalData->m_stateMachineDebug)
{
return true;
}
#ifdef DEBUG_OBJECT_ID_EXISTS
if (TheObjectIDToDebug != 0 && getOwner() != NULL && getOwner()->getID() == TheObjectIDToDebug)
{
return true;
}
#endif
return false;
}
#endif
//-----------------------------------------------------------------------------
/**
* Clear the internal variables of state machine to known values.
*/
void StateMachine::internalClear()
{
m_goalObjectID = INVALID_ID;
m_goalPosition.x = 0.0f;
m_goalPosition.y = 0.0f;
m_goalPosition.z = 0.0f;
#ifdef STATE_MACHINE_DEBUG
if (getWantsDebugOutput())
{
DEBUG_LOG(("%d '%s'%x -- '%s' %x internalClear()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_owner, m_name.str(), this));
}
#endif
}
//-----------------------------------------------------------------------------
/**
* Clear the machine
*/
void StateMachine::clear()
{
// if the machine is locked, it cannot be cleared
if (m_locked)
{
#ifdef STATE_MACHINE_DEBUG
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
#endif
return;
}
// invoke the old state's onExit()
if (m_currentState)
m_currentState->onExit( EXIT_RESET );
m_currentState = NULL;
internalClear();
}
//-----------------------------------------------------------------------------
/**
* Reset the machine to its default state
*/
StateReturnType StateMachine::resetToDefaultState()
{
// if the machine is locked, it cannot be reset
if (m_locked)
{
#ifdef STATE_MACHINE_DEBUG
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
#endif
return STATE_FAILURE;
}
if (!m_defaultStateInited)
{
DEBUG_CRASH(("you may not call resetToDefaultState before initDefaultState"));
return STATE_FAILURE;
}
// allow current state to exit with EXIT_RESET if present
if (m_currentState)
m_currentState->onExit( EXIT_RESET );
m_currentState = NULL;
//
// the current state has done an onExit, clear the internal guts before we set
// the new state, to clear it after the new state is set might be overwriting
// things the new state transition causes to happen
//
internalClear();
// change to the default state
StateReturnType status = internalSetState( m_defaultStateID );
DEBUG_ASSERTCRASH( status != STATE_FAILURE, ( "StateMachine::resetToDefaultState() Error setting default state" ) );
return status;
}
//-----------------------------------------------------------------------------
/**
* Run one step of the machine
*/
StateReturnType StateMachine::updateStateMachine()
{
UnsignedInt now = TheGameLogic->getFrame();
if (m_sleepTill != 0 && now < m_sleepTill)
{
if( m_currentState == NULL )
{
return STATE_FAILURE;
}
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
}
// not sleeping anymore
m_sleepTill = 0;
if (m_currentState)
{
// update() can change m_currentState, so save it for a moment...
State* stateBeforeUpdate = m_currentState;
// execute this state
StateReturnType status = m_currentState->update();
// it is possible that the state's update() method may cause the state to be destroyed
if (m_currentState == NULL)
{
return STATE_FAILURE;
}
// here's the scenario:
// -- State A calls foo() and then says "sleep for 2000 frames".
// -- however, foo() called setState() to State B. thus our current state is not the same.
// -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
// so that the new state will be called immediately.
if (stateBeforeUpdate != m_currentState)
{
status = STATE_CONTINUE;
}
if (IS_STATE_SLEEP(status))
{
// hey, we're sleepy!
m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
}
else
{
// check for state transitions, possibly exiting this machine
return m_currentState->friend_checkForTransitions( status );
}
}
else
{
DEBUG_CRASH(("State machine has no current state -- did you remember to call initDefaultState?"));
return STATE_FAILURE;
}
}
//-----------------------------------------------------------------------------
/**
* Given a unique (for this machine) id number, and an instance of the
* State class, the machine records this as a possible state, and
* retains the id mapping.
* These state id's are used to change the machine's state via setState().
*/
void StateMachine::defineState( StateID id, State *state, StateID successID, StateID failureID, const StateConditionInfo* conditions )
{
#ifdef STATE_MACHINE_DEBUG
DEBUG_ASSERTCRASH(m_stateMap.find( id ) == m_stateMap.end(), ("duplicate state ID in statemachine %s\n",m_name.str()));
#endif
// map the ID to the state
m_stateMap.insert( std::map<StateID, State *>::value_type( id, state ) );
// store the ID in the state itself, as well
state->friend_setID( id );
state->friend_onSuccess(successID);
state->friend_onFailure(failureID);
while (conditions && conditions->test != NULL)
{
state->friend_onCondition(conditions->test, conditions->toStateID, conditions->userData);
++conditions;
}
if (m_defaultStateID == INVALID_STATE_ID)
m_defaultStateID = id;
}
//-----------------------------------------------------------------------------
/**
* Given a state ID, return the state instance
*/
State *StateMachine::internalGetState( StateID id )
{
// locate the actual state associated with the given ID
std::map<StateID, State *>::iterator i;
i = m_stateMap.find( id );
if (i == m_stateMap.end())
{
DEBUG_CRASH( ("StateMachine::internalGetState(): Invalid state for object %s using state %d", m_owner->getTemplate()->getName().str(), id) );
DEBUG_LOG(("Transisioning to state #d\n", (Int)id));
DEBUG_LOG(("Attempting to recover - locating default state...\n"));
i = m_stateMap.find(m_defaultStateID);
if (i == m_stateMap.end()) {
DEBUG_LOG(("Failed to located default state. Aborting...\n"));
throw ERROR_BAD_ARG;
} else {
DEBUG_LOG(("Located default state to recover.\n"));
}
}
return (*i).second;
}
//-----------------------------------------------------------------------------
/**
* Change the current state of the machine.
* This causes the old state's onExit() method to be invoked,
* and the new state's onEnter() method to be invoked.
*/
StateReturnType StateMachine::setState( StateID newStateID )
{
// if the machine is locked, it cannot change state via external events
if (m_locked)
{
#ifdef STATE_MACHINE_DEBUG
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
#endif
return STATE_CONTINUE;
}
return internalSetState( newStateID );
}
//-----------------------------------------------------------------------------
/**
* Change the current state of the machine.
* This causes the old state's onExit() method to be invoked,
* and the new state's onEnter() method to be invoked.
*/
StateReturnType StateMachine::internalSetState( StateID newStateID )
{
State *newState = NULL;
// anytime the state changes, stop sleeping
m_sleepTill = 0;
// if we're not setting the "done" state ID we will continue with the actual transition
if( newStateID != MACHINE_DONE_STATE_ID )
{
// if incoming state is invalid, go to the machine's default state
if (newStateID == INVALID_STATE_ID)
{
newStateID = m_defaultStateID;
if (newStateID == INVALID_STATE_ID)
{
DEBUG_CRASH(("you may NEVER set the current state to an invalid state id."));
return STATE_FAILURE;
}
}
// extract the state associated with the given ID
newState = internalGetState( newStateID );
#ifdef STATE_MACHINE_DEBUG
if (getWantsDebugOutput())
{
StateID curState = INVALID_STATE_ID;
if (m_currentState) {
curState = m_currentState->getID();
}
DEBUG_LOG(("%d '%s'%x -- '%s' %x exit ", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_owner, m_name.str(), this));
if (m_currentState) {
DEBUG_LOG((" '%s' ", m_currentState->getName().str()));
} else {
DEBUG_LOG((" INVALID_STATE_ID "));
}
if (newState) {
DEBUG_LOG(("enter '%s' \n", newState->getName().str()));
} else {
DEBUG_LOG(("to INVALID_STATE\n"));
}
}
#endif
}
// invoke the old state's onExit()
if (m_currentState)
m_currentState->onExit( EXIT_NORMAL );
// set the new state
m_currentState = newState;
// invoke the new state's onEnter()
/// @todo It might be useful to pass the old state in... (MSB)
if( m_currentState )
{
// onEnter() could conceivably change m_currentState, so save it for a moment...
State* stateBeforeEnter = m_currentState;
StateReturnType status = m_currentState->onEnter();
// it is possible that the state's onEnter() method may cause the state to be destroyed
if (m_currentState == NULL)
{
return STATE_FAILURE;
}
// here's the scenario:
// -- State A calls foo() and then says "sleep for 2000 frames".
// -- however, foo() called setState() to State B. thus our current state is not the same.
// -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
// so that the new state will be called immediately.
if (stateBeforeEnter != m_currentState)
{
status = STATE_CONTINUE;
}
if (IS_STATE_SLEEP(status))
{
// hey, we're sleepy!
UnsignedInt now = TheGameLogic->getFrame();
m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
}
else
{
// check for state transitions, possibly exiting this machine
return m_currentState->friend_checkForTransitions( status );
}
}
else
{
return STATE_CONTINUE; // irrelevant return code, but we must return something
}
}
//-----------------------------------------------------------------------------
/**
* Define the default state of the machine, and
* set the machine's state to it.
*/
StateReturnType StateMachine::initDefaultState()
{
#if defined(_DEBUG) || defined(_INTERNAL)
#ifdef STATE_MACHINE_DEBUG
#define REALLY_VERBOSE_LOG(x) /* */
// Run through all the transitions and make sure there aren't any transitions to undefined states. jba. [8/18/2003]
std::map<StateID, State *>::iterator i;
REALLY_VERBOSE_LOG(("SM_BEGIN\n"));
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
State *state = (*i).second;
StateID id = state->getID();
// Check transitions. [8/18/2003]
std::vector<StateID> *ids = state->getTransitions();
// check transitions
REALLY_VERBOSE_LOG(("State %s(%d) : ", state->getName().str(), id));
if (!ids->empty())
{
for(std::vector<StateID>::const_iterator it = ids->begin(); it != ids->end(); ++it)
{
StateID curID = *it;
REALLY_VERBOSE_LOG(("%d('", curID));
if (curID == INVALID_STATE_ID) {
REALLY_VERBOSE_LOG(("INVALID_STATE_ID', "));
continue;
}
if (curID == EXIT_MACHINE_WITH_SUCCESS) {
REALLY_VERBOSE_LOG(("EXIT_MACHINE_WITH_SUCCESS', "));
continue;
}
if (curID == EXIT_MACHINE_WITH_FAILURE) {
REALLY_VERBOSE_LOG(("EXIT_MACHINE_WITH_FAILURE', "));
continue;
}
// locate the actual state associated with the given ID
std::map<StateID, State *>::iterator i;
i = m_stateMap.find( curID );
if (i == m_stateMap.end()) {
DEBUG_LOG(("\nState %s(%d) : ", state->getName().str(), id));
DEBUG_LOG(("Transition %d not found\n", curID));
DEBUG_LOG(("This MUST BE FIXED!!!jba\n"));
DEBUG_CRASH(("Invalid transition."));
} else {
State *st = (*i).second;
if (st->getName().isNotEmpty()) {
REALLY_VERBOSE_LOG(("%s') ", st->getName().str()));
}
}
}
}
REALLY_VERBOSE_LOG(("\n"));
delete ids;
ids = NULL;
}
REALLY_VERBOSE_LOG(("SM_END\n\n"));
#endif
#endif
DEBUG_ASSERTCRASH(!m_locked, ("Machine is locked here, but probably should not be"));
if (m_defaultStateInited)
{
DEBUG_CRASH(("you may not call initDefaultState twice for the same StateMachine"));
return STATE_FAILURE;
}
else
{
m_defaultStateInited = true;
return internalSetState( m_defaultStateID );
}
}
//-----------------------------------------------------------------------------
void StateMachine::setGoalObject( const Object *obj )
{
if (m_locked)
return;
internalSetGoalObject( obj );
}
//-----------------------------------------------------------------------------
Bool StateMachine::isGoalObjectDestroyed() const
{
if (m_goalObjectID == 0)
{
return false; // never had a goal object
}
return getGoalObject() == NULL;
}
//-----------------------------------------------------------------------------
void StateMachine::halt()
{
m_locked = true;
m_currentState = NULL; // don't exit current state, just clear it.
#ifdef STATE_MACHINE_DEBUG
if (getWantsDebugOutput())
{
DEBUG_LOG(("%d '%s' -- '%s' %x halt()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
}
#endif
}
//-----------------------------------------------------------------------------
void StateMachine::internalSetGoalObject( const Object *obj )
{
if (obj) {
m_goalObjectID = obj->getID();
internalSetGoalPosition(obj->getPosition());
}
else {
m_goalObjectID = INVALID_ID;
}
}
//-----------------------------------------------------------------------------
Object *StateMachine::getGoalObject()
{
return TheGameLogic->findObjectByID( m_goalObjectID );
}
//-----------------------------------------------------------------------------
const Object *StateMachine::getGoalObject() const
{
return TheGameLogic->findObjectByID( m_goalObjectID );
}
//-----------------------------------------------------------------------------
void StateMachine::setGoalPosition( const Coord3D *pos )
{
if (m_locked)
return;
internalSetGoalPosition( pos );
}
//-----------------------------------------------------------------------------
void StateMachine::internalSetGoalPosition( const Coord3D *pos )
{
if (pos) {
m_goalPosition = *pos;
// Don't clear the goal object, or everything breaks. Like construction of buildings.
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void StateMachine::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer Method
* Version Info
* 1: Initial version
*/
// ------------------------------------------------------------------------------------------------
void StateMachine::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
xfer->xferUnsignedInt(&m_sleepTill);
xfer->xferUnsignedInt(&m_defaultStateID);
StateID curStateID = getCurrentStateID();
xfer->xferUnsignedInt(&curStateID);
if (xfer->getXferMode() == XFER_LOAD) {
// We are going to jump into the current state. We don't call onEnter or onExit, because the
// state was already active when we saved.
m_currentState = internalGetState( curStateID );
}
Bool snapshotAllStates = false;
#ifdef _DEBUG
//snapshotAllStates = true;
#endif
xfer->xferBool(&snapshotAllStates);
if (snapshotAllStates) {
std::map<StateID, State *>::iterator i;
// count all states in the mapping
Int count = 0;
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
count++;
Int saveCount = count;
xfer->xferInt(&saveCount);
if (saveCount!=count) {
DEBUG_CRASH(("State count mismatch - %d expected, %d read", count, saveCount));
throw SC_INVALID_DATA;
}
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
State *state = (*i).second;
StateID id = state->getID();
xfer->xferUnsignedInt(&id);
if (id!=state->getID()) {
DEBUG_CRASH(("State ID mismatch - %d expected, %d read", state->getID(), id));
throw SC_INVALID_DATA;
}
if( state == NULL )
{
DEBUG_ASSERTCRASH(state != NULL, ("state was NULL on xfer, trying to heal..."));
// Hmm... too late to find out why we are getting NULL in our state, but if we let it go, we will Throw in xferSnapshot.
state = internalGetState(m_defaultStateID);
}
xfer->xferSnapshot(state);
}
} else {
if( m_currentState == NULL )
{
DEBUG_ASSERTCRASH(m_currentState != NULL, ("currentState was NULL on xfer, trying to heal..."));
// Hmm... too late to find out why we are getting NULL in our state, but if we let it go, we will Throw in xferSnapshot.
m_currentState = internalGetState(m_defaultStateID);
}
xfer->xferSnapshot(m_currentState);
}
xfer->xferObjectID(&m_goalObjectID);
xfer->xferCoord3D(&m_goalPosition);
xfer->xferBool(&m_locked);
xfer->xferBool(&m_defaultStateInited);
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void StateMachine::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,425 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: StatsCollector.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// created: Jul 2002
//
// Filename: StatsCollector.cpp
//
// author: Chris Huybregts
//
// purpose: Convinience class to gather player stats
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "Common/StatsCollector.h"
#include "Common/FileSystem.h"
#include "Common/PlayerList.h"
#include "Common/Player.h"
#include "Common/GlobalData.h"
#include "Common/Money.h"
#include "GameLogic/Object.h"
#include "GameLogic/GameLogic.h"
#include "GameClient/MapUtil.h"
#include "GameNetwork/NetworkUtil.h"
#include "GameNetwork/LANAPICallbacks.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
StatsCollector *TheStatsCollector = NULL;
static char statsDir[255] = "Stats\\";
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// init all
//=============================================================================
StatsCollector::StatsCollector( void )
{
//Added By Sadullah Nader
//Initialization(s) inserted
m_isScrolling = FALSE;
m_scrollBeginTime = 0;
m_scrollTime = 0;
//
m_timeCount = 0;
m_buildCommands = 0;
m_moveCommands = 0;
m_attackCommands = 0;
m_scrollMapCommands = 0;
m_AIUnits = 0;
m_playerUnits = 0;
m_lastUpdate = 0;
m_startFrame = TheGameLogic->getFrame();
}
//Destructor
//=============================================================================
StatsCollector::~StatsCollector( void )
{
}
// Reset and create the file header
//=============================================================================
void StatsCollector::reset( void )
{
// make sure we have a stats Dir.
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData->m_saveStats)
{
AsciiString playtestDir = TheGlobalData->m_baseStatsDir;
playtestDir.concat(statsDir);
if (TheNetwork)
{
if (TheLAN)
{
TheFileSystem->createDirectory(playtestDir);
}
}
}
#endif
TheFileSystem->createDirectory(AsciiString(statsDir));
createFileName();
writeInitialFileInfo();
// zero out
zeroOutStats();
m_lastUpdate = TheGameLogic->getFrame(); // timeGetTime();
}
// Msgs pass through here so we can track whichever ones we want
//=============================================================================
void StatsCollector::collectMsgStats( const GameMessage *msg )
{
// We only care about our own messages.
if(ThePlayerList->getLocalPlayer()->getPlayerIndex() != msg->getPlayerIndex())
return;
switch (msg->getType())
{
case GameMessage::MSG_QUEUE_UNIT_CREATE:
case GameMessage::MSG_DOZER_CONSTRUCT:
case GameMessage::MSG_DOZER_CONSTRUCT_LINE:
{
++m_buildCommands;
break;
}
}
}
//Loop through all objects and count up the ones we want. (Very Slow!!!)
//=============================================================================
void StatsCollector::collectUnitCountStats( void )
{
for(Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject())
{
if((!(obj->isKindOf(KINDOF_INFANTRY) || obj->isKindOf(KINDOF_VEHICLE))) || ( obj->isNeutralControlled()) ||(obj->getControllingPlayer()->getSide().compare("Civilian") == 0))
continue;
if(obj->getControllingPlayer()->isLocalPlayer())
{
++m_playerUnits;
}
else
{
++m_AIUnits;
}
}
}
// call every frame and only do stuff when our time is up
//=============================================================================
void StatsCollector::update( void )
{
if(m_lastUpdate + (TheGlobalData->m_playStats * LOGICFRAMES_PER_SECOND) > TheGameLogic->getFrame())
return;
collectUnitCountStats();
if(m_isScrolling)
{
m_scrollTime += TheGameLogic->getFrame() - m_scrollBeginTime;
m_scrollBeginTime = TheGameLogic->getFrame();
}
m_timeCount += TheGlobalData->m_playStats;
writeStatInfo();
zeroOutStats();
m_lastUpdate = TheGameLogic->getFrame(); //timeGetTime();
}
void StatsCollector::incrementScrollMoveCount( void )
{
++m_scrollMapCommands;
}
void StatsCollector::incrementAttackCount( void )
{
++m_attackCommands;
}
void StatsCollector::incrementBuildCount( void )
{
++m_buildCommands;
}
void StatsCollector::incrementMoveCount( void )
{
++m_moveCommands;
}
void StatsCollector::writeFileEnd( void )
{
//open the file
FILE *f = fopen(m_statsFileName.str(), "a");
if(!f)
{
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
return;
}
m_timeCount += (TheGameLogic->getFrame() - m_lastUpdate) / LOGICFRAMES_PER_SECOND;
writeStatInfo();
fprintf(f, "---------------------------------------------------\n");
// Time
struct tm *newTime;
time_t aclock;
time( &aclock );
newTime = localtime( &aclock );
fprintf(f, "End Time:\t%s\n",asctime(newTime) );
fprintf(f, "=KEY===============================================\n");
fprintf(f, "Time* = The Time Interval\n");
fprintf(f, "BC = Build Commands\n");
fprintf(f, "MC = Move Commands\n");
fprintf(f, "AC = Attack Commands\n");
fprintf(f, "SMC = Scroll Map Commands\n");
fprintf(f, "ST* = Scroll Time in Seconds\n");
fprintf(f, "OC = Other Commands (N/A)\n");
fprintf(f, "$$$ = Local Player's Cash Amount\n");
fprintf(f, "#PU = # of Player's Units\n");
fprintf(f, "#AIU = # of AI's Units\n");
fprintf(f, "===================================================\n");
fprintf(f, "* Times are in Game Seconds which are based off of frames. Current fps is set to %d\n", LOGICFRAMES_PER_SECOND);
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData->m_benchmarkTimer > 0)
{
fprintf(f, "\n*** BENCHMARK MODE STATS ***\n");
fprintf(f, " Frames = %d\n", TheGameLogic->getFrame()-m_startFrame);
fprintf(f, "Seconds = %d\n", TheGlobalData->m_benchmarkTimer);
fprintf(f, " FPS = %.2f\n", ((Real)TheGameLogic->getFrame()-(Real)m_startFrame)/(Real)TheGlobalData->m_benchmarkTimer);
}
#endif
fclose(f);
}
void StatsCollector::startScrollTime( void )
{
m_isScrolling = TRUE;
m_scrollBeginTime = TheGameLogic->getFrame();
++m_scrollMapCommands;
}
void StatsCollector::endScrollTime( void )
{
if(!m_isScrolling)
return;
m_isScrolling = FALSE;
m_scrollTime += TheGameLogic->getFrame() - m_scrollBeginTime;
}
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
void StatsCollector::zeroOutStats( void )
{
m_buildCommands = 0;
m_moveCommands = 0;
m_attackCommands = 0;
m_scrollMapCommands = 0;
m_AIUnits = 0;
m_playerUnits = 0;
m_scrollTime = 0;
}
// create the filename based off of map time and date
//=============================================================================
void StatsCollector::createFileName( void )
{
m_statsFileName.clear();
// Date and Time
char datestr[256] = "";
time_t longTime;
struct tm *curtime;
time(&longTime);
curtime = localtime(&longTime);
strftime(datestr, 256, "_%b%d_%I%M%p", curtime);
// const MapMetaData *m = TheMapCache->findMap(TheGlobalData->m_mapName);
AsciiString name = TheGlobalData->m_mapName;
const char *fname = name.reverseFind('\\');
if (fname)
name = fname+1;
name.removeLastChar(); // p
name.removeLastChar(); // a
name.removeLastChar(); // m
name.removeLastChar(); // .
m_statsFileName.clear();
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData->m_saveStats)
{
m_statsFileName.set(TheGlobalData->m_baseStatsDir);
m_statsFileName.concat(statsDir);
if (TheNetwork)
{
if (TheLAN)
{
GameInfo *game = TheLAN->GetMyGame();
AsciiString players;
AsciiString full;
AsciiString fullPlusNum;
for (Int i=0; i<MAX_SLOTS; ++i)
{
GameSlot *slot = game->getSlot(i);
if (slot && slot->isHuman())
{
AsciiString player;
player.format("%ls_", slot->getName().str());
players.concat(player);
}
}
full.format("%s%s_%d_%d", players.str(), name.str(), game->getSeed(), game->getLocalSlotNum());
AsciiString testString;
testString.format("%s%s.txt", m_statsFileName.str(), full.str());
m_statsFileName = testString;
}
}
else
{
m_statsFileName.format("%s%s%s.txt",statsDir, name.str(),datestr);
}
}
else
#endif
{
m_statsFileName.format("%s%s%s.txt",statsDir, name.str(),datestr);
}
}
// create the header of the file
//=============================================================================
void StatsCollector::writeInitialFileInfo()
{
//open the file
FILE *f = fopen(m_statsFileName.str(), "w");
if(!f)
{
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
return;
}
fprintf(f, "---------------------------------------------------\n");
// Time
struct tm *newTime;
time_t aclock;
time( &aclock );
newTime = localtime( &aclock );
fprintf(f, "Date:\t%s",asctime(newTime) );
// Map
fprintf(f, "Map:\t%s\n", TheGlobalData->m_mapName.str());
// Side
fprintf(f, "Side:\t%s\n", ThePlayerList->getLocalPlayer()->getSide().str());
fprintf(f, "---------------------------------------------------\n\n");
fprintf(f, "Time*\tBC\tMC\tAC\tSMC\tST*\tOC\t$$$\t#PU\t#AIU\n");
collectUnitCountStats();
Money *m = ThePlayerList->getLocalPlayer()->getMoney();
fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 0, m_buildCommands, m_moveCommands, m_attackCommands,
m_scrollMapCommands, 0 ,/*other commands*/0, m->countMoney(),
m_playerUnits, m_AIUnits );
// initial stats
// we don't want a file pointer open for seconds on end... we'll open it each time.
fclose(f);
}
// Write out the stats
//=============================================================================
void StatsCollector::writeStatInfo()
{
//open the file
FILE *f = fopen(m_statsFileName.str(), "a");
if(!f)
{
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
return;
}
Money *m = ThePlayerList->getLocalPlayer()->getMoney();
fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", m_timeCount, m_buildCommands, m_moveCommands, m_attackCommands,
m_scrollMapCommands, m_scrollTime / LOGICFRAMES_PER_SECOND, /*other commands*/0,m->countMoney() ,
m_playerUnits, m_AIUnits );
fclose(f);
}

View File

@@ -0,0 +1,235 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
/////////ArchiveFile.cpp ///////////////////////
// Bryan Cleveland, August 2002
////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/File.h"
#include "Common/PerfTimer.h"
// checks to see if str matches searchString. Search string is done in the
// using * and ? as wildcards. * is used to denote any number of characters,
// and ? is used to denote a single wildcard character.
static Bool SearchStringMatches(AsciiString str, AsciiString searchString)
{
if (str.getLength() == 0) {
if (searchString.getLength() == 0) {
return TRUE;
}
return FALSE;
}
if (searchString.getLength() == 0) {
return FALSE;
}
const char *c1 = str.str();
const char *c2 = searchString.str();
while ((*c1 == *c2) || (*c2 == '?') || (*c2 == '*')) {
if ((*c1 == *c2) || (*c2 == '?')) {
++c1;
++c2;
} else if (*c2 == '*') {
++c2;
if (*c2 == 0) {
return TRUE;
}
while (*c1 != 0) {
if (SearchStringMatches(AsciiString(c1), AsciiString(c2))) {
return TRUE;
}
++c1;
}
}
if (*c1 == 0) {
if (*c2 == 0) {
return TRUE;
}
return FALSE;
}
if (*c2 == 0) {
return FALSE;
}
}
return FALSE;
}
ArchiveFile::~ArchiveFile()
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
ArchiveFile::ArchiveFile()
{
m_rootDirectory.clear();
}
void ArchiveFile::addFile(const AsciiString& path, const ArchivedFileInfo *fileInfo)
{
AsciiString temp;
temp = path;
temp.toLower();
AsciiString token;
AsciiString debugpath;
DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
temp.nextToken(&token, "\\/");
while (token.getLength() > 0) {
if (dirInfo->m_directories.find(token) == dirInfo->m_directories.end())
{
dirInfo->m_directories[token].clear();
dirInfo->m_directories[token].m_directoryName = token;
}
debugpath.concat(token);
debugpath.concat('\\');
dirInfo = &(dirInfo->m_directories[token]);
temp.nextToken(&token, "\\/");
}
dirInfo->m_files[fileInfo->m_filename] = *fileInfo;
//path.concat(fileInfo->m_filename);
}
void ArchiveFile::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
AsciiString searchDir;
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
searchDir = originalDirectory;
searchDir.toLower();
AsciiString token;
searchDir.nextToken(&token, "\\/");
while (token.getLength() > 0) {
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
// if the directory doesn't exist, then there aren't any files to be had.
return;
}
searchDir.nextToken(&token, "\\/");
}
getFileListInDirectory(dirInfo, originalDirectory, searchName, filenameList, searchSubdirectories);
}
void ArchiveFile::getFileListInDirectory(const DetailedArchivedDirectoryInfo *dirInfo, const AsciiString& currentDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
DetailedArchivedDirectoryInfoMap::const_iterator diriter = dirInfo->m_directories.begin();
while (diriter != dirInfo->m_directories.end()) {
const DetailedArchivedDirectoryInfo *tempDirInfo = &(diriter->second);
AsciiString tempdirname;
tempdirname = currentDirectory;
if ((tempdirname.getLength() > 0) && (!tempdirname.endsWith("\\"))) {
tempdirname.concat('\\');
}
tempdirname.concat(tempDirInfo->m_directoryName);
getFileListInDirectory(tempDirInfo, tempdirname, searchName, filenameList, searchSubdirectories);
diriter++;
}
ArchivedFileInfoMap::const_iterator fileiter = dirInfo->m_files.begin();
while (fileiter != dirInfo->m_files.end()) {
if (SearchStringMatches(fileiter->second.m_filename, searchName)) {
AsciiString tempfilename;
tempfilename = currentDirectory;
if ((tempfilename.getLength() > 0) && (!tempfilename.endsWith("\\"))) {
tempfilename.concat('\\');
}
tempfilename.concat(fileiter->second.m_filename);
if (filenameList.find(tempfilename) == filenameList.end()) {
// only insert into the list if its not already in there.
filenameList.insert(tempfilename);
}
}
fileiter++;
}
}
void ArchiveFile::attachFile(File *file)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
m_file = file;
}
const ArchivedFileInfo * ArchiveFile::getArchivedFileInfo(const AsciiString& filename) const
{
AsciiString path;
path = filename;
path.toLower();
AsciiString token;
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while ((token.find('.') == NULL) || (path.find('.') != NULL)) {
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
return NULL;
}
path.nextToken(&token, "\\/");
}
ArchivedFileInfoMap::const_iterator it = dirInfo->m_files.find(token);
if (it != dirInfo->m_files.end())
{
return &it->second;
}
else
{
return NULL;
}
}

View File

@@ -0,0 +1,327 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Game Engine Common
//
// File name: ArchiveFileSystem.cpp
//
// Created: 11/26/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/AsciiString.h"
#include "Common/PerfTimer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
ArchiveFileSystem *TheArchiveFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//------------------------------------------------------
// ArchivedFileInfo
//------------------------------------------------------
ArchiveFileSystem::ArchiveFileSystem()
{
}
ArchiveFileSystem::~ArchiveFileSystem()
{
ArchiveFileMap::iterator iter = m_archiveFileMap.begin();
while (iter != m_archiveFileMap.end()) {
ArchiveFile *file = iter->second;
if (file != NULL) {
delete file;
file = NULL;
}
iter++;
}
}
void ArchiveFileSystem::loadIntoDirectoryTree(const ArchiveFile *archiveFile, const AsciiString& archiveFilename, Bool overwrite)
{
FilenameList filenameList;
archiveFile->getFileListInDirectory(AsciiString(""), AsciiString(""), AsciiString("*"), filenameList, TRUE);
FilenameListIter it = filenameList.begin();
while (it != filenameList.end()) {
// add this filename to the directory tree.
AsciiString path = *it;
path.toLower();
AsciiString token;
AsciiString debugpath;
ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
Bool infoInPath;
infoInPath = path.nextToken(&token, "\\/");
while (infoInPath && (!token.find('.') || path.find('.'))) {
ArchivedDirectoryInfoMap::iterator tempiter = dirInfo->m_directories.find(token);
if (tempiter == dirInfo->m_directories.end())
{
dirInfo->m_directories[token].clear();
dirInfo->m_directories[token].m_directoryName = token;
}
dirInfo = &(dirInfo->m_directories[token]);
debugpath.concat(token);
debugpath.concat('\\');
infoInPath = path.nextToken(&token, "\\/");
}
// token is the filename, and dirInfo is the directory that this file is in.
if (dirInfo->m_files.find(token) == dirInfo->m_files.end() || overwrite) {
AsciiString path2;
path2 = debugpath;
path2.concat(token);
// DEBUG_LOG(("ArchiveFileSystem::loadIntoDirectoryTree - adding file %s, archived in %s\n", path2.str(), archiveFilename.str()));
dirInfo->m_files[token] = archiveFilename;
}
it++;
}
}
void ArchiveFileSystem::loadMods() {
if (TheGlobalData->m_modBIG.isNotEmpty())
{
ArchiveFile *archiveFile = openArchiveFile(TheGlobalData->m_modBIG.str());
if (archiveFile != NULL) {
DEBUG_LOG(("ArchiveFileSystem::loadMods - loading %s into the directory tree.\n", TheGlobalData->m_modBIG.str()));
loadIntoDirectoryTree(archiveFile, TheGlobalData->m_modBIG, TRUE);
m_archiveFileMap[TheGlobalData->m_modBIG] = archiveFile;
DEBUG_LOG(("ArchiveFileSystem::loadMods - %s inserted into the archive file map.\n", TheGlobalData->m_modBIG.str()));
}
else
{
DEBUG_LOG(("ArchiveFileSystem::loadMods - could not openArchiveFile(%s)\n", TheGlobalData->m_modBIG.str()));
}
}
if (TheGlobalData->m_modDir.isNotEmpty())
{
#ifdef DEBUG_LOGGING
Bool ret =
#endif
loadBigFilesFromDirectory(TheGlobalData->m_modDir, "*.big", TRUE);
DEBUG_ASSERTLOG(ret, ("loadBigFilesFromDirectory(%s) returned FALSE!\n", TheGlobalData->m_modDir.str()));
}
}
Bool ArchiveFileSystem::doesFileExist(const Char *filename) const
{
AsciiString path = filename;
path.toLower();
AsciiString token;
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while (!token.find('.') || path.find('.'))
{
ArchivedDirectoryInfoMap::const_iterator tempiter = dirInfo->m_directories.find(token);
if (tempiter != dirInfo->m_directories.end())
{
dirInfo = &tempiter->second;
path.nextToken(&token, "\\/");
}
else
{
// the directory doesn't exist, so return false
return FALSE;
}
}
// token is the filename, and dirInfo is the directory that this file is in.
if (dirInfo->m_files.find(token) == dirInfo->m_files.end()) {
return FALSE;
}
return TRUE;
}
File * ArchiveFileSystem::openFile(const Char *filename, Int access /* = 0 */)
{
AsciiString archiveFilename;
archiveFilename = getArchiveFilenameForFile(AsciiString(filename));
if (archiveFilename.getLength() == 0) {
return NULL;
}
return m_archiveFileMap[archiveFilename]->openFile(filename, access);
}
Bool ArchiveFileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
{
if (fileInfo == NULL) {
return FALSE;
}
if (filename.getLength() <= 0) {
return FALSE;
}
AsciiString archiveFilename = getArchiveFilenameForFile(filename);
ArchiveFileMap::const_iterator it = m_archiveFileMap.find(archiveFilename);
if (it != m_archiveFileMap.end())
{
return it->second->getFileInfo(filename, fileInfo);
}
else
{
return FALSE;
}
}
AsciiString ArchiveFileSystem::getArchiveFilenameForFile(const AsciiString& filename) const
{
AsciiString path;
path = filename;
path.toLower();
AsciiString token;
AsciiString debugpath;
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
path.nextToken(&token, "\\/");
while (!token.find('.') || path.find('.')) {
ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
if (it != dirInfo->m_directories.end())
{
dirInfo = &it->second;
}
else
{
// the directory doesn't exist, so return NULL
// dump the directories;
//DEBUG_LOG(("directory %s not found in %s in archive file system\n", token.str(), debugpath.str()));
//DEBUG_LOG(("directories in %s in archive file system are:\n", debugpath.str()));
//ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.begin();
//while (it != dirInfo->m_directories.end()) {
// DEBUG_LOG(("\t%s\n", it->second.m_directoryName.str()));
// it++;
//}
//DEBUG_LOG(("end of directory list.\n"));
return AsciiString::TheEmptyString;
}
debugpath.concat(token);
debugpath.concat('\\');
path.nextToken(&token, "\\/");
}
ArchivedFileLocationMap::const_iterator it = dirInfo->m_files.find(token);
if (it != dirInfo->m_files.end())
{
return it->second;
}
else
{
return AsciiString::TheEmptyString;
}
}
void ArchiveFileSystem::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
ArchiveFileMap::const_iterator it = m_archiveFileMap.begin();
while (it != m_archiveFileMap.end()) {
it->second->getFileListInDirectory(currentDirectory, originalDirectory, searchName, filenameList, searchSubdirectories);
it++;
}
}

View File

@@ -0,0 +1,396 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: AsciiString.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: AsciiString.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: General-purpose string classes
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CriticalSection.h"
// -----------------------------------------------------
/*static*/ AsciiString AsciiString::TheEmptyString;
//-----------------------------------------------------------------------------
inline char* skipSeps(char* p, const char* seps)
{
while (*p && strchr(seps, *p) != NULL)
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipNonSeps(char* p, const char* seps)
{
while (*p && strchr(seps, *p) == NULL)
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipWhitespace(char* p)
{
while (*p && isspace(*p))
++p;
return p;
}
//-----------------------------------------------------------------------------
inline char* skipNonWhitespace(char* p)
{
while (*p && !isspace(*p))
++p;
return p;
}
void AsciiString::freeBytes(void)
{
TheDynamicMemoryAllocator->freeBytes(m_data);
}
// -----------------------------------------------------
#ifdef _DEBUG
void AsciiString::validate() const
{
if (!m_data) return;
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
// DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated < 1024, ("m_numCharsAllocated suspiciously large"));
DEBUG_ASSERTCRASH(strlen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long (%d) for storage",strlen(m_data->peek())+1));
}
#endif
// -----------------------------------------------------
void AsciiString::debugIgnoreLeaks()
{
#ifdef MEMORYPOOL_DEBUG
if (m_data)
{
TheDynamicMemoryAllocator->debugIgnoreLeaksForThisBlock(m_data);
}
else
{
DEBUG_LOG(("cannot ignore the leak (no data)\n"));
}
#endif
}
// -----------------------------------------------------
void AsciiString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCopy, const char* strToCat)
{
validate();
if (m_data &&
m_data->m_refCount == 1 &&
m_data->m_numCharsAllocated >= numCharsNeeded)
{
// no buffer manhandling is needed (it's already large enough, and unique to us)
if (strToCopy)
strcpy(m_data->peek(), strToCopy);
if (strToCat)
strcat(m_data->peek(), strToCat);
return;
}
int minBytes = sizeof(AsciiStringData) + numCharsNeeded*sizeof(char);
if (minBytes > MAX_LEN)
throw ERROR_OUT_OF_MEMORY;
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
AsciiStringData* newData = (AsciiStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_AsciiString::ensureUniqueBufferOfSize");
newData->m_refCount = 1;
newData->m_numCharsAllocated = (actualBytes - sizeof(AsciiStringData))/sizeof(char);
#if defined(_DEBUG) || defined(_INTERNAL)
newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
#endif
if (m_data && preserveData)
strcpy(newData->peek(), m_data->peek());
else
newData->peek()[0] = 0;
// do these BEFORE releasing the old buffer, so that self-copies
// or self-cats will work correctly.
if (strToCopy)
strcpy(newData->peek(), strToCopy);
if (strToCat)
strcat(newData->peek(), strToCat);
releaseBuffer();
m_data = newData;
validate();
}
// -----------------------------------------------------
char* AsciiString::getBufferForRead(Int len)
{
validate();
DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
validate();
return peek();
}
// -----------------------------------------------------
void AsciiString::translate(const UnicodeString& stringSrc)
{
validate();
/// @todo srj put in a real translation here; this will only work for 7-bit ascii
clear();
Int len = stringSrc.getLength();
for (Int i = 0; i < len; i++)
concat((char)stringSrc.getCharAt(i));
validate();
}
// -----------------------------------------------------
void AsciiString::trim()
{
validate();
if (m_data)
{
char *c = peek();
// Strip leading white space from the string.
c = skipWhitespace(c);
if (c != peek())
{
set(c);
}
if (m_data) // another check, because the previous set() could erase m_data
{
// Clip trailing white space from the string.
int len = strlen(peek());
for (int index = len-1; index >= 0; index--)
{
if (isspace(getCharAt(index)))
{
removeLastChar();
}
else
{
break;
}
}
}
}
validate();
}
// -----------------------------------------------------
void AsciiString::toLower()
{
validate();
if (m_data)
{
char buf[MAX_FORMAT_BUF_LEN];
strcpy(buf, peek());
char *c = buf;
while (c && *c)
{
*c = tolower(*c);
c++;
}
set(buf);
}
validate();
}
// -----------------------------------------------------
void AsciiString::removeLastChar()
{
validate();
if (m_data)
{
int len = strlen(peek());
if (len > 0)
{
ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
peek()[len - 1] = 0;
}
}
validate();
}
// -----------------------------------------------------
void AsciiString::format(AsciiString format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void AsciiString::format(const char* format, ...)
{
validate();
va_list args;
va_start(args, format);
format_va(format, args);
va_end(args);
validate();
}
// -----------------------------------------------------
void AsciiString::format_va(const AsciiString& format, va_list args)
{
validate();
char buf[MAX_FORMAT_BUF_LEN];
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format.str(), args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
// -----------------------------------------------------
void AsciiString::format_va(const char* format, va_list args)
{
validate();
char buf[MAX_FORMAT_BUF_LEN];
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format, args) < 0)
throw ERROR_OUT_OF_MEMORY;
set(buf);
validate();
}
// -----------------------------------------------------
Bool AsciiString::startsWith(const char* p) const
{
if (*p == 0)
return true; // everything starts with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strncmp(peek(), p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::startsWithNoCase(const char* p) const
{
if (*p == 0)
return true; // everything starts with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strnicmp(peek(), p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::endsWith(const char* p) const
{
if (*p == 0)
return true; // everything ends with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
}
// -----------------------------------------------------
Bool AsciiString::endsWithNoCase(const char* p) const
{
if (*p == 0)
return true; // everything ends with the empty string
int lenThis = getLength();
int lenThat = strlen(p);
if (lenThis < lenThat)
return false; // that must be smaller than this
return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
}
//-----------------------------------------------------------------------------
Bool AsciiString::isNone() const
{
return m_data && stricmp(peek(), "None") == 0;
}
//-----------------------------------------------------------------------------
Bool AsciiString::nextToken(AsciiString* tok, const char* seps)
{
if (this->isEmpty() || tok == this)
return false;
if (seps == NULL)
seps = " \n\r\t";
char* start = skipSeps(peek(), seps);
char* end = skipNonSeps(start, seps);
if (end > start)
{
Int len = end - start;
char* tmp = tok->getBufferForRead(len + 1);
memcpy(tmp, start, len);
tmp[len] = 0;
this->set(end);
return true;
}
else
{
this->clear();
tok->clear();
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,297 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Game Engine Common
//
// File name: CDManager.cpp
//
// Created: 11/26/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CDManager.h"
#include "GameLogic/GameLogic.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
CDManagerInterface* TheCDManager = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// CDDrive::CDDrive
//============================================================================
CDDrive::CDDrive()
: m_disk(CD::UNKNOWN_DISK)
{
m_diskName.clear();
m_drivePath.clear();
}
//============================================================================
// CDDrive::~CDDrive
//============================================================================
CDDrive::~CDDrive()
{
}
//============================================================================
// CDDrive::getPath
//============================================================================
AsciiString CDDrive::getPath( void )
{
return m_drivePath;
}
//============================================================================
// CDDrive::getDiskName
//============================================================================
AsciiString CDDrive::getDiskName( void )
{
return m_diskName;
}
void CDDrive::refreshInfo( void )
{
// map disk names to disk ID
m_disk = CD::UNKNOWN_DISK;
}
//============================================================================
// CDDrive::getDisk
//============================================================================
CD::Disk CDDrive::getDisk( void )
{
return m_disk;
}
//============================================================================
// CDDrive::setPath
//============================================================================
void CDDrive::setPath( const Char *path )
{
m_drivePath = path;
}
//============================================================================
// CDManager::CDManager
//============================================================================
CDManager::CDManager()
{
}
//============================================================================
// CDManager::~CDManager
//============================================================================
CDManager::~CDManager()
{
destroyAllDrives();
}
//============================================================================
// CDManager::init
//============================================================================
void CDManager::init( void )
{
}
//============================================================================
// CDManager::update
//============================================================================
void CDManager::update( void )
{
// Every so often, check to make sure the CD is still in the drive
if ((TheGameLogic->getFrame() % 300) == 299) {
refreshDrives();
}
}
//============================================================================
// CDManager::reset
//============================================================================
void CDManager::reset( void )
{
}
//============================================================================
// CDManager::driveCount
//============================================================================
Int CDManager::driveCount( void )
{
return m_drives.nodeCount();
}
//============================================================================
// CDManager::getDrive
//============================================================================
CDDriveInterface* CDManager::getDrive( Int index )
{
CDDriveInterface *cd = NULL;
LListNode *node = m_drives.getNode( index );
if ( node )
{
cd = (CDDriveInterface*) node->item();
}
return cd;
}
//============================================================================
// CDManager::newDrive
//============================================================================
CDDriveInterface* CDManager::newDrive( const Char *path )
{
CDDrive *drive= (CDDrive*) createDrive();
if ( drive )
{
drive->setPath( path );
drive->m_node.setItem( drive );
m_drives.add( &drive->m_node );
}
return drive;
}
//============================================================================
// CDManager::refreshDrives
//============================================================================
void CDManager::refreshDrives( void )
{
LListNode *node = m_drives.firstNode();
while ( node )
{
CDDriveInterface *drive = (CDDriveInterface *) node->item();
if ( drive )
{
drive->refreshInfo();
}
node = node->next();
}
}
//============================================================================
// CDManager::destroyAllDrives
//============================================================================
void CDManager::destroyAllDrives( void )
{
LListNode *node;
while ( (node = m_drives.firstNode() ) != NULL )
{
node->remove();
CDDriveInterface *drive = (CDDriveInterface *) node->item();
if ( drive )
{
delete drive;
}
}
}

View File

@@ -0,0 +1,204 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Compression.cpp /////////////////////////////////////////////////////
// Author: Matthew D. Campbell
//////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Compression.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///// Performance Testing ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//#define TEST_COMPRESSION
#ifdef TEST_COMPRESSION
#include "GameClient/MapUtil.h"
#include "Common/FileSystem.h"
#include "Common/File.h"
#include "Common/PerfTimer.h"
enum { NUM_TIMES = 1 };
struct CompData
{
public:
Int origSize;
Int compressedSize[COMPRESSION_MAX+1];
};
#define TEST_COMPRESSION_MIN COMPRESSION_BTREE
#define TEST_COMPRESSION_MAX COMPRESSION_MAX
void DoCompressTest( void )
{
Int i;
/*
PerfGather *s_compressGathers[TEST_COMPRESSION_MAX+1];
PerfGather *s_decompressGathers[TEST_COMPRESSION_MAX+1];
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
{
s_compressGathers[i] = new PerfGather(CompressionManager::getCompressionNameByType((CompressionType)i));
s_decompressGathers[i] = new PerfGather(CompressionManager::getDecompressionNameByType((CompressionType)i));
}
*/
std::map<AsciiString, CompData> s_sizes;
std::map<AsciiString, MapMetaData>::const_iterator it = TheMapCache->find("userdata\\maps\\_usa01\\_usa01.map");
if (it != TheMapCache->end())
{
//if (it->second.m_isOfficial)
//{
//++it;
//continue;
//}
//static Int count = 0;
//if (count++ > 2)
//break;
File *f = TheFileSystem->openFile(it->first.str());
if (f)
{
DEBUG_LOG(("***************************\nTesting '%s'\n\n", it->first.str()));
Int origSize = f->size();
UnsignedByte *buf = (UnsignedByte *)f->readEntireAndClose();
UnsignedByte *uncompressedBuf = NEW UnsignedByte[origSize];
CompData d = s_sizes[it->first];
d.origSize = origSize;
d.compressedSize[COMPRESSION_NONE] = origSize;
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
{
DEBUG_LOG(("=================================================\n"));
DEBUG_LOG(("Compression Test %d\n", i));
Int maxCompressedSize = CompressionManager::getMaxCompressedSize( origSize, (CompressionType)i );
DEBUG_LOG(("Orig size is %d, max compressed size is %d bytes\n", origSize, maxCompressedSize));
UnsignedByte *compressedBuf = NEW UnsignedByte[maxCompressedSize];
memset(compressedBuf, 0, maxCompressedSize);
memset(uncompressedBuf, 0, origSize);
Int compressedLen, decompressedLen;
for (Int j=0; j < NUM_TIMES; ++j)
{
//s_compressGathers[i]->startTimer();
compressedLen = CompressionManager::compressData((CompressionType)i, buf, origSize, compressedBuf, maxCompressedSize);
//s_compressGathers[i]->stopTimer();
//s_decompressGathers[i]->startTimer();
decompressedLen = CompressionManager::decompressData(compressedBuf, compressedLen, uncompressedBuf, origSize);
//s_decompressGathers[i]->stopTimer();
}
d.compressedSize[i] = compressedLen;
DEBUG_LOG(("Compressed len is %d (%g%% of original size)\n", compressedLen, (double)compressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(compressedLen, ("Failed to compress\n"));
DEBUG_LOG(("Decompressed len is %d (%g%% of original size)\n", decompressedLen, (double)decompressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(decompressedLen == origSize, ("orig size does not match compressed+uncompressed output\n"));
if (decompressedLen == origSize)
{
Int ret = memcmp(buf, uncompressedBuf, origSize);
if (ret != 0)
{
DEBUG_CRASH(("orig buffer does not match compressed+uncompressed output - ret was %d\n", ret));
}
}
delete compressedBuf;
compressedBuf = NULL;
}
DEBUG_LOG(("d = %d -> %d\n", d.origSize, d.compressedSize[i]));
s_sizes[it->first] = d;
DEBUG_LOG(("s_sizes[%s] = %d -> %d\n", it->first.str(), s_sizes[it->first].origSize, s_sizes[it->first].compressedSize[i]));
delete[] buf;
buf = NULL;
delete[] uncompressedBuf;
uncompressedBuf = NULL;
}
++it;
}
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
{
Real maxCompression = 1000.0f;
Real minCompression = 0.0f;
Int totalUncompressedBytes = 0;
Int totalCompressedBytes = 0;
for (std::map<AsciiString, CompData>::iterator cd = s_sizes.begin(); cd != s_sizes.end(); ++cd)
{
CompData d = cd->second;
Real ratio = d.compressedSize[i]/(Real)d.origSize;
maxCompression = min(maxCompression, ratio);
minCompression = max(minCompression, ratio);
totalUncompressedBytes += d.origSize;
totalCompressedBytes += d.compressedSize[i];
}
DEBUG_LOG(("***************************************************\n"));
DEBUG_LOG(("Compression method %s:\n", CompressionManager::getCompressionNameByType((CompressionType)i)));
DEBUG_LOG(("%d bytes compressed to %d (%g%%)\n", totalUncompressedBytes, totalCompressedBytes,
totalCompressedBytes/(Real)totalUncompressedBytes*100.0f));
DEBUG_LOG(("Min ratio: %g%%, Max ratio: %g%%\n",
minCompression*100.0f, maxCompression*100.0f));
DEBUG_LOG(("\n"));
}
/*
PerfGather::dumpAll(10000);
PerfGather::resetAll();
CopyFile( "AAAPerfStats.csv", "AAACompressPerfStats.csv", FALSE );
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
{
delete s_compressGathers[i];
s_compressGathers[i] = NULL;
delete s_decompressGathers[i];
s_decompressGathers[i] = NULL;
}
*/
}
#endif // TEST_COMPRESSION

View File

@@ -0,0 +1,233 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: CopyProtection.cpp //////////////////////////////////////////////////
// Author: Matthew D. Campbell
// Taken From: Denzil Long's code in Tiberian Sun, by way of Yuri's Revenge
//////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h"
#include "Common/CopyProtection.h"
#ifndef DO_COPY_PROTECTION
//#pragma MESSAGE("*********************************************************")
//#pragma MESSAGE("************* COPY PROTECTION IS DISABLED ***************")
//#pragma MESSAGE("*********************************************************")
#else
LPVOID CopyProtect::s_protectedData = NULL;
static const char* const LAUNCHER_GUID =
"150C6462-4E49-4ccf-B073-57579569D994"; // Generals Multiplayer Test Launcher GUID
static const char* const protectGUID =
"6096561D-8A70-48ed-9FF8-18552419E50D"; // Generals Multiplayer Test Protect GUID
/*
static const char* const LAUNCHER_GUID =
"FB327081-64F2-43d8-9B72-503C3B765134"; // Generals Launcher GUID
static const char* const protectGUID =
"7BEB9006-CC19-4aca-913A-C870A88DE01A"; // Generals Protect GUID
*/
#if defined(_DEBUG) || defined(_INTERNAL)
Bool skipProtection(void)
{
//return FALSE;
if (FindWindow("Afx:400000:8:10011:0:fe8e0125", NULL) != NULL) // DevStudio
{
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
return TRUE;
}
//return FALSE;
return TRUE;
}
#endif
// ---------------------------------------------------------------------------
Bool CopyProtect::isLauncherRunning(void)
{
DEBUG_LOG(("COPYPROTECTION - Checking if launcher is running\n"));
HANDLE launcherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID);
Bool isRunning = (GetLastError() == ERROR_ALREADY_EXISTS);
if (launcherMutex != NULL)
{
CloseHandle(launcherMutex);
}
DEBUG_LOG(("result was %d\n", (int)isRunning));
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
return TRUE;
}
#endif
return isRunning;
}
// ---------------------------------------------------------------------------
Bool CopyProtect::notifyLauncher(void)
{
DEBUG_LOG(("COPYPROTECTION - Notify launcher\n"));
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
return TRUE;
}
#endif
// Force system to create message queue
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Signal launcher to send the beef
unsigned long eventTime = (timeGetTime() + 60000);
HANDLE event = NULL;
while (timeGetTime() < eventTime)
{
event = OpenEvent(EVENT_MODIFY_STATE, TRUE, protectGUID);
if (event)
{
break;
}
Sleep(0);
}
if (event)
{
SetEvent(event);
CloseHandle(event);
DEBUG_LOG(("Launcher notified.\n"));
DEBUG_LOG(("Waiting for message from launcher.\n"));
unsigned long endTime = (timeGetTime() + 10000);
while (timeGetTime() <= endTime)
{
if (PeekMessage(&msg, NULL, 0xBEEF, 0xBEEF, PM_REMOVE))
{
// 0xBEEF is in the WM_APP - 0xBFFF range
if (msg.message == 0xBEEF)
{
DEBUG_LOG(("COPYPROTECTION - Received message from launcher (Elapsed time %ld).\n",
(10000 - (endTime - timeGetTime()))));
HANDLE mappedFile = (HANDLE)msg.lParam;
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
if (s_protectedData == NULL)
{
DEBUG_LOG(("***** MapViewOfFileEx() Failed!\n"));
break;
}
DEBUG_LOG(("The message says: %s\n", s_protectedData));
return TRUE;
}
}
Sleep(0);
}
DEBUG_LOG(("***** Failed to notify launcher!\n"));
return FALSE;
}
else
{
DEBUG_LOG(("***** Failed to notify launcher!\n"));
}
return FALSE;
}
// ---------------------------------------------------------------------------
void CopyProtect::checkForMessage(UINT message, LPARAM lParam)
{
// 0xBEEF is in the WM_APP - 0xBFFF range
if (message == 0xBEEF)
{
DEBUG_LOG(("COPYPROTECTION - Received message from launcher.\n"));
HANDLE mappedFile = (HANDLE)lParam;
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
if (s_protectedData == NULL)
{
DEBUG_LOG(("***** MapViewOfFileEx() Failed!"));
return;
}
DEBUG_LOG(("The message says: %s\n", s_protectedData));
}
}
// ---------------------------------------------------------------------------
Bool CopyProtect::validate(void)
{
DEBUG_LOG(("COPYPROTECTION - Validating\n"));
DEBUG_LOG(("s_protectedData = %d (%s)\n", s_protectedData, (s_protectedData)?s_protectedData:"EEK!"));
if (s_protectedData != NULL)
{
return (strcmp((const char*)s_protectedData,
"Play the \"Command & Conquer: Generals\" Multiplayer Test.") == 0);
}
#if defined(_DEBUG) || defined(_INTERNAL)
if (skipProtection())
{
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
return TRUE;
}
#endif
return FALSE;
}
// ---------------------------------------------------------------------------
void CopyProtect::shutdown(void)
{
DEBUG_LOG(("COPYPROTECTION - Shutdown\n"));
if (s_protectedData != NULL)
{
UnmapViewOfFile(s_protectedData);
s_protectedData = NULL;
}
}
#endif // DO_COPY_PROTECTION

View File

@@ -0,0 +1,39 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CriticalSection.h"
// Definitions.
FastCriticalSectionClass TheAsciiStringCriticalSection;
CriticalSection *TheUnicodeStringCriticalSection = NULL;
CriticalSection *TheDmaCriticalSection = NULL;
CriticalSection *TheMemoryPoolCriticalSection = NULL;
CriticalSection *TheDebugLogCriticalSection = NULL;
#ifdef PERF_TIMERS
PerfGather TheCritSecPerfGather("CritSec");
#endif

View File

@@ -0,0 +1,988 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// DataChunk.cpp
// Implementation of Data Chunk save/load system
// Author: Michael S. Booth, October 2000
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "stdlib.h"
#include "string.h"
#include "Compression.h"
#include "Common/DataChunk.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
// If verbose, lots of debug logging.
#define not_VERBOSE
CachedFileInputStream::CachedFileInputStream(void):m_buffer(NULL),m_size(0)
{
}
CachedFileInputStream::~CachedFileInputStream(void)
{
if (m_buffer) {
delete[] m_buffer;
m_buffer=NULL;
}
}
Bool CachedFileInputStream::open(AsciiString path)
{
File *file=TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
m_size = 0;
if (file) {
m_size=file->size();
if (m_size) {
m_buffer = file->readEntireAndClose();
file = NULL;
}
m_pos=0;
}
if (CompressionManager::isDataCompressed(m_buffer, m_size) == 0)
{
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is uncompressed at %d bytes!\n", path.str(), m_size));
}
else
{
Int uncompLen = CompressionManager::getUncompressedSize(m_buffer, m_size);
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is compressed! It should go from %d to %d\n", path.str(),
// m_size, uncompLen));
char *uncompBuffer = NEW char[uncompLen];
Int actualLen = CompressionManager::decompressData(m_buffer, m_size, uncompBuffer, uncompLen);
if (actualLen == uncompLen)
{
//DEBUG_LOG(("Using uncompressed data\n"));
delete[] m_buffer;
m_buffer = uncompBuffer;
m_size = uncompLen;
}
else
{
//DEBUG_LOG(("Decompression failed - using compressed data\n"));
// decompression failed. Maybe we invalidly thought it was compressed?
delete[] uncompBuffer;
}
}
//if (m_size >= 4)
//{
// DEBUG_LOG(("File starts as '%c%c%c%c'\n", m_buffer[0], m_buffer[1],
// m_buffer[2], m_buffer[3]));
//}
if (file)
{
file->close();
}
return m_size != 0;
}
void CachedFileInputStream::close(void)
{
if (m_buffer) {
delete[] m_buffer;
m_buffer=NULL;
}
m_pos=0;
m_size=0;
}
Int CachedFileInputStream::read(void *pData, Int numBytes)
{
if (m_buffer) {
if ((numBytes+m_pos)>m_size) {
numBytes=m_size-m_pos;
}
if (numBytes) {
memcpy(pData,m_buffer+m_pos,numBytes);
m_pos+=numBytes;
}
return(numBytes);
}
return 0;
}
UnsignedInt CachedFileInputStream::tell(void)
{
return m_pos;
}
Bool CachedFileInputStream::absoluteSeek(UnsignedInt pos)
{
if (pos<0) return false;
if (pos>m_size) {
pos=m_size;
}
m_pos=pos;
return true;
}
Bool CachedFileInputStream::eof(void)
{
return m_size==m_pos;
}
void CachedFileInputStream::rewind()
{
m_pos=0;
}
// -----------------------------------------------------------
//
// FileInputStream - helper class. Used to read in data using a FILE *
//
/*
FileInputStream::FileInputStream(void):m_file(NULL)
{
}
FileInputStream::~FileInputStream(void)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
Bool FileInputStream::open(AsciiString path)
{
m_file = TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
return m_file==NULL?false:true;
}
void FileInputStream::close(void)
{
if (m_file != NULL) {
m_file->close();
m_file = NULL;
}
}
Int FileInputStream::read(void *pData, Int numBytes)
{
int bytesRead = 0;
if (m_file != NULL) {
bytesRead = m_file->read(pData, numBytes);
}
return(bytesRead);
}
UnsignedInt FileInputStream::tell(void)
{
UnsignedInt pos = 0;
if (m_file != NULL) {
pos = m_file->position();
}
return(pos);
}
Bool FileInputStream::absoluteSeek(UnsignedInt pos)
{
if (m_file != NULL) {
return (m_file->seek(pos, File::START) != -1);
}
return(false);
}
Bool FileInputStream::eof(void)
{
if (m_file != NULL) {
return (m_file->size() == m_file->position());
}
return(true);
}
void FileInputStream::rewind()
{
if (m_file != NULL) {
m_file->seek(0, File::START);
}
}
*/
//----------------------------------------------------------------------
// DataChunkOutput
// Data will be stored to a temporary m_tmp_file until the DataChunkOutput
// object is destroyed. At that time, the actual output m_tmp_file will
// be written, including a table of m_contents.
//----------------------------------------------------------------------
#define TEMP_FILENAME "_tmpChunk.dat"
DataChunkOutput::DataChunkOutput( OutputStream *pOut ) :
m_pOut(pOut)
{
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
tmpFileName.concat(TEMP_FILENAME);
m_tmp_file = ::fopen( tmpFileName.str(), "wb" );
// Added Sadullah Nader
// Initializations missing and needed
m_chunkStack = NULL;
// End Add
}
DataChunkOutput::~DataChunkOutput()
{
// store the table of m_contents
m_contents.write(*m_pOut);
// Rewind the temp m_tmp_file
::fclose(m_tmp_file);
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
tmpFileName.concat(TEMP_FILENAME);
m_tmp_file = ::fopen( tmpFileName.str(), "rb" );
::fseek(m_tmp_file, 0, SEEK_SET);
// append the temp m_tmp_file m_contents
char buffer[256];
int len = 256;
while( len == 256 )
{
// copy data from the temp m_tmp_file to the output m_tmp_file
len = ::fread( buffer, 1, 256, m_tmp_file );
m_pOut->write( buffer, len );
}
::fclose(m_tmp_file);
}
void DataChunkOutput::openDataChunk( char *name, DataChunkVersionType ver )
{
// allocate (or get existing) ID from the table of m_contents
UnsignedInt id = m_contents.allocateID( AsciiString(name) );
// allocate a new chunk and place it on top of the chunk stack
OutputChunk *c = newInstance(OutputChunk);
c->next = m_chunkStack;
m_chunkStack = c;
m_chunkStack->id = id;
// store the chunk ID
::fwrite( (const char *)&id, sizeof(UnsignedInt), 1, m_tmp_file );
// store the chunk version number
::fwrite( (const char *)&ver, sizeof(DataChunkVersionType), 1, m_tmp_file );
// remember this m_tmp_file position so we can write the real data size later
c->filepos = ::ftell(m_tmp_file);
#ifdef VERBOSE
DEBUG_LOG(("Writing chunk %s at %d (%x)\n", name, ::ftell(m_tmp_file), ::ftell(m_tmp_file)));
#endif
// store a placeholder for the data size
Int dummy = 0xffff;
::fwrite( (const char *)&dummy, sizeof(Int), 1, m_tmp_file );
}
void DataChunkOutput::closeDataChunk( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
return;
}
// remember where we are
Int here = ::ftell(m_tmp_file);
// rewind to store the data size
::fseek(m_tmp_file, m_chunkStack->filepos , SEEK_SET);
// compute data size (not including the actual data size itself)
Int size = here - m_chunkStack->filepos - sizeof(Int);
// store the data size
::fwrite( (const char *)&size, sizeof(Int) , 1, m_tmp_file );
// go back to where we were
::fseek(m_tmp_file, here , SEEK_SET);
// pop the chunk off the stack
OutputChunk *c = m_chunkStack;
#ifdef VERBOSE
DEBUG_LOG(("Closing chunk %s at %d (%x)\n", m_contents.getName(c->id).str(), here, here));
#endif
m_chunkStack = m_chunkStack->next;
c->deleteInstance();
}
void DataChunkOutput::writeReal( Real r )
{
::fwrite( (const char *)&r, sizeof(float) , 1, m_tmp_file );
}
void DataChunkOutput::writeInt( Int i )
{
::fwrite( (const char *)&i, sizeof(Int) , 1, m_tmp_file );
}
void DataChunkOutput::writeByte( Byte b )
{
::fwrite( (const char *)&b, sizeof(Byte) , 1, m_tmp_file );
}
void DataChunkOutput::writeArrayOfBytes(char *ptr, Int len)
{
::fwrite( (const char *)ptr, 1, len , m_tmp_file );
}
void DataChunkOutput::writeAsciiString( const AsciiString& theString )
{
UnsignedShort len = theString.getLength();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
::fwrite( theString.str(), len , 1, m_tmp_file );
}
void DataChunkOutput::writeUnicodeString( UnicodeString theString )
{
UnsignedShort len = theString.getLength();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
::fwrite( theString.str(), len*sizeof(WideChar) , 1, m_tmp_file );
}
void DataChunkOutput::writeNameKey( const NameKeyType key )
{
AsciiString kname = TheNameKeyGenerator->keyToName(key);
Int keyAndType = m_contents.allocateID(kname);
keyAndType <<= 8;
Dict::DataType t = Dict::DICT_ASCIISTRING;
keyAndType |= (t & 0xff);
writeInt(keyAndType);
}
void DataChunkOutput::writeDict( const Dict& d )
{
UnsignedShort len = d.getPairCount();
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
for (int i = 0; i < len; i++)
{
NameKeyType k = d.getNthKey(i);
AsciiString kname = TheNameKeyGenerator->keyToName(k);
Int keyAndType = m_contents.allocateID(kname);
keyAndType <<= 8;
Dict::DataType t = d.getNthType(i);
keyAndType |= (t & 0xff);
writeInt(keyAndType);
switch(t)
{
case Dict::DICT_BOOL:
writeByte(d.getNthBool(i)?1:0);
break;
case Dict::DICT_INT:
writeInt(d.getNthInt(i));
break;
case Dict::DICT_REAL:
writeReal(d.getNthReal(i));
break;
case Dict::DICT_ASCIISTRING:
writeAsciiString(d.getNthAsciiString(i));
break;
case Dict::DICT_UNICODESTRING:
writeUnicodeString(d.getNthUnicodeString(i));
break;
default:
DEBUG_CRASH(("impossible"));
break;
}
}
}
//----------------------------------------------------------------------
// DataChunkTableOfContents
//----------------------------------------------------------------------
DataChunkTableOfContents::DataChunkTableOfContents( void ) :
m_list(NULL),
m_nextID(1),
m_listLength(0),
m_headerOpened(false)
{
}
DataChunkTableOfContents::~DataChunkTableOfContents()
{
Mapping *m, *next;
// free all list elements
for( m=m_list; m; m=next )
{
next = m->next;
m->deleteInstance();
}
}
// return mapping data
Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
{
Mapping *m;
for( m=m_list; m; m=m->next )
if (name == m->name )
return m;
return NULL;
}
// convert name to integer identifier
UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
{
Mapping *m = findMapping( name );
if (m)
return m->id;
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
return 0;
}
// convert integer identifier to name
AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
{
Mapping *m;
for( m=m_list; m; m=m->next )
if (m->id == id)
return m->name;
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
return AsciiString::TheEmptyString;
}
// create new ID for given name or return existing mapping
UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
{
Mapping *m = findMapping( name );
if (m)
return m->id;
else
{
// allocate new id mapping
m = newInstance(Mapping);
m->id = m_nextID++;
m->name = name ;
// prepend to list
m->next = m_list;
m_list = m;
m_listLength++;
return m->id;
}
}
// output the table of m_contents to a binary m_tmp_file stream
void DataChunkTableOfContents::write( OutputStream &s )
{
Mapping *m;
unsigned char len;
Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
s.write(tag,sizeof(tag));
// output number of elements in the table
s.write( (void *)&this->m_listLength, sizeof(Int) );
// output symbol table
for( m=this->m_list; m; m=m->next )
{
len = m->name.getLength();
s.write( (char *)&len, sizeof(unsigned char) );
s.write( (char *)m->name.str(), len);
s.write( (char *)&m->id, sizeof(UnsignedInt) );
}
}
// read the table of m_contents from a binary m_tmp_file stream
// TODO: Should this reset the symbol table?
// Append symbols to table
void DataChunkTableOfContents::read( ChunkInputStream &s)
{
Int count, i;
UnsignedInt maxID = 0;
unsigned char len;
Mapping *m;
Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
s.read(tag,sizeof(tag));
if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
return; // Don't throw, may happen with legacy files.
}
// get number of symbols in table
s.read( (char *)&count, sizeof(Int) );
for( i=0; i<count; i++ )
{
// allocate new id mapping
m = newInstance(Mapping);
// read string length
s.read( (char *)&len, sizeof(unsigned char) );
// allocate and read in string
if (len>0) {
char *str = m->name.getBufferForRead(len);
s.read( str, len );
str[len] = '\000';
}
// read id
s.read( (char *)&m->id, sizeof(UnsignedInt) );
// prepend to list
m->next = this->m_list;
this->m_list = m;
this->m_listLength++;
// track max ID used
if (m->id > maxID)
maxID = m->id;
}
m_headerOpened = count > 0 && !s.eof();
// adjust next ID so no ID's are reused
this->m_nextID = max( this->m_nextID, maxID+1 );
}
//----------------------------------------------------------------------
// DataChunkInput
//----------------------------------------------------------------------
DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
m_userData(NULL),
m_currentObject(NULL),
m_chunkStack(NULL),
m_parserList(NULL)
{
// read table of m_contents
m_contents.read(*m_file);
// store location of first data chunk
m_fileposOfFirstChunk = m_file->tell();
}
DataChunkInput::~DataChunkInput()
{
clearChunkStack();
UserParser *p, *next;
for (p=m_parserList; p; p=next) {
next = p->next;
p->deleteInstance();
}
}
// register a user parsing function for a given DataChunk label
void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
DataChunkParserPtr parser, void *userData )
{
UserParser *p = newInstance(UserParser);
p->label.set( label );
p->parentLabel.set(parentLabel );
p->parser = parser;
p->userData = userData;
// prepend parser to parser list
p->next = m_parserList;
m_parserList = p;
}
// parse the chunk stream using registered parsers
// it is assumed that the file position is at the start of a data chunk
// (it can be inside a parent chunk) when parse is called.
Bool DataChunkInput::parse( void *userData )
{
AsciiString label;
AsciiString parentLabel;
DataChunkVersionType ver;
UserParser *parser;
Bool scopeOK;
DataChunkInfo info;
// If the header wasn't a chunk table of contents, we can't parse.
if (!m_contents.isOpenedForRead()) {
return false;
}
// if we are inside a data chunk right now, get its name
if (m_chunkStack)
parentLabel = m_contents.getName( m_chunkStack->id );
while( atEndOfFile() == false )
{
if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
break;
}
}
// open the chunk
label = openDataChunk( &ver );
if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
break;
}
// find a registered parser for this chunk
for( parser=m_parserList; parser; parser=parser->next )
{
// chunk labels must match
if ( parser->label == label )
{
// make sure parent name (scope) also matches
scopeOK = true;
if (parentLabel != parser->parentLabel)
scopeOK = false;
if (scopeOK)
{
// m_tmp_file out the chunk info and call the user parser
info.label = label;
info.parentLabel = parentLabel;
info.version = ver;
info.dataSize = getChunkDataSize();
if (parser->parser( *this, &info, userData ) == false)
return false;
break;
}
}
}
// close chunk (and skip to end if need be)
closeDataChunk();
}
return true;
}
// clear the stack
void DataChunkInput::clearChunkStack( void )
{
InputChunk *c, *next;
for( c=m_chunkStack; c; c=next )
{
next = c->next;
c->deleteInstance();
}
m_chunkStack = NULL;
}
// reset the stream to just-opened state - ready to parse the first chunk
void DataChunkInput::reset( void )
{
clearChunkStack();
m_file->absoluteSeek( m_fileposOfFirstChunk );
}
// Checks if the file has our initial tag word.
Bool DataChunkInput::isValidFileType(void)
{
return m_contents.isOpenedForRead();
}
AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
{
// allocate a new chunk and place it on top of the chunk stack
InputChunk *c = newInstance(InputChunk);
c->id = 0;
c->version = 0;
c->dataSize = 0;
//DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
// read the chunk ID
m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
decrementDataLeft( sizeof(UnsignedInt) );
// read the chunk version number
m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
decrementDataLeft( sizeof(DataChunkVersionType) );
// read the chunk data size
m_file->read( (char *)&c->dataSize, sizeof(Int) );
decrementDataLeft( sizeof(Int) );
// all of the data remains to be read
c->dataLeft = c->dataSize;
c->chunkStart = m_file->tell();
*ver = c->version;
c->next = m_chunkStack;
m_chunkStack = c;
if (this->atEndOfFile()) {
return (AsciiString(""));
}
return m_contents.getName( c->id );
}
// close chunk and move to start of next chunk
void DataChunkInput::closeDataChunk( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
return;
}
if (m_chunkStack->dataLeft > 0)
{
// skip past the remainder of this chunk
m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
decrementDataLeft( m_chunkStack->dataLeft );
}
// pop the chunk off the stack
InputChunk *c = m_chunkStack;
m_chunkStack = m_chunkStack->next;
c->deleteInstance();
}
// return label of current data chunk
AsciiString DataChunkInput::getChunkLabel( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return AsciiString("");
}
return m_contents.getName( m_chunkStack->id );
}
// return version of current data chunk
DataChunkVersionType DataChunkInput::getChunkVersion( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->version;
}
// return size of data stored in this chunk
UnsignedInt DataChunkInput::getChunkDataSize( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->dataSize;
}
// return size of data left to read in this chunk
UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
{
if (m_chunkStack == NULL)
{
// TODO: Throw exception
DEBUG_CRASH(("Bad."));
return NULL;
}
return m_chunkStack->dataLeft;
}
Bool DataChunkInput::atEndOfChunk( void )
{
if (m_chunkStack)
{
if (m_chunkStack->dataLeft <= 0)
return true;
return false;
}
return true;
}
// update data left in chunk(s)
// since data read from a chunk is also read from all parent chunks,
// traverse the chunk stack and decrement the data left for each
void DataChunkInput::decrementDataLeft( Int size )
{
InputChunk *c;
c = m_chunkStack;
while (c) {
c->dataLeft -= size;
c = c->next;
}
// The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
}
Real DataChunkInput::readReal(void)
{
Real r;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
m_file->read( (char *)&r, sizeof(Real) );
decrementDataLeft( sizeof(Real) );
return r;
}
Int DataChunkInput::readInt(void)
{
Int i;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
m_file->read( (char *)&i, sizeof(Int) );
decrementDataLeft( sizeof(Int) );
return i;
}
Byte DataChunkInput::readByte(void)
{
Byte b;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
m_file->read( (char *)&b, sizeof(Byte) );
decrementDataLeft( sizeof(Byte) );
return b;
}
void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
{
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
m_file->read( ptr, len );
decrementDataLeft( len );
}
NameKeyType DataChunkInput::readNameKey(void)
{
Int keyAndType = readInt();
#if (defined(_DEBUG) || defined(_INTERNAL))
Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
DEBUG_ASSERTCRASH(t==Dict::DICT_ASCIISTRING,("Invalid key data."));
#endif
keyAndType >>= 8;
AsciiString kname = m_contents.getName(keyAndType);
NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
return k;
}
Dict DataChunkInput::readDict()
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
Dict d(len);
for (int i = 0; i < len; i++)
{
Int keyAndType = readInt();
Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
keyAndType >>= 8;
AsciiString kname = m_contents.getName(keyAndType);
NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
switch(t)
{
case Dict::DICT_BOOL:
d.setBool(k, readByte() ? true : false);
break;
case Dict::DICT_INT:
d.setInt(k, readInt());
break;
case Dict::DICT_REAL:
d.setReal(k, readReal());
break;
case Dict::DICT_ASCIISTRING:
d.setAsciiString(k, readAsciiString());
break;
case Dict::DICT_UNICODESTRING:
d.setUnicodeString(k, readUnicodeString());
break;
default:
throw ERROR_CORRUPT_FILE_FORMAT;
break;
}
}
return d;
}
AsciiString DataChunkInput::readAsciiString(void)
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
AsciiString theString;
if (len>0) {
char *str = theString.getBufferForRead(len);
m_file->read( str, len );
decrementDataLeft( len );
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
str[len] = '\000';
}
return theString;
}
UnicodeString DataChunkInput::readUnicodeString(void)
{
UnsignedShort len;
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
m_file->read( &len, sizeof(UnsignedShort) );
decrementDataLeft( sizeof(UnsignedShort) );
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
UnicodeString theString;
if (len>0) {
WideChar *str = theString.getBufferForRead(len);
m_file->read( (char*)str, len*sizeof(WideChar) );
decrementDataLeft( len*sizeof(WideChar) );
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
str[len] = '\000';
}
return theString;
}

View File

@@ -0,0 +1,788 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Debug.cpp
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Debug.cpp
//
// Created: Steven Johnson, August 2001
//
// Desc: Debug logging and other debug utilities
//
// ----------------------------------------------------------------------------
// SYSTEM INCLUDES
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
// USER INCLUDES
#define DEBUG_THREADSAFE
#ifdef DEBUG_THREADSAFE
#include "Common/CriticalSection.h"
#endif
#include "Common/Debug.h"
#include "Common/SystemInfo.h"
#include "Common/UnicodeString.h"
#include "GameClient/GameText.h"
#include "GameClient/Keyboard.h"
#include "GameClient/Mouse.h"
#if defined(DEBUG_STACKTRACE) || defined(IG_DEBUG_STACKTRACE)
#include "Common/StackDump.h"
#endif
// Horrible reference, but we really, really need to know if we are windowed.
extern bool DX8Wrapper_IsWindowed;
extern HWND ApplicationHWnd;
extern char *gAppPrefix; /// So WB can have a different log file name.
#ifdef _INTERNAL
// this should ALWAYS be present
#pragma optimize("", off)
#endif
// ----------------------------------------------------------------------------
// DEFINES
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
#if defined(_INTERNAL)
#define DEBUG_FILE_NAME "DebugLogFileI.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevI.txt"
#elif defined(_DEBUG)
#define DEBUG_FILE_NAME "DebugLogFileD.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevD.txt"
#else
#define DEBUG_FILE_NAME "DebugLogFile.txt"
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrev.txt"
#endif
#endif
// ----------------------------------------------------------------------------
// PRIVATE TYPES
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// PRIVATE DATA
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
static FILE *theLogFile = NULL;
#endif
#define LARGE_BUFFER 8192
static char theBuffer[ LARGE_BUFFER ]; // make it big to avoid weird overflow bugs in debug mode
static int theDebugFlags = 0;
static DWORD theMainThreadID = 0;
// ----------------------------------------------------------------------------
// PUBLIC DATA
// ----------------------------------------------------------------------------
char* TheCurrentIgnoreCrashPtr = NULL;
// ----------------------------------------------------------------------------
// PRIVATE PROTOTYPES
// ----------------------------------------------------------------------------
static const char *getCurrentTimeString(void);
static const char *getCurrentTickString(void);
static const char *prepBuffer(const char* format, char *buffer);
#ifdef DEBUG_LOGGING
static void doLogOutput(const char *buffer);
#endif
static int doCrashBox(const char *buffer, Bool logResult);
static void whackFunnyCharacters(char *buf);
#ifdef DEBUG_STACKTRACE
static void doStackDump();
#endif
// ----------------------------------------------------------------------------
// PRIVATE FUNCTIONS
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
inline Bool ignoringAsserts()
{
#if defined(_DEBUG) || defined(_INTERNAL)
return !DX8Wrapper_IsWindowed || (TheGlobalData&&TheGlobalData->m_debugIgnoreAsserts);
#else
return !DX8Wrapper_IsWindowed;
#endif
}
// ----------------------------------------------------------------------------
inline HWND getThreadHWND()
{
return (theMainThreadID == GetCurrentThreadId())?ApplicationHWnd:NULL;
}
// ----------------------------------------------------------------------------
int MessageBoxWrapper( LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
HWND threadHWND = getThreadHWND();
if (!threadHWND)
return (uType & MB_ABORTRETRYIGNORE)?IDIGNORE:IDYES;
return ::MessageBox(threadHWND, lpText, lpCaption, uType);
}
// ----------------------------------------------------------------------------
// getCurrentTimeString
/**
Return the current time in string form
*/
// ----------------------------------------------------------------------------
static const char *getCurrentTimeString(void)
{
time_t aclock;
time(&aclock);
struct tm *newtime = localtime(&aclock);
return asctime(newtime);
}
// ----------------------------------------------------------------------------
// getCurrentTickString
/**
Return the current TickCount in string form
*/
// ----------------------------------------------------------------------------
static const char *getCurrentTickString(void)
{
static char TheTickString[32];
sprintf(TheTickString, "(T=%08lx)",::GetTickCount());
return TheTickString;
}
// ----------------------------------------------------------------------------
// prepBuffer
// zap the buffer and optionally prepend the tick time.
// ----------------------------------------------------------------------------
/**
Empty the buffer passed in, then optionally prepend the current TickCount
value in string form, depending on the setting of theDebugFlags.
*/
static const char *prepBuffer(const char* format, char *buffer)
{
buffer[0] = 0;
#ifdef ALLOW_DEBUG_UTILS
if (theDebugFlags & DEBUG_FLAG_PREPEND_TIME)
{
strcpy(buffer, getCurrentTickString());
strcat(buffer, " ");
}
#endif
return format;
}
// ----------------------------------------------------------------------------
// doLogOutput
/**
send a string directly to the log file and/or console without further processing.
*/
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
static void doLogOutput(const char *buffer)
{
// log message to file
if (theDebugFlags & DEBUG_FLAG_LOG_TO_FILE)
{
if (theLogFile)
{
fprintf(theLogFile, "%s", buffer); // note, no \n (should be there already)
fflush(theLogFile);
}
}
// log message to dev studio output window
if (theDebugFlags & DEBUG_FLAG_LOG_TO_CONSOLE)
{
::OutputDebugString(buffer);
}
}
#endif
// ----------------------------------------------------------------------------
// doCrashBox
/*
present a messagebox with the given message. Depending on user selection,
we exit the app, break into debugger, or continue execution.
*/
// ----------------------------------------------------------------------------
static int doCrashBox(const char *buffer, Bool logResult)
{
int result;
if (!ignoringAsserts()) {
result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING|MB_DEFBUTTON3);
//result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING);
} else {
result = IDIGNORE;
}
switch(result)
{
case IDABORT:
#ifdef DEBUG_LOGGING
if (logResult)
DebugLog("[Abort]\n");
#endif
_exit(1);
break;
case IDRETRY:
#ifdef DEBUG_LOGGING
if (logResult)
DebugLog("[Retry]\n");
#endif
::DebugBreak();
break;
case IDIGNORE:
#ifdef DEBUG_LOGGING
// do nothing, just keep going
if (logResult)
DebugLog("[Ignore]\n");
#endif
break;
}
return result;
}
#ifdef DEBUG_STACKTRACE
// ----------------------------------------------------------------------------
/**
Dumps a stack trace (from the current PC) to logfile and/or console.
*/
static void doStackDump()
{
const int STACKTRACE_SIZE = 24;
const int STACKTRACE_SKIP = 2;
void* stacktrace[STACKTRACE_SIZE];
doLogOutput("\nStack Dump:\n");
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, doLogOutput);
}
#endif
// ----------------------------------------------------------------------------
// whackFunnyCharacters
/**
Eliminates any undesirable nonprinting characters, aside from newline,
replacing them with spaces.
*/
// ----------------------------------------------------------------------------
static void whackFunnyCharacters(char *buf)
{
for (char *p = buf + strlen(buf) - 1; p >= buf; --p)
{
// ok, these are naughty magic numbers, but I'm guessing you know ASCII....
if (*p >= 0 && *p < 32 && *p != 10 && *p != 13)
*p = 32;
}
}
// ----------------------------------------------------------------------------
// PUBLIC FUNCTIONS
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// DebugInit
// ----------------------------------------------------------------------------
#ifdef ALLOW_DEBUG_UTILS
/**
Initialize the debug utilities. This should be called once, as near to the
start of the app as possible, before anything else (since other code will
probably want to make use of it).
*/
void DebugInit(int flags)
{
// if (theDebugFlags != 0)
// ::MessageBox(NULL, "Debug already inited", "", MB_OK|MB_APPLMODAL);
// just quietly allow multiple calls to this, so that static ctors can call it.
if (theDebugFlags == 0)
{
theDebugFlags = flags;
theMainThreadID = GetCurrentThreadId();
#ifdef DEBUG_LOGGING
char dirbuf[ _MAX_PATH ];
::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
char *pEnd = dirbuf + strlen( dirbuf );
while( pEnd != dirbuf )
{
if( *pEnd == '\\' )
{
*(pEnd + 1) = 0;
break;
}
pEnd--;
}
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
strcpy(prevbuf, dirbuf);
strcat(prevbuf, gAppPrefix);
strcat(prevbuf, DEBUG_FILE_NAME_PREV);
strcpy(curbuf, dirbuf);
strcat(curbuf, gAppPrefix);
strcat(curbuf, DEBUG_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theLogFile = fopen(curbuf, "w");
if (theLogFile != NULL)
{
DebugLog("Log %s opened: %s\n", curbuf, getCurrentTimeString());
}
#endif
}
}
#endif
// ----------------------------------------------------------------------------
// DebugLog
// ----------------------------------------------------------------------------
#ifdef DEBUG_LOGGING
/**
Print a character string to the logfile and/or console.
*/
void DebugLog(const char *format, ...)
{
#ifdef DEBUG_THREADSAFE
ScopedCriticalSection scopedCriticalSection(TheDebugLogCriticalSection);
#endif
if (theDebugFlags == 0)
MessageBoxWrapper("DebugLog - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
format = prepBuffer(format, theBuffer);
va_list arg;
va_start(arg, format);
vsprintf(theBuffer + strlen(theBuffer), format, arg);
va_end(arg);
if (strlen(theBuffer) >= sizeof(theBuffer))
MessageBoxWrapper("String too long for debug buffer", "", MB_OK|MB_TASKMODAL);
whackFunnyCharacters(theBuffer);
doLogOutput(theBuffer);
}
#endif
// ----------------------------------------------------------------------------
// DebugCrash
// ----------------------------------------------------------------------------
#ifdef DEBUG_CRASHING
/**
Print a character string to the logfile and/or console, then halt execution
while presenting the user with an exit/debug/ignore dialog containing the same
text message.
*/
void DebugCrash(const char *format, ...)
{
// Note: You might want to make this thread safe, but we cannot. The reason is that
// there is an implicit requirement on other threads that the message loop be running.
// make it not static so that it'll be thread-safe.
// make it big to avoid weird overflow bugs in debug mode
char theCrashBuffer[ LARGE_BUFFER ];
if (theDebugFlags == 0)
{
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
MessageBoxWrapper("DebugCrash - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
}
format = prepBuffer(format, theCrashBuffer);
strcat(theCrashBuffer, "ASSERTION FAILURE: ");
va_list arg;
va_start(arg, format);
vsprintf(theCrashBuffer + strlen(theCrashBuffer), format, arg);
va_end(arg);
if (strlen(theCrashBuffer) >= sizeof(theCrashBuffer))
{
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
MessageBoxWrapper("String too long for debug buffers", "", MB_OK|MB_TASKMODAL);
}
#ifdef DEBUG_LOGGING
if (ignoringAsserts())
{
doLogOutput("**** CRASH IN FULL SCREEN - Auto-ignored, CHECK THIS LOG!\n");
}
whackFunnyCharacters(theCrashBuffer);
doLogOutput(theCrashBuffer);
#endif
#ifdef DEBUG_STACKTRACE
if (!(TheGlobalData && TheGlobalData->m_debugIgnoreStackTrace))
{
doStackDump();
}
#endif
strcat(theCrashBuffer, "\n\nAbort->exception; Retry->debugger; Ignore->continue\n");
int result = doCrashBox(theCrashBuffer, true);
if (result == IDIGNORE && TheCurrentIgnoreCrashPtr != NULL)
{
int yn;
if (!ignoringAsserts())
{
yn = MessageBoxWrapper("Ignore this crash from now on?", "", MB_YESNO|MB_TASKMODAL);
}
else
{
yn = IDYES;
}
if (yn == IDYES)
*TheCurrentIgnoreCrashPtr = 1;
if( TheKeyboard )
TheKeyboard->resetKeys();
if( TheMouse )
TheMouse->reset();
}
}
#endif
// ----------------------------------------------------------------------------
// DebugShutdown
// ----------------------------------------------------------------------------
#ifdef ALLOW_DEBUG_UTILS
/**
Shut down the debug utilities. This should be called once, as near to the
end of the app as possible, after everything else (since other code will
probably want to make use of it).
*/
void DebugShutdown()
{
#ifdef DEBUG_LOGGING
if (theLogFile)
{
DebugLog("Log closed: %s\n", getCurrentTimeString());
fclose(theLogFile);
}
theLogFile = NULL;
#endif
theDebugFlags = 0;
}
// ----------------------------------------------------------------------------
// DebugGetFlags
// ----------------------------------------------------------------------------
/**
Get the current values for the flags passed to DebugInit. Most code will never
need to use this; the most common usage would be to temporarily enable or disable
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
*/
int DebugGetFlags()
{
return theDebugFlags;
}
// ----------------------------------------------------------------------------
// DebugSetFlags
// ----------------------------------------------------------------------------
/**
Set the current values for the flags passed to DebugInit. Most code will never
need to use this; the most common usage would be to temporarily enable or disable
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
*/
void DebugSetFlags(int flags)
{
theDebugFlags = flags;
}
#endif // ALLOW_DEBUG_UTILS
#ifdef DEBUG_PROFILE
// ----------------------------------------------------------------------------
SimpleProfiler::SimpleProfiler()
{
QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq);
m_startThisSession = 0;
m_totalThisSession = 0;
m_totalAllSessions = 0;
m_numSessions = 0;
}
// ----------------------------------------------------------------------------
void SimpleProfiler::start()
{
DEBUG_ASSERTCRASH(m_startThisSession == 0, ("already started"));
QueryPerformanceCounter((LARGE_INTEGER*)&m_startThisSession);
}
// ----------------------------------------------------------------------------
void SimpleProfiler::stop()
{
if (m_startThisSession != 0)
{
__int64 stop;
QueryPerformanceCounter((LARGE_INTEGER*)&stop);
m_totalThisSession = stop - m_startThisSession;
m_totalAllSessions += stop - m_startThisSession;
m_startThisSession = 0;
++m_numSessions;
}
}
// ----------------------------------------------------------------------------
void SimpleProfiler::stopAndLog(const char *msg, int howOftenToLog, int howOftenToResetAvg)
{
stop();
// howOftenToResetAvg==0 means "never reset"
if (howOftenToResetAvg > 0 && m_numSessions >= howOftenToResetAvg)
{
m_numSessions = 0;
m_totalAllSessions = 0;
DEBUG_LOG(("%s: reset averages\n",msg));
}
DEBUG_ASSERTLOG(m_numSessions % howOftenToLog != 0, ("%s: %f msec, total %f msec, avg %f msec\n",msg,getTime(),getTotalTime(),getAverageTime()));
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getTime()
{
stop();
return (double)m_totalThisSession / (double)m_freq * 1000.0;
}
// ----------------------------------------------------------------------------
int SimpleProfiler::getNumSessions()
{
stop();
return m_numSessions;
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getTotalTime()
{
stop();
if (!m_numSessions)
return 0.0;
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq);
}
// ----------------------------------------------------------------------------
double SimpleProfiler::getAverageTime()
{
stop();
if (!m_numSessions)
return 0.0;
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq * (double)m_numSessions);
}
#endif // ALLOW_DEBUG_UTILS
// ----------------------------------------------------------------------------
// ReleaseCrash
// ----------------------------------------------------------------------------
/**
Halt the application, EVEN IN FINAL RELEASE BUILDS. This should be called
only when a crash is guaranteed by continuing, and no meaningful continuation
of processing is possible, even by throwing an exception.
*/
#define RELEASECRASH_FILE_NAME "ReleaseCrashInfo.txt"
#define RELEASECRASH_FILE_NAME_PREV "ReleaseCrashInfoPrev.txt"
static FILE *theReleaseCrashLogFile = NULL;
static void releaseCrashLogOutput(const char *buffer)
{
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "%s", buffer); // note, no \n (should be there already)
fflush(theReleaseCrashLogFile);
}
}
void ReleaseCrash(const char *reason)
{
/// do additional reporting on the crash, if possible
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
//#if defined(_DEBUG) || defined(_INTERNAL)
// /* static */ char buff[8192]; // not so static so we can be threadsafe
// _snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);/
// buff[8191] = 0;
// ::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
//#else
// ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
//#endif
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
if (TheGlobalData==NULL) {
return; // We are shutting down, and TheGlobalData has been freed. jba. [4/15/2003]
}
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
strcat(curbuf, RELEASECRASH_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theReleaseCrashLogFile = fopen(curbuf, "w");
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), reason);
fprintf(theReleaseCrashLogFile, "\nLast error:\n%s\n\nCurrent stack:\n", g_LastErrorDump.str());
const int STACKTRACE_SIZE = 12;
const int STACKTRACE_SKIP = 6;
void* stacktrace[STACKTRACE_SIZE];
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
fflush(theReleaseCrashLogFile);
fclose(theReleaseCrashLogFile);
theReleaseCrashLogFile = NULL;
}
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
#if defined(_DEBUG) || defined(_INTERNAL)
/* static */ char buff[8192]; // not so static so we can be threadsafe
_snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);
buff[8191] = 0;
::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
#else
// crash error messaged changed 3/6/03 BGC
// ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
// ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
// crash error message changed again 8/22/03 M Lorenzen... made this message box modal to the system so it will appear on top of any task-modal windows, splash-screen, etc.
::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.",
"Technical Difficulties...",
MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
#endif
_exit(1);
}
void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m)
{
if (!TheGameText) {
ReleaseCrash(m.str());
// This won't ever return
return;
}
UnicodeString prompt = TheGameText->fetch(p);
UnicodeString mesg = TheGameText->fetch(m);
/// do additional reporting on the crash, if possible
if (!DX8Wrapper_IsWindowed) {
if (ApplicationHWnd) {
ShowWindow(ApplicationHWnd, SW_HIDE);
}
}
if (TheSystemIsUnicode)
{
::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
}
else
{
// However, if we're using the default version of the message box, we need to
// translate the string into an AsciiString
AsciiString promptA, mesgA;
promptA.translate(prompt);
mesgA.translate(mesg);
//Make sure main window is not TOP_MOST
::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR);
}
char prevbuf[ _MAX_PATH ];
char curbuf[ _MAX_PATH ];
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
strcat(curbuf, RELEASECRASH_FILE_NAME);
remove(prevbuf);
rename(curbuf, prevbuf);
theReleaseCrashLogFile = fopen(curbuf, "w");
if (theReleaseCrashLogFile)
{
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), mesg.str());
const int STACKTRACE_SIZE = 12;
const int STACKTRACE_SKIP = 6;
void* stacktrace[STACKTRACE_SIZE];
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
fflush(theReleaseCrashLogFile);
fclose(theReleaseCrashLogFile);
theReleaseCrashLogFile = NULL;
}
_exit(1);
}

View File

@@ -0,0 +1,135 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#if (0)
#include "Common/Directory.h"
//-------------------------------------------------------------------------------------------------
static void TimetToFileTime( time_t t, FILETIME& ft )
{
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
ft.dwLowDateTime = (DWORD) ll;
ft.dwHighDateTime = ll >>32;
}
static time_t FileTimeToTimet( const FILETIME& ft )
{
LONGLONG ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime - 116444736000000000;
ll /= 10000000;
return (time_t)ll;
}
//-------------------------------------------------------------------------------------------------
void FileInfo::set( const WIN32_FIND_DATA& info )
{
filename = info.cFileName;
creationTime = FileTimeToTimet(info.ftCreationTime);
accessTime = FileTimeToTimet(info.ftLastAccessTime);
modTime = FileTimeToTimet(info.ftLastWriteTime);
attributes = info.dwFileAttributes;
filesize = info.nFileSizeLow;
//DEBUG_LOG(("FileInfo::set(): fname=%s, size=%d\n", filename.str(), filesize));
}
//-------------------------------------------------------------------------------------------------
Directory::Directory( const AsciiString& dirPath ) : m_dirPath(dirPath)
{
WIN32_FIND_DATA item; // search item
HANDLE hFile; // handle for search resources
char currDir[ MAX_PATH ];
// sanity
if( m_dirPath.isEmpty() )
{
DEBUG_LOG(( "Empty dirname\n"));
return;
}
// save the current directory
GetCurrentDirectory( MAX_PATH, currDir );
// switch into the directory provided
if( SetCurrentDirectory( m_dirPath.str() ) == 0 )
{
DEBUG_LOG(( "Can't set directory '%s'\n", m_dirPath.str() ));
return;
}
// go through each item in the output directory
bool done = false;
hFile = FindFirstFile( "*", &item);
if( hFile == INVALID_HANDLE_VALUE )
{
DEBUG_LOG(( "Can't search directory '%s'\n", m_dirPath.str() ));
done = true;
}
FileInfo info;
while (!done)
{
// if this is a subdirectory keep the name around till the end
if( BitTest( item.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) )
{
if ( strcmp( item.cFileName, "." ) && strcmp( item.cFileName, ".." ) )
{
info.set(item);
m_subdirs.insert( info );
}
}
else
{
info.set(item);
m_files.insert( info );
}
if ( FindNextFile( hFile, &item ) == 0 )
{
done = true;
}
}
// close search
FindClose( hFile );
// restore the working directory to what it was when we started here
SetCurrentDirectory( currDir );
}
FileInfoSet* Directory::getFiles( void )
{
return &m_files;
}
FileInfoSet* Directory::getSubdirs( void )
{
return &m_subdirs;
}
#endif

View File

@@ -0,0 +1,60 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// DisabledTypes.cpp /////////////////////////////////////////////////////////////////////////////////////
// Kris Morness, September 2002
#include "PreRTS.h"
#include "Common/DisabledTypes.h"
#include "Common/BitFlagsIO.h"
const char* DisabledMaskType::s_bitNameList[] =
{
"DEFAULT",
"DISABLED_HACKED",
"DISABLED_EMP",
"DISABLED_HELD",
"DISABLED_PARALYZED",
"DISABLED_UNMANNED",
"DISABLED_UNDERPOWERED",
"DISABLED_FREEFALL",
"DISABLED_AWESTRUCK",
"DISABLED_BRAINWASHED",
"DISABLED_SUBDUED",
"DISABLED_SCRIPT_DISABLED",
"DISABLED_SCRIPT_UNDERPOWERED",
NULL
};
DisabledMaskType DISABLEDMASK_NONE; // inits to all zeroes
DisabledMaskType DISABLEDMASK_ALL;
void initDisabledMasks()
{
SET_ALL_DISABLEDMASK_BITS( DISABLEDMASK_ALL );
}

View File

@@ -0,0 +1,259 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO_
//
// File name: IO_File.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "Common/File.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// File::File
//=================================================================
File::File()
: m_open(FALSE),
m_deleteOnClose(FALSE),
m_access(NONE)
{
setName("<no file>");
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// File::~File
//=================================================================
File::~File()
{
close();
}
//=================================================================
// File::open
//=================================================================
/**
* Any derived open() members must first call File::open. If File::open
* succeeds but the derived class's open failes then make sure to call
* File::close() before returning.
*/
//=================================================================
Bool File::open( const Char *filename, Int access )
{
if( m_open )
{
return FALSE;
}
setName( filename );
if( (access & ( STREAMING | WRITE )) == ( STREAMING | WRITE ))
{
// illegal access
return FALSE;
}
if( (access & ( TEXT | BINARY)) == ( TEXT | BINARY ))
{
// illegal access
return FALSE;
}
if ( (access & (READ|WRITE)) == 0 )
{
access |= READ;
}
if ( !(access & (READ|APPEND)) )
{
access |= TRUNCATE;
}
if ( (access & (TEXT|BINARY)) == 0 )
{
access |= BINARY;
}
m_access = access;
m_open = TRUE;
return TRUE;
}
//=================================================================
// File::close
//=================================================================
/**
* Must call File::close() for each successful File::open() call.
*/
//=================================================================
void File::close( void )
{
if( m_open )
{
setName( "<no file>" );
m_open = FALSE;
if ( m_deleteOnClose )
{
this->deleteInstance(); // on special cases File object will delete itself when closing
}
}
}
//=================================================================
// File::size
//=================================================================
/**
* Default implementation of File::size. Derived classes can optimize
* this member function.
*/
//=================================================================
Int File::size( void )
{
Int pos = seek( 0, CURRENT );
Int size = seek( 0, END );
seek( pos, START );
return size < 0 ? 0 : size;
}
//============================================================================
// File::position
//============================================================================
Int File::position( void )
{
return seek(0, CURRENT);
}
//============================================================================
// File::print
//============================================================================
Bool File::print ( const Char *format, ...)
{
Char buffer[10*1024];
Int len;
if ( ! (m_access & TEXT ) )
{
return FALSE;
}
va_list args;
va_start( args, format ); /* Initialize variable arguments. */
len = vsprintf( buffer, format, args );
va_end( args );
if ( len >= sizeof(buffer) )
{
// Big Problem
assert( FALSE );
return FALSE;
}
return (write ( buffer, len ) == len);
}
Bool File::eof() {
return (position() == size());
}

View File

@@ -0,0 +1,332 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO
//
// File name: IO_FileSystem.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/CDManager.h"
#include "Common/GameAudio.h"
#include "Common/LocalFileSystem.h"
#include "Common/PerfTimer.h"
DECLARE_PERF_TIMER(FileSystem)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//===============================
// TheFileSystem
//===============================
/**
* This is the FileSystem's singleton class. All file access
* should be through TheFileSystem, unless code needs to use an explicit
* File or FileSystem derivative.
*
* Using TheFileSystem->open and File exclusively for file access, particularly
* in library or modular code, allows applications to transparently implement
* file access as they see fit. This is particularly important for code that
* needs to be shared between applications, such as games and tools.
*/
//===============================
FileSystem *TheFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// FileSystem::FileSystem
//============================================================================
FileSystem::FileSystem()
{
}
//============================================================================
// FileSystem::~FileSystem
//============================================================================
FileSystem::~FileSystem()
{
}
//============================================================================
// FileSystem::init
//============================================================================
void FileSystem::init( void )
{
TheLocalFileSystem->init();
TheArchiveFileSystem->init();
}
//============================================================================
// FileSystem::update
//============================================================================
void FileSystem::update( void )
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->update();
TheArchiveFileSystem->update();
}
//============================================================================
// FileSystem::reset
//============================================================================
void FileSystem::reset( void )
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->reset();
TheArchiveFileSystem->reset();
}
//============================================================================
// FileSystem::open
//============================================================================
File* FileSystem::openFile( const Char *filename, Int access )
{
USE_PERF_TIMER(FileSystem)
File *file = NULL;
if ( TheLocalFileSystem != NULL )
{
file = TheLocalFileSystem->openFile( filename, access );
}
if ( (TheArchiveFileSystem != NULL) && (file == NULL) )
{
file = TheArchiveFileSystem->openFile( filename );
}
return file;
}
//============================================================================
// FileSystem::doesFileExist
//============================================================================
Bool FileSystem::doesFileExist(const Char *filename) const
{
USE_PERF_TIMER(FileSystem)
unsigned key=TheNameKeyGenerator->nameToLowercaseKey(filename);
std::map<unsigned,bool>::iterator i=m_fileExist.find(key);
if (i!=m_fileExist.end())
return i->second;
if (TheLocalFileSystem->doesFileExist(filename))
{
m_fileExist[key]=true;
return TRUE;
}
if (TheArchiveFileSystem->doesFileExist(filename))
{
m_fileExist[key]=true;
return TRUE;
}
m_fileExist[key]=false;
return FALSE;
}
//============================================================================
// FileSystem::getFileListInDirectory
//============================================================================
void FileSystem::getFileListInDirectory(const AsciiString& directory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
{
USE_PERF_TIMER(FileSystem)
TheLocalFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
TheArchiveFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
}
//============================================================================
// FileSystem::getFileInfo
//============================================================================
Bool FileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
{
USE_PERF_TIMER(FileSystem)
if (fileInfo == NULL) {
return FALSE;
}
memset(fileInfo, 0, sizeof(fileInfo));
if (TheLocalFileSystem->getFileInfo(filename, fileInfo)) {
return TRUE;
}
if (TheArchiveFileSystem->getFileInfo(filename, fileInfo)) {
return TRUE;
}
return FALSE;
}
//============================================================================
// FileSystem::createDirectory
//============================================================================
Bool FileSystem::createDirectory(AsciiString directory)
{
USE_PERF_TIMER(FileSystem)
if (TheLocalFileSystem != NULL) {
return TheLocalFileSystem->createDirectory(directory);
}
return FALSE;
}
//============================================================================
// FileSystem::areMusicFilesOnCD
//============================================================================
Bool FileSystem::areMusicFilesOnCD()
{
if (!TheCDManager) {
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - No CD Manager; returning false\n"));
return FALSE;
}
AsciiString cdRoot;
Int dc = TheCDManager->driveCount();
for (Int i = 0; i < dc; ++i) {
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking drive %d\n", i));
CDDriveInterface *cdi = TheCDManager->getDrive(i);
if (!cdi) {
continue;
}
cdRoot = cdi->getPath();
if (!cdRoot.endsWith("\\"))
cdRoot.concat("\\");
cdRoot.concat("genseczh.big");
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking for %s\n", cdRoot.str()));
File *musicBig = TheLocalFileSystem->openFile(cdRoot.str());
if (musicBig)
{
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - found it!\n"));
musicBig->close();
return TRUE;
}
}
return FALSE;
}
//============================================================================
// FileSystem::loadMusicFilesFromCD
//============================================================================
void FileSystem::loadMusicFilesFromCD()
{
if (!TheCDManager) {
return;
}
AsciiString cdRoot;
Int dc = TheCDManager->driveCount();
for (Int i = 0; i < dc; ++i) {
CDDriveInterface *cdi = TheCDManager->getDrive(i);
if (!cdi) {
continue;
}
cdRoot = cdi->getPath();
if (TheArchiveFileSystem->loadBigFilesFromDirectory(cdRoot, MUSIC_BIG)) {
break;
}
}
}
//============================================================================
// FileSystem::unloadMusicFilesFromCD
//============================================================================
void FileSystem::unloadMusicFilesFromCD()
{
if (!(TheAudio && TheAudio->isMusicPlayingFromCD())) {
return;
}
TheArchiveFileSystem->closeArchiveFile( MUSIC_BIG );
}

View File

@@ -0,0 +1,717 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: FunctionLexicon.cpp //////////////////////////////////////////////////////////////////////
// Created: Colin Day, September 2001
// Desc: Collection of function pointers to help us in managing
// and assign callbacks
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/FunctionLexicon.h"
#include "GameClient/GameWindow.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GUICallbacks.h"
#include "GameClient/Gadget.h"
// Popup Ladder Select --------------------------------------------------------------------------
extern void PopupLadderSelectInit( WindowLayout *layout, void *userData );
extern WindowMsgHandledType PopupLadderSelectSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupLadderSelectInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupBuddyNotificationSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// WOL Buddy Overlay Right Click menu callbacks --------------------------------------------------------------
extern void RCGameDetailsMenuInit( WindowLayout *layout, void *userData );
extern WindowMsgHandledType RCGameDetailsMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Beacon control bar callback --------------------------------------------------------------
extern WindowMsgHandledType BeaconWindowInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Popup Replay Save Menu ----------------------------------------------------------------------------------
extern void PopupReplayInit( WindowLayout *layout, void *userData );
extern void PopupReplayUpdate( WindowLayout *layout, void *userData );
extern void PopupReplayShutdown( WindowLayout *layout, void *userData );
extern WindowMsgHandledType PopupReplaySystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
extern WindowMsgHandledType PopupReplayInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// Extended MessageBox ----------------------------------------------------------------------------------
extern WindowMsgHandledType ExtendedMessageBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
// game window draw table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinDrawTable[] =
{
{ NAMEKEY_INVALID, "IMECandidateMainDraw", IMECandidateMainDraw },
{ NAMEKEY_INVALID, "IMECandidateTextAreaDraw", IMECandidateTextAreaDraw },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window system table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinSystemTable[] =
{
{ NAMEKEY_INVALID, "PassSelectedButtonsToParentSystem", PassSelectedButtonsToParentSystem },
{ NAMEKEY_INVALID, "PassMessagesToParentSystem", PassMessagesToParentSystem },
{ NAMEKEY_INVALID, "GameWinDefaultSystem", GameWinDefaultSystem },
{ NAMEKEY_INVALID, "GadgetPushButtonSystem", GadgetPushButtonSystem },
{ NAMEKEY_INVALID, "GadgetCheckBoxSystem", GadgetCheckBoxSystem },
{ NAMEKEY_INVALID, "GadgetRadioButtonSystem", GadgetRadioButtonSystem },
{ NAMEKEY_INVALID, "GadgetTabControlSystem", GadgetTabControlSystem },
{ NAMEKEY_INVALID, "GadgetListBoxSystem", GadgetListBoxSystem },
{ NAMEKEY_INVALID, "GadgetComboBoxSystem", GadgetComboBoxSystem },
{ NAMEKEY_INVALID, "GadgetHorizontalSliderSystem", GadgetHorizontalSliderSystem },
{ NAMEKEY_INVALID, "GadgetVerticalSliderSystem", GadgetVerticalSliderSystem },
{ NAMEKEY_INVALID, "GadgetProgressBarSystem", GadgetProgressBarSystem },
{ NAMEKEY_INVALID, "GadgetStaticTextSystem", GadgetStaticTextSystem },
{ NAMEKEY_INVALID, "GadgetTextEntrySystem", GadgetTextEntrySystem },
{ NAMEKEY_INVALID, "MessageBoxSystem", MessageBoxSystem },
{ NAMEKEY_INVALID, "QuitMessageBoxSystem", QuitMessageBoxSystem },
{ NAMEKEY_INVALID, "ExtendedMessageBoxSystem", ExtendedMessageBoxSystem },
{ NAMEKEY_INVALID, "MOTDSystem", MOTDSystem },
{ NAMEKEY_INVALID, "MainMenuSystem", MainMenuSystem },
{ NAMEKEY_INVALID, "OptionsMenuSystem", OptionsMenuSystem },
{ NAMEKEY_INVALID, "SinglePlayerMenuSystem", SinglePlayerMenuSystem },
{ NAMEKEY_INVALID, "QuitMenuSystem", QuitMenuSystem },
{ NAMEKEY_INVALID, "MapSelectMenuSystem", MapSelectMenuSystem },
{ NAMEKEY_INVALID, "ReplayMenuSystem", ReplayMenuSystem },
{ NAMEKEY_INVALID, "CreditsMenuSystem", CreditsMenuSystem },
{ NAMEKEY_INVALID, "LanLobbyMenuSystem", LanLobbyMenuSystem },
{ NAMEKEY_INVALID, "LanGameOptionsMenuSystem", LanGameOptionsMenuSystem },
{ NAMEKEY_INVALID, "LanMapSelectMenuSystem", LanMapSelectMenuSystem },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuSystem", SkirmishGameOptionsMenuSystem },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuSystem", SkirmishMapSelectMenuSystem },
{ NAMEKEY_INVALID, "ChallengeMenuSystem", ChallengeMenuSystem },
{ NAMEKEY_INVALID, "SaveLoadMenuSystem", SaveLoadMenuSystem },
{ NAMEKEY_INVALID, "PopupCommunicatorSystem", PopupCommunicatorSystem },
{ NAMEKEY_INVALID, "PopupBuddyNotificationSystem", PopupBuddyNotificationSystem },
{ NAMEKEY_INVALID, "PopupReplaySystem", PopupReplaySystem },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuSystem", KeyboardOptionsMenuSystem },
{ NAMEKEY_INVALID, "WOLLadderScreenSystem", WOLLadderScreenSystem },
{ NAMEKEY_INVALID, "WOLLoginMenuSystem", WOLLoginMenuSystem },
{ NAMEKEY_INVALID, "WOLLocaleSelectSystem", WOLLocaleSelectSystem },
{ NAMEKEY_INVALID, "WOLLobbyMenuSystem", WOLLobbyMenuSystem },
{ NAMEKEY_INVALID, "WOLGameSetupMenuSystem", WOLGameSetupMenuSystem },
{ NAMEKEY_INVALID, "WOLMapSelectMenuSystem", WOLMapSelectMenuSystem },
{ NAMEKEY_INVALID, "WOLBuddyOverlaySystem", WOLBuddyOverlaySystem },
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuSystem", WOLBuddyOverlayRCMenuSystem },
{ NAMEKEY_INVALID, "RCGameDetailsMenuSystem", RCGameDetailsMenuSystem },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlaySystem",GameSpyPlayerInfoOverlaySystem },
{ NAMEKEY_INVALID, "WOLMessageWindowSystem", WOLMessageWindowSystem },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuSystem", WOLQuickMatchMenuSystem },
{ NAMEKEY_INVALID, "WOLWelcomeMenuSystem", WOLWelcomeMenuSystem },
{ NAMEKEY_INVALID, "WOLStatusMenuSystem", WOLStatusMenuSystem },
{ NAMEKEY_INVALID, "WOLQMScoreScreenSystem", WOLQMScoreScreenSystem },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenSystem", WOLCustomScoreScreenSystem },
{ NAMEKEY_INVALID, "NetworkDirectConnectSystem", NetworkDirectConnectSystem },
{ NAMEKEY_INVALID, "PopupHostGameSystem", PopupHostGameSystem },
{ NAMEKEY_INVALID, "PopupJoinGameSystem", PopupJoinGameSystem },
{ NAMEKEY_INVALID, "PopupLadderSelectSystem", PopupLadderSelectSystem },
{ NAMEKEY_INVALID, "InGamePopupMessageSystem", InGamePopupMessageSystem },
{ NAMEKEY_INVALID, "ControlBarSystem", ControlBarSystem },
{ NAMEKEY_INVALID, "ControlBarObserverSystem", ControlBarObserverSystem },
{ NAMEKEY_INVALID, "IMECandidateWindowSystem", IMECandidateWindowSystem },
{ NAMEKEY_INVALID, "ReplayControlSystem", ReplayControlSystem },
{ NAMEKEY_INVALID, "InGameChatSystem", InGameChatSystem },
{ NAMEKEY_INVALID, "DisconnectControlSystem", DisconnectControlSystem },
{ NAMEKEY_INVALID, "DiplomacySystem", DiplomacySystem },
{ NAMEKEY_INVALID, "GeneralsExpPointsSystem", GeneralsExpPointsSystem },
{ NAMEKEY_INVALID, "DifficultySelectSystem", DifficultySelectSystem },
{ NAMEKEY_INVALID, "IdleWorkerSystem", IdleWorkerSystem },
{ NAMEKEY_INVALID, "EstablishConnectionsControlSystem", EstablishConnectionsControlSystem },
{ NAMEKEY_INVALID, "GameInfoWindowSystem", GameInfoWindowSystem },
{ NAMEKEY_INVALID, "ScoreScreenSystem", ScoreScreenSystem },
{ NAMEKEY_INVALID, "DownloadMenuSystem", DownloadMenuSystem },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window input table ------------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinInputTable[] =
{
{ NAMEKEY_INVALID, "GameWinDefaultInput", GameWinDefaultInput },
{ NAMEKEY_INVALID, "GameWinBlockInput", GameWinBlockInput },
{ NAMEKEY_INVALID, "GadgetPushButtonInput", GadgetPushButtonInput },
{ NAMEKEY_INVALID, "GadgetCheckBoxInput", GadgetCheckBoxInput },
{ NAMEKEY_INVALID, "GadgetRadioButtonInput", GadgetRadioButtonInput },
{ NAMEKEY_INVALID, "GadgetTabControlInput", GadgetTabControlInput },
{ NAMEKEY_INVALID, "GadgetListBoxInput", GadgetListBoxInput },
{ NAMEKEY_INVALID, "GadgetListBoxMultiInput", GadgetListBoxMultiInput },
{ NAMEKEY_INVALID, "GadgetComboBoxInput", GadgetComboBoxInput },
{ NAMEKEY_INVALID, "GadgetHorizontalSliderInput", GadgetHorizontalSliderInput },
{ NAMEKEY_INVALID, "GadgetVerticalSliderInput", GadgetVerticalSliderInput },
{ NAMEKEY_INVALID, "GadgetStaticTextInput", GadgetStaticTextInput },
{ NAMEKEY_INVALID, "GadgetTextEntryInput", GadgetTextEntryInput },
{ NAMEKEY_INVALID, "MainMenuInput", MainMenuInput },
{ NAMEKEY_INVALID, "MapSelectMenuInput", MapSelectMenuInput },
{ NAMEKEY_INVALID, "OptionsMenuInput", OptionsMenuInput },
{ NAMEKEY_INVALID, "SinglePlayerMenuInput", SinglePlayerMenuInput },
{ NAMEKEY_INVALID, "LanLobbyMenuInput", LanLobbyMenuInput },
{ NAMEKEY_INVALID, "ReplayMenuInput", ReplayMenuInput },
{ NAMEKEY_INVALID, "CreditsMenuInput", CreditsMenuInput },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInput", KeyboardOptionsMenuInput },
{ NAMEKEY_INVALID, "PopupCommunicatorInput", PopupCommunicatorInput },
{ NAMEKEY_INVALID, "LanGameOptionsMenuInput", LanGameOptionsMenuInput },
{ NAMEKEY_INVALID, "LanMapSelectMenuInput", LanMapSelectMenuInput },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInput", SkirmishGameOptionsMenuInput },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInput", SkirmishMapSelectMenuInput },
{ NAMEKEY_INVALID, "ChallengeMenuInput", ChallengeMenuInput },
{ NAMEKEY_INVALID, "WOLLadderScreenInput", WOLLadderScreenInput },
{ NAMEKEY_INVALID, "WOLLoginMenuInput", WOLLoginMenuInput },
{ NAMEKEY_INVALID, "WOLLocaleSelectInput", WOLLocaleSelectInput },
{ NAMEKEY_INVALID, "WOLLobbyMenuInput", WOLLobbyMenuInput },
{ NAMEKEY_INVALID, "WOLGameSetupMenuInput", WOLGameSetupMenuInput },
{ NAMEKEY_INVALID, "WOLMapSelectMenuInput", WOLMapSelectMenuInput },
{ NAMEKEY_INVALID, "WOLBuddyOverlayInput", WOLBuddyOverlayInput },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInput", GameSpyPlayerInfoOverlayInput },
{ NAMEKEY_INVALID, "WOLMessageWindowInput", WOLMessageWindowInput },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInput", WOLQuickMatchMenuInput },
{ NAMEKEY_INVALID, "WOLWelcomeMenuInput", WOLWelcomeMenuInput },
{ NAMEKEY_INVALID, "WOLStatusMenuInput", WOLStatusMenuInput },
{ NAMEKEY_INVALID, "WOLQMScoreScreenInput", WOLQMScoreScreenInput },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInput", WOLCustomScoreScreenInput },
{ NAMEKEY_INVALID, "NetworkDirectConnectInput", NetworkDirectConnectInput },
{ NAMEKEY_INVALID, "PopupHostGameInput", PopupHostGameInput },
{ NAMEKEY_INVALID, "PopupJoinGameInput", PopupJoinGameInput },
{ NAMEKEY_INVALID, "PopupLadderSelectInput", PopupLadderSelectInput },
{ NAMEKEY_INVALID, "InGamePopupMessageInput", InGamePopupMessageInput },
{ NAMEKEY_INVALID, "ControlBarInput", ControlBarInput },
{ NAMEKEY_INVALID, "ReplayControlInput", ReplayControlInput },
{ NAMEKEY_INVALID, "InGameChatInput", InGameChatInput },
{ NAMEKEY_INVALID, "DisconnectControlInput", DisconnectControlInput },
{ NAMEKEY_INVALID, "DiplomacyInput", DiplomacyInput },
{ NAMEKEY_INVALID, "EstablishConnectionsControlInput", EstablishConnectionsControlInput },
{ NAMEKEY_INVALID, "LeftHUDInput", LeftHUDInput },
{ NAMEKEY_INVALID, "ScoreScreenInput", ScoreScreenInput },
{ NAMEKEY_INVALID, "SaveLoadMenuInput", SaveLoadMenuInput },
{ NAMEKEY_INVALID, "BeaconWindowInput", BeaconWindowInput },
{ NAMEKEY_INVALID, "DifficultySelectInput", DifficultySelectInput },
{ NAMEKEY_INVALID, "PopupReplayInput", PopupReplayInput },
{ NAMEKEY_INVALID, "GeneralsExpPointsInput", GeneralsExpPointsInput},
{ NAMEKEY_INVALID, "DownloadMenuInput", DownloadMenuInput },
{ NAMEKEY_INVALID, "IMECandidateWindowInput", IMECandidateWindowInput },
{ NAMEKEY_INVALID, NULL, NULL }
};
// game window tooltip table ----------------------------------------------------------------------
static FunctionLexicon::TableEntry gameWinTooltipTable[] =
{
{ NAMEKEY_INVALID, "GameWinDefaultTooltip", GameWinDefaultTooltip },
{ NAMEKEY_INVALID, NULL, NULL }
};
// window layout init table -----------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutInitTable[] =
{
{ NAMEKEY_INVALID, "MainMenuInit", MainMenuInit },
{ NAMEKEY_INVALID, "OptionsMenuInit", OptionsMenuInit },
{ NAMEKEY_INVALID, "SaveLoadMenuInit", SaveLoadMenuInit },
{ NAMEKEY_INVALID, "SaveLoadMenuFullScreenInit", SaveLoadMenuFullScreenInit },
{ NAMEKEY_INVALID, "PopupCommunicatorInit", PopupCommunicatorInit },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInit", KeyboardOptionsMenuInit },
{ NAMEKEY_INVALID, "SinglePlayerMenuInit", SinglePlayerMenuInit },
{ NAMEKEY_INVALID, "MapSelectMenuInit", MapSelectMenuInit },
{ NAMEKEY_INVALID, "LanLobbyMenuInit", LanLobbyMenuInit },
{ NAMEKEY_INVALID, "ReplayMenuInit", ReplayMenuInit },
{ NAMEKEY_INVALID, "CreditsMenuInit", CreditsMenuInit },
{ NAMEKEY_INVALID, "LanGameOptionsMenuInit", LanGameOptionsMenuInit },
{ NAMEKEY_INVALID, "LanMapSelectMenuInit", LanMapSelectMenuInit },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInit", SkirmishGameOptionsMenuInit },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInit", SkirmishMapSelectMenuInit },
{ NAMEKEY_INVALID, "ChallengeMenuInit", ChallengeMenuInit },
{ NAMEKEY_INVALID, "WOLLadderScreenInit", WOLLadderScreenInit },
{ NAMEKEY_INVALID, "WOLLoginMenuInit", WOLLoginMenuInit },
{ NAMEKEY_INVALID, "WOLLocaleSelectInit", WOLLocaleSelectInit },
{ NAMEKEY_INVALID, "WOLLobbyMenuInit", WOLLobbyMenuInit },
{ NAMEKEY_INVALID, "WOLGameSetupMenuInit", WOLGameSetupMenuInit },
{ NAMEKEY_INVALID, "WOLMapSelectMenuInit", WOLMapSelectMenuInit },
{ NAMEKEY_INVALID, "WOLBuddyOverlayInit", WOLBuddyOverlayInit },
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuInit", WOLBuddyOverlayRCMenuInit },
{ NAMEKEY_INVALID, "RCGameDetailsMenuInit", RCGameDetailsMenuInit },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInit", GameSpyPlayerInfoOverlayInit },
{ NAMEKEY_INVALID, "WOLMessageWindowInit", WOLMessageWindowInit },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInit", WOLQuickMatchMenuInit },
{ NAMEKEY_INVALID, "WOLWelcomeMenuInit", WOLWelcomeMenuInit },
{ NAMEKEY_INVALID, "WOLStatusMenuInit", WOLStatusMenuInit },
{ NAMEKEY_INVALID, "WOLQMScoreScreenInit", WOLQMScoreScreenInit },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInit", WOLCustomScoreScreenInit },
{ NAMEKEY_INVALID, "NetworkDirectConnectInit", NetworkDirectConnectInit },
{ NAMEKEY_INVALID, "PopupHostGameInit", PopupHostGameInit },
{ NAMEKEY_INVALID, "PopupJoinGameInit", PopupJoinGameInit },
{ NAMEKEY_INVALID, "PopupLadderSelectInit", PopupLadderSelectInit },
{ NAMEKEY_INVALID, "InGamePopupMessageInit", InGamePopupMessageInit },
{ NAMEKEY_INVALID, "GameInfoWindowInit", GameInfoWindowInit },
{ NAMEKEY_INVALID, "ScoreScreenInit", ScoreScreenInit },
{ NAMEKEY_INVALID, "DownloadMenuInit", DownloadMenuInit },
{ NAMEKEY_INVALID, "DifficultySelectInit", DifficultySelectInit },
{ NAMEKEY_INVALID, "PopupReplayInit", PopupReplayInit },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
// window layout update table ---------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutUpdateTable[] =
{
{ NAMEKEY_INVALID, "MainMenuUpdate", MainMenuUpdate },
{ NAMEKEY_INVALID, "OptionsMenuUpdate", OptionsMenuUpdate },
{ NAMEKEY_INVALID, "SinglePlayerMenuUpdate", SinglePlayerMenuUpdate },
{ NAMEKEY_INVALID, "MapSelectMenuUpdate", MapSelectMenuUpdate },
{ NAMEKEY_INVALID, "LanLobbyMenuUpdate", LanLobbyMenuUpdate },
{ NAMEKEY_INVALID, "ReplayMenuUpdate", ReplayMenuUpdate },
{ NAMEKEY_INVALID, "SaveLoadMenuUpdate", SaveLoadMenuUpdate },
{ NAMEKEY_INVALID, "CreditsMenuUpdate", CreditsMenuUpdate },
{ NAMEKEY_INVALID, "LanGameOptionsMenuUpdate", LanGameOptionsMenuUpdate },
{ NAMEKEY_INVALID, "LanMapSelectMenuUpdate", LanMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuUpdate", SkirmishGameOptionsMenuUpdate },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuUpdate", SkirmishMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "ChallengeMenuUpdate", ChallengeMenuUpdate },
{ NAMEKEY_INVALID, "WOLLadderScreenUpdate", WOLLadderScreenUpdate },
{ NAMEKEY_INVALID, "WOLLoginMenuUpdate", WOLLoginMenuUpdate },
{ NAMEKEY_INVALID, "WOLLocaleSelectUpdate", WOLLocaleSelectUpdate },
{ NAMEKEY_INVALID, "WOLLobbyMenuUpdate", WOLLobbyMenuUpdate },
{ NAMEKEY_INVALID, "WOLGameSetupMenuUpdate", WOLGameSetupMenuUpdate },
{ NAMEKEY_INVALID, "PopupHostGameUpdate", PopupHostGameUpdate },
{ NAMEKEY_INVALID, "WOLMapSelectMenuUpdate", WOLMapSelectMenuUpdate },
{ NAMEKEY_INVALID, "WOLBuddyOverlayUpdate", WOLBuddyOverlayUpdate },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayUpdate",GameSpyPlayerInfoOverlayUpdate },
{ NAMEKEY_INVALID, "WOLMessageWindowUpdate", WOLMessageWindowUpdate },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuUpdate", WOLQuickMatchMenuUpdate },
{ NAMEKEY_INVALID, "WOLWelcomeMenuUpdate", WOLWelcomeMenuUpdate },
{ NAMEKEY_INVALID, "WOLStatusMenuUpdate", WOLStatusMenuUpdate },
{ NAMEKEY_INVALID, "WOLQMScoreScreenUpdate", WOLQMScoreScreenUpdate },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenUpdate", WOLCustomScoreScreenUpdate },
{ NAMEKEY_INVALID, "NetworkDirectConnectUpdate", NetworkDirectConnectUpdate },
{ NAMEKEY_INVALID, "ScoreScreenUpdate", ScoreScreenUpdate },
{ NAMEKEY_INVALID, "DownloadMenuUpdate", DownloadMenuUpdate },
{ NAMEKEY_INVALID, "PopupReplayUpdate", PopupReplayUpdate },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
// window layout shutdown table -------------------------------------------------------------------
static FunctionLexicon::TableEntry winLayoutShutdownTable[] =
{
{ NAMEKEY_INVALID, "MainMenuShutdown", MainMenuShutdown },
{ NAMEKEY_INVALID, "OptionsMenuShutdown", OptionsMenuShutdown },
{ NAMEKEY_INVALID, "SaveLoadMenuShutdown", SaveLoadMenuShutdown },
{ NAMEKEY_INVALID, "PopupCommunicatorShutdown", PopupCommunicatorShutdown },
{ NAMEKEY_INVALID, "KeyboardOptionsMenuShutdown", KeyboardOptionsMenuShutdown },
{ NAMEKEY_INVALID, "SinglePlayerMenuShutdown", SinglePlayerMenuShutdown },
{ NAMEKEY_INVALID, "MapSelectMenuShutdown", MapSelectMenuShutdown },
{ NAMEKEY_INVALID, "LanLobbyMenuShutdown", LanLobbyMenuShutdown },
{ NAMEKEY_INVALID, "ReplayMenuShutdown", ReplayMenuShutdown },
{ NAMEKEY_INVALID, "CreditsMenuShutdown", CreditsMenuShutdown },
{ NAMEKEY_INVALID, "LanGameOptionsMenuShutdown", LanGameOptionsMenuShutdown },
{ NAMEKEY_INVALID, "LanMapSelectMenuShutdown", LanMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuShutdown",SkirmishGameOptionsMenuShutdown },
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuShutdown", SkirmishMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "ChallengeMenuShutdown", ChallengeMenuShutdown },
{ NAMEKEY_INVALID, "WOLLadderScreenShutdown", WOLLadderScreenShutdown },
{ NAMEKEY_INVALID, "WOLLoginMenuShutdown", WOLLoginMenuShutdown },
{ NAMEKEY_INVALID, "WOLLocaleSelectShutdown", WOLLocaleSelectShutdown },
{ NAMEKEY_INVALID, "WOLLobbyMenuShutdown", WOLLobbyMenuShutdown },
{ NAMEKEY_INVALID, "WOLGameSetupMenuShutdown", WOLGameSetupMenuShutdown },
{ NAMEKEY_INVALID, "WOLMapSelectMenuShutdown", WOLMapSelectMenuShutdown },
{ NAMEKEY_INVALID, "WOLBuddyOverlayShutdown", WOLBuddyOverlayShutdown },
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayShutdown",GameSpyPlayerInfoOverlayShutdown },
{ NAMEKEY_INVALID, "WOLMessageWindowShutdown", WOLMessageWindowShutdown },
{ NAMEKEY_INVALID, "WOLQuickMatchMenuShutdown", WOLQuickMatchMenuShutdown },
{ NAMEKEY_INVALID, "WOLWelcomeMenuShutdown", WOLWelcomeMenuShutdown },
{ NAMEKEY_INVALID, "WOLStatusMenuShutdown", WOLStatusMenuShutdown },
{ NAMEKEY_INVALID, "WOLQMScoreScreenShutdown", WOLQMScoreScreenShutdown },
{ NAMEKEY_INVALID, "WOLCustomScoreScreenShutdown", WOLCustomScoreScreenShutdown },
{ NAMEKEY_INVALID, "NetworkDirectConnectShutdown", NetworkDirectConnectShutdown },
{ NAMEKEY_INVALID, "ScoreScreenShutdown", ScoreScreenShutdown },
{ NAMEKEY_INVALID, "DownloadMenuShutdown", DownloadMenuShutdown },
{ NAMEKEY_INVALID, "PopupReplayShutdown", PopupReplayShutdown },
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC DATA
///////////////////////////////////////////////////////////////////////////////////////////////////
FunctionLexicon *TheFunctionLexicon = NULL; ///< the function dictionary
//-------------------------------------------------------------------------------------------------
/** Since we have a convenient table to organize our callbacks anyway,
* we'll just use this same storage space to load in any run time
* components we might want to add to the table, such as generating
* a key based off the name supplied in the table for faster access */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::loadTable( TableEntry *table,
TableIndex tableIndex )
{
// sanity
if( table == NULL )
return;
// loop through all entries
TableEntry *entry = table;
while( entry->name )
{
// assign key from name key based on name provided in table
entry->key = TheNameKeyGenerator->nameToKey( AsciiString(entry->name) );
// next table entry please
entry++;
} // end while
// assign table to the index specified
m_tables[ tableIndex ] = table;
} // end loadTable
//-------------------------------------------------------------------------------------------------
/** Search the provided table for a function matching the key */
//-------------------------------------------------------------------------------------------------
void *FunctionLexicon::keyToFunc( NameKeyType key, TableEntry *table )
{
// sanity
if( key == NAMEKEY_INVALID )
return NULL;
// search table for key
TableEntry *entry = table;
while( entry && entry->key != NAMEKEY_INVALID )
{
if( entry->key == key )
return entry->func;
entry++;
} // end if
return NULL; // not found
} // end keyToFunc
//-------------------------------------------------------------------------------------------------
/** Search tables for the function given this key, if the index parameter
* is TABLE_ANY, then ALL tables will be searched. Otherwise index refers
* to only a single table index to be searched */
//-------------------------------------------------------------------------------------------------
void *FunctionLexicon::findFunction( NameKeyType key, TableIndex index )
{
void *func = NULL;
// sanity
if( key == NAMEKEY_INVALID )
return NULL;
// search ALL tables for function if the index paramater allows if
if( index == TABLE_ANY )
{
Int i;
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
func = keyToFunc( key, m_tables[ i ] );
if( func )
break; // exit for i
} // end for i
} // end if
else
{
// do NOT search all tables, just the one specified by the parameter
func = keyToFunc( key, m_tables[ index ] );
} // end else
// return function, if found
return func;
} // end findFunction
#ifdef NOT_IN_USE
//-------------------------------------------------------------------------------------------------
/** Search for the function in the specified table */
//-------------------------------------------------------------------------------------------------
const char *FunctionLexicon::funcToName( void *func, TableEntry *table )
{
// sanity
if( func == NULL )
return NULL;
// search the table
TableEntry *entry = table;
while( entry && entry->key != NAMEKEY_INVALID )
{
// is this it
if( entry->func == func )
return entry->name;
// not it, check next
entry++;
} // end while
return NULL; // not found
} // end funcToName
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
FunctionLexicon::FunctionLexicon( void )
{
Int i;
// empty the tables
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
m_tables[ i ] = NULL;
} // end FunctionLexicon
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
FunctionLexicon::~FunctionLexicon( void )
{
} // end ~FunctionLexicon
//-------------------------------------------------------------------------------------------------
/** Initialize our dictionary of funtion pointers and symbols */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::init( void )
{
// if you change this method, check the reset() and make sure it's OK
// initialize the name key identifiers for the lookup table
loadTable( gameWinDrawTable, TABLE_GAME_WIN_DRAW );
loadTable( gameWinSystemTable, TABLE_GAME_WIN_SYSTEM );
loadTable( gameWinInputTable, TABLE_GAME_WIN_INPUT );
loadTable( gameWinTooltipTable, TABLE_GAME_WIN_TOOLTIP );
loadTable( winLayoutInitTable, TABLE_WIN_LAYOUT_INIT );
loadTable( winLayoutUpdateTable, TABLE_WIN_LAYOUT_UPDATE );
loadTable( winLayoutShutdownTable, TABLE_WIN_LAYOUT_SHUTDOWN );
validate();
} // end init
//-------------------------------------------------------------------------------------------------
/** reset */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::reset( void )
{
//
// make sure the ordering of what happens here with respect to derived classes resets is
// all OK since we're cheating and using the init() method
//
// nothing dynamically loaded, just reinit the tables
init();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Update */
//-------------------------------------------------------------------------------------------------
void FunctionLexicon::update( void )
{
} // end update
/*
// !NOTE! We can not have this function, see the header for
// more information as to why
//
//-------------------------------------------------------------------------------------------------
// translate a function pointer to its symbolic name
//-------------------------------------------------------------------------------------------------
char *FunctionLexicon::functionToName( void *func )
{
// sanity
if( func == NULL )
return NULL;
// search ALL the tables
Int i;
char *name = NULL;
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
name = funcToName( func, m_tables[ i ] );
if( name )
return name;
} // end for i
return NULL; // not found
} // end functionToName
*/
//-------------------------------------------------------------------------------------------------
/** Scan the tables and make sure that each function address is unique.
* We want to do this to prevent accidental entries of two identical
* functions and because the compiler will optimize identical functions
* to the same address (typically in empty functions with no body and the
* same parameters) which we MUST keep separate for when we add code to
* them */
//-------------------------------------------------------------------------------------------------
Bool FunctionLexicon::validate( void )
{
Bool valid = TRUE;
Int i, j;
TableEntry *sourceEntry, *lookAtEntry;
// scan all talbes
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
{
// scan through this table
sourceEntry = m_tables[ i ];
while( sourceEntry && sourceEntry->key != NAMEKEY_INVALID )
{
//
// scan all tables looking for the function in sourceEntry, do not bother
// of source entry is NULL (a valid entry in the table, but not a function)
//
if( sourceEntry->func )
{
// scan all tables
for( j = 0; j < MAX_FUNCTION_TABLES; j++ )
{
// scan all entries in this table
lookAtEntry = m_tables[ j ];
while( lookAtEntry && lookAtEntry->key != NAMEKEY_INVALID )
{
//
// check for match, do not match the entry source with itself
//
if( sourceEntry != lookAtEntry )
if( sourceEntry->func == lookAtEntry->func )
{
DEBUG_LOG(( "WARNING! Function lexicon entries match same address! '%s' and '%s'\n",
sourceEntry->name, lookAtEntry->name ));
valid = FALSE;
} // end if
// next entry in this target table
lookAtEntry++;
} // end while
} // end for j
} // end if
// next source entry
sourceEntry++;
} // end while
} // end for i
// return the valid state of our tables
return valid;
} // end validate
//============================================================================
// FunctionLexicon::gameWinDrawFunc
//============================================================================
GameWinDrawFunc FunctionLexicon::gameWinDrawFunc( NameKeyType key, TableIndex index )
{
if ( index == TABLE_ANY )
{
// first search the device depended table then the device independent table
GameWinDrawFunc func;
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DEVICEDRAW );
if ( func == NULL )
{
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DRAW );
}
return func;
}
// search the specified table
return (GameWinDrawFunc)findFunction( key, index );
}
WindowLayoutInitFunc FunctionLexicon::winLayoutInitFunc( NameKeyType key, TableIndex index )
{
if ( index == TABLE_ANY )
{
// first search the device depended table then the device independent table
WindowLayoutInitFunc func;
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_DEVICEINIT );
if ( func == NULL )
{
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_INIT );
}
return func;
}
// search the specified table
return (WindowLayoutInitFunc)findFunction( key, index );
}

View File

@@ -0,0 +1,66 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameCommon.h
// Part of header detangling
// John McDonald, Aug 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameCommon.h"
const char *TheVeterancyNames[] =
{
"REGULAR",
"VETERAN",
"ELITE",
"HEROIC",
NULL
};
const char *TheRelationshipNames[] =
{
"ENEMIES",
"NEUTRAL",
"ALLIES",
NULL
};
//-------------------------------------------------------------------------------------------------
Real normalizeAngle(Real angle)
{
DEBUG_ASSERTCRASH(!_isnan(angle), ("Angle is NAN in normalizeAngle!\n"));
if( _isnan(angle) )
return 0;// ARGH!!!! Don't assert and then not handle it! Error bad! Fix error!
while (angle > PI)
angle -= 2*PI;
while (angle <= -PI)
angle += 2*PI;
return angle;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameType.cpp ///////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
char *TimeOfDayNames[] =
{
"NONE",
"MORNING",
"AFTERNOON",
"EVENING",
"NIGHT",
NULL
};
char *WeatherNames[] =
{
"NORMAL",
"SNOWY",
NULL
};

View File

@@ -0,0 +1,605 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Geometry.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, Aug 2002
// Desc:
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_GEOMETRY_NAMES
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "Common/Geometry.h"
#include "Common/INI.h"
#include "Common/RandomValue.h"
#include "Common/Xfer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryType( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_type = (GeometryType)INI::scanIndexList(ini->getNextToken(), GeometryNames);
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryIsSmall( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_isSmall = INI::scanBool(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryHeight( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_height = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryMajorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_majorRadius = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//=============================================================================
/*static*/ void GeometryInfo::parseGeometryMinorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
{
((GeometryInfo*)store)->m_minorRadius = INI::scanReal(ini->getNextToken());
((GeometryInfo*)store)->calcBoundingStuff();
}
//-----------------------------------------------------------------------------
void GeometryInfo::set(GeometryType type, Bool isSmall, Real height, Real majorRadius, Real minorRadius)
{
m_type = type;
m_isSmall = isSmall;
switch(m_type)
{
case GEOMETRY_SPHERE:
m_majorRadius = majorRadius;
m_minorRadius = majorRadius;
m_height = majorRadius;
break;
case GEOMETRY_CYLINDER:
m_majorRadius = majorRadius;
m_minorRadius = majorRadius;
m_height = height;
break;
case GEOMETRY_BOX:
m_majorRadius = majorRadius;
m_minorRadius = minorRadius;
m_height = height;
break;
};
calcBoundingStuff();
}
//-----------------------------------------------------------------------------
static Real calcDotProduct(const Coord3D& a, const Coord3D& b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
//-----------------------------------------------------------------------------
static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
{
return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
}
//-----------------------------------------------------------------------------
static Real calcPointToLineDistSquared(const Coord3D& pt, const Coord3D& lineStart, const Coord3D& lineEnd)
{
Coord3D line, lineToPt, closest;
line.x = lineEnd.x - lineStart.x;
line.y = lineEnd.y - lineStart.y;
line.z = lineEnd.z - lineStart.z;
lineToPt.x = pt.x - lineStart.x;
lineToPt.y = pt.y - lineStart.y;
lineToPt.z = pt.z - lineStart.z;
Real dot = calcDotProduct(lineToPt, line);
if (dot <= 0.0f)
{
// angle is obtuse
return calcDistSquared(pt, lineStart);
}
Real lineLenSqr = calcDistSquared(lineStart, lineEnd);
DEBUG_ASSERTCRASH(lineLenSqr==calcDotProduct(line,line),("hmm"));
if (lineLenSqr <= dot)
{
return calcDistSquared(pt, lineEnd);
}
Real tmp = dot / lineLenSqr;
closest.x = lineStart.x + tmp * line.x;
closest.y = lineStart.y + tmp * line.y;
closest.z = lineStart.z + tmp * line.z;
return calcDistSquared(pt, closest);
}
//=============================================================================
Bool GeometryInfo::isIntersectedByLineSegment(const Coord3D& loc, const Coord3D& from, const Coord3D& to) const
{
DEBUG_CRASH(("this call does not work properly for nonspheres yet. use with caution."));
/// @todo srj -- treats everything as a sphere for now. fix.
Real distSquared = calcPointToLineDistSquared(loc, from, to);
return distSquared <= sqr(getBoundingSphereRadius());
}
//=============================================================================
// given an object with this geom, located at 'pos', and another obj with the given
// pos & geom, calc the min and max pitches from this to that.
void GeometryInfo::calcPitches(const Coord3D& thisPos, const GeometryInfo& that, const Coord3D& thatPos,
Real& minPitch, Real& maxPitch) const
{
Coord3D thisCenter;
getCenterPosition(thisPos, thisCenter);
Real dxy = sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
Real dz;
/** @todo srj -- this could be better, by calcing it for all the corners, not just top-center
and bottom-center... oh well */
dz = (thatPos.z + that.getMaxHeightAbovePosition()) - thisCenter.z;
maxPitch = atan2(dz, dxy);
dz = (thatPos.z - that.getMaxHeightBelowPosition()) - thisCenter.z;
minPitch = atan2(dz, dxy);
}
//=============================================================================
// given an object with this geom, SET how far above the object's canonical position its max z should extend.
void GeometryInfo::setMaxHeightAbovePosition(Real z)
{
switch(m_type)
{
case GEOMETRY_SPHERE:
m_majorRadius = z;
break;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
m_height = z;
break;
};
calcBoundingStuff();
}
//=============================================================================
// given an object with this geom, how far above the object's canonical position does its max z extend?
Real GeometryInfo::getMaxHeightAbovePosition() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
return m_majorRadius;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
return m_height;
};
return 0.0f;
}
//=============================================================================
// given an object with this geom, how far below the object's canonical position does its max z extend?
Real GeometryInfo::getMaxHeightBelowPosition() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
return m_majorRadius;
case GEOMETRY_BOX:
case GEOMETRY_CYLINDER:
return 0.0f;
};
return 0.0f;
}
//=============================================================================
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
Real GeometryInfo::getZDeltaToCenterPosition() const
{
return (m_type == GEOMETRY_SPHERE) ? 0.0f : (m_height * 0.5f);
}
//=============================================================================
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
void GeometryInfo::getCenterPosition(const Coord3D& pos, Coord3D& center) const
{
center = pos;
center.z += getZDeltaToCenterPosition();
}
//=============================================================================
void GeometryInfo::expandFootprint(Real radius)
{
m_majorRadius += radius;
m_minorRadius += radius;
calcBoundingStuff();
}
//=============================================================================
void GeometryInfo::get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
bounds.lo.x = geomCenter.x - m_majorRadius;
bounds.lo.y = geomCenter.y - m_majorRadius;
bounds.hi.x = geomCenter.x + m_majorRadius;
bounds.hi.y = geomCenter.y + m_majorRadius;
break;
}
case GEOMETRY_BOX:
{
Real c = (Real)cos(angle);
Real s = (Real)sin(angle);
Real exc = m_majorRadius*c;
Real eyc = m_minorRadius*c;
Real exs = m_majorRadius*s;
Real eys = m_minorRadius*s;
Real x,y;
x = geomCenter.x - exc - eys;
y = geomCenter.y + eyc - exs;
bounds.lo.x = x;
bounds.lo.y = y;
bounds.hi.x = x;
bounds.hi.y = y;
x = geomCenter.x + exc - eys;
y = geomCenter.y + eyc + exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
x = geomCenter.x + exc + eys;
y = geomCenter.y - eyc + exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
x = geomCenter.x - exc + eys;
y = geomCenter.y - eyc - exs;
if (bounds.lo.x > x) bounds.lo.x = x;
if (bounds.lo.y > y) bounds.lo.y = y;
if (bounds.hi.x < x) bounds.hi.x = x;
if (bounds.hi.y < y) bounds.hi.y = y;
break;
}
}
}
//=============================================================================
void GeometryInfo::clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
Real dx = ptToClip.x - geomCenter.x;
Real dy = ptToClip.y - geomCenter.y;
Real radius = sqrt(sqr(dx) + sqr(dy));
if (radius > m_majorRadius)
{
Real ratio = m_majorRadius / radius;
ptToClip.x = geomCenter.x + dx * ratio;
ptToClip.y = geomCenter.y + dy * ratio;
}
break;
}
case GEOMETRY_BOX:
{
ptToClip.x = clamp(geomCenter.x - m_majorRadius, ptToClip.x, geomCenter.x + m_majorRadius);
ptToClip.y = clamp(geomCenter.y - m_minorRadius, ptToClip.y, geomCenter.y + m_minorRadius);
break;
}
};
}
//=============================================================================
inline Bool isWithin(Real a, Real b, Real c) { return a<=b && b<=c; }
//=============================================================================
Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D& pt) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
Real dx = pt.x - geomCenter.x;
Real dy = pt.y - geomCenter.y;
Real radius = sqrt(sqr(dx) + sqr(dy));
return (radius <= m_majorRadius);
break;
}
case GEOMETRY_BOX:
{
return isWithin(geomCenter.x - m_majorRadius, pt.x, geomCenter.x + m_majorRadius) &&
isWithin(geomCenter.y - m_minorRadius, pt.y, geomCenter.y + m_minorRadius);
}
};
return false;
}
//=============================================================================
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
#if 1
// this is a better technique than the more obvious radius-and-angle
// one, below, because the latter tends to clump more towards the center.
Real maxDistSqr = sqr(m_majorRadius);
Real distSqr;
do
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.z = 0.0f;
distSqr = sqr(pt.x) + sqr(pt.y);
} while (distSqr > maxDistSqr);
#else
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
Real angle = GameLogicRandomValueReal(-PI, PI);
pt.x = radius * Cos(angle);
pt.y = radius * Sin(angle);
pt.z = 0.0f;
#endif
break;
}
case GEOMETRY_BOX:
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
pt.z = 0.0f;
break;
}
};
}
//=============================================================================
void GeometryInfo::makeRandomOffsetOnPerimeter(Coord3D& pt) const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
DEBUG_CRASH( ("GeometryInfo::makeRandomOffsetOnPerimeter() not implemented for SPHERE or CYLINDER extents. Using position.") );
//Kris: Did not have time nor need to support non-box extents. I added this feature for script placement
// of boobytraps.
pt.x = 0.0f;
pt.y = 0.0f;
break;
}
case GEOMETRY_BOX:
{
if( GameLogicRandomValueReal( 0.0f, 1.0f ) < 0.5f )
{
//Pick random point on x axis.
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
//Min or max the y axis value
if( GameLogicRandomValueReal( 0.0f, 1.0f ) < 0.5f )
pt.y = -m_minorRadius;
else
pt.y = m_minorRadius;
}
else
{
//Pick random point on y axis.
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
//Min or max the x axis value
if( GameLogicRandomValueReal( 0.0f, 1.0f ) < 0.5f )
pt.x = -m_majorRadius;
else
pt.x = m_majorRadius;
}
pt.z = 0.0f;
break;
}
};
}
//=============================================================================
Real GeometryInfo::getFootprintArea() const
{
switch(m_type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
return PI * sqr(m_boundingCircleRadius);
}
case GEOMETRY_BOX:
{
return 4.0f * m_majorRadius * m_minorRadius;
}
};
DEBUG_CRASH(("should never get here"));
return 0.0f;
}
//=============================================================================
void GeometryInfo::calcBoundingStuff()
{
switch(m_type)
{
case GEOMETRY_SPHERE:
{
m_boundingSphereRadius = m_majorRadius;
m_boundingCircleRadius = m_majorRadius;
break;
}
case GEOMETRY_CYLINDER:
{
m_boundingCircleRadius = m_majorRadius;
m_boundingSphereRadius = m_height*0.5;
if (m_boundingSphereRadius < m_majorRadius)
m_boundingSphereRadius = m_majorRadius;
break;
}
case GEOMETRY_BOX:
{
m_boundingCircleRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
m_boundingSphereRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
break;
}
};
}
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
void GeometryInfo::tweakExtents(ExtentModType extentModType, Real extentModAmount)
{
switch(extentModType)
{
case EXTENTMOD_HEIGHT:
m_height += extentModAmount;
break;
case EXTENTMOD_MAJOR:
m_majorRadius += extentModAmount;
break;
case EXTENTMOD_MINOR:
m_minorRadius += extentModAmount;
break;
case EXTENTMOD_TYPE:
m_type = (GeometryType)((m_type + ((extentModType == EXTENTMOD_TYPE)?1:0)) % GEOMETRY_NUM_TYPES);
break;
}
m_isSmall = false;
calcBoundingStuff();
}
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
//=============================================================================
AsciiString GeometryInfo::getDescriptiveString() const
{
AsciiString msg;
msg.format("%d/%d(%g %g %g)", (Int)m_type, (Int)m_isSmall, m_majorRadius, m_minorRadius, m_height);
return msg;
}
#endif
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// type
xfer->xferUser( &m_type, sizeof( GeometryType ) );
// is small
xfer->xferBool( &m_isSmall );
// height
xfer->xferReal( &m_height );
// major radius
xfer->xferReal( &m_majorRadius );
// minor radius
xfer->xferReal( &m_minorRadius );
// bouncing circle radius
xfer->xferReal( &m_boundingCircleRadius );
// bounding sphere radius
xfer->xferReal( &m_boundingSphereRadius );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void GeometryInfo::loadPostProcess( void )
{
} // end loadPostProcess

View File

@@ -0,0 +1,183 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// Kindof.cpp /////////////////////////////////////////////////////////////////////////////////////
// Part of header detangling
// John McDonald, Aug 2002
#include "PreRTS.h"
#include "Common/KindOf.h"
#include "Common/BitFlagsIO.h"
const char* KindOfMaskType::s_bitNameList[] =
{
"OBSTACLE",
"SELECTABLE",
"IMMOBILE",
"CAN_ATTACK",
"STICK_TO_TERRAIN_SLOPE",
"CAN_CAST_REFLECTIONS",
"SHRUBBERY",
"STRUCTURE",
"INFANTRY",
"VEHICLE",
"AIRCRAFT",
"HUGE_VEHICLE",
"DOZER",
"HARVESTER",
"COMMANDCENTER",
#ifdef ALLOW_SURRENDER
"PRISON",
"COLLECTS_PRISON_BOUNTY",
"POW_TRUCK",
#endif
"LINEBUILD",
"SALVAGER",
"WEAPON_SALVAGER",
"TRANSPORT",
"BRIDGE",
"LANDMARK_BRIDGE",
"BRIDGE_TOWER",
"PROJECTILE",
"PRELOAD",
"NO_GARRISON",
"WAVEGUIDE",
"WAVE_EFFECT",
"NO_COLLIDE",
"REPAIR_PAD",
"HEAL_PAD",
"STEALTH_GARRISON",
"CASH_GENERATOR",
"DRAWABLE_ONLY",
"MP_COUNT_FOR_VICTORY",
"REBUILD_HOLE",
"SCORE",
"SCORE_CREATE",
"SCORE_DESTROY",
"NO_HEAL_ICON",
"CAN_RAPPEL",
"PARACHUTABLE",
#ifdef ALLOW_SURRENDER
"CAN_SURRENDER",
#endif
"CAN_BE_REPULSED",
"MOB_NEXUS",
"IGNORED_IN_GUI",
"CRATE",
"CAPTURABLE",
"CLEARED_BY_BUILD",
"SMALL_MISSILE",
"ALWAYS_VISIBLE",
"UNATTACKABLE",
"MINE",
"CLEANUP_HAZARD",
"PORTABLE_STRUCTURE",
"ALWAYS_SELECTABLE",
"ATTACK_NEEDS_LINE_OF_SIGHT",
"WALK_ON_TOP_OF_WALL",
"DEFENSIVE_WALL",
"FS_POWER",
"FS_FACTORY",
"FS_BASE_DEFENSE",
"FS_TECHNOLOGY",
"AIRCRAFT_PATH_AROUND",
"LOW_OVERLAPPABLE",
"FORCEATTACKABLE",
"AUTO_RALLYPOINT",
"TECH_BUILDING",
"POWERED",
"PRODUCED_AT_HELIPAD",
"DRONE",
"CAN_SEE_THROUGH_STRUCTURE",
"BALLISTIC_MISSILE",
"CLICK_THROUGH",
"SUPPLY_SOURCE_ON_PREVIEW",
"PARACHUTE",
"GARRISONABLE_UNTIL_DESTROYED",
"BOAT",
"IMMUNE_TO_CAPTURE",
"HULK",
"SHOW_PORTRAIT_WHEN_CONTROLLED",
"SPAWNS_ARE_THE_WEAPONS",
"CANNOT_BUILD_NEAR_SUPPLIES",
"SUPPLY_SOURCE",
"REVEAL_TO_ALL",
"DISGUISER",
"INERT",
"HERO",
"IGNORES_SELECT_ALL",
"DONT_AUTO_CRUSH_INFANTRY",
"CLIFF_JUMPER",
"FS_SUPPLY_DROPZONE",
"FS_SUPERWEAPON",
"FS_BLACK_MARKET",
"FS_SUPPLY_CENTER",
"FS_STRATEGY_CENTER",
"MONEY_HACKER",
"ARMOR_SALVAGER",
"REVEALS_ENEMY_PATHS",
"BOOBY_TRAP",
"FS_FAKE",
"FS_INTERNET_CENTER",
"BLAST_CRATER",
"PROP",
"OPTIMIZED_TREE",
"FS_ADVANCED_TECH",
"FS_BARRACKS",
"FS_WARFACTORY",
"FS_AIRFIELD",
"AIRCRAFT_CARRIER",
"NO_SELECT",
"REJECT_UNMANNED",
"CANNOT_RETALIATE",
"TECH_BASE_DEFENSE",
"EMP_HARDENED",
"DEMOTRAP",
"CONSERVATIVE_BUILDING",
"IGNORE_DOCKING_BONES",
NULL
};
KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes
KindOfMaskType KINDOFMASK_FS; // inits to all zeroes
void initKindOfMasks()
{
KINDOFMASK_FS.set( KINDOF_FS_FACTORY );
KINDOFMASK_FS.set( KINDOF_FS_BASE_DEFENSE );
KINDOFMASK_FS.set( KINDOF_FS_TECHNOLOGY );
KINDOFMASK_FS.set( KINDOF_FS_SUPPLY_DROPZONE );
KINDOFMASK_FS.set( KINDOF_FS_SUPERWEAPON );
KINDOFMASK_FS.set( KINDOF_FS_BLACK_MARKET );
KINDOFMASK_FS.set( KINDOF_FS_SUPPLY_CENTER );
KINDOFMASK_FS.set( KINDOF_FS_STRATEGY_CENTER );
KINDOFMASK_FS.set( KINDOF_FS_FAKE );
KINDOFMASK_FS.set( KINDOF_FS_INTERNET_CENTER );
KINDOFMASK_FS.set( KINDOF_FS_ADVANCED_TECH );
KINDOFMASK_FS.set( KINDOF_FS_BARRACKS );
KINDOFMASK_FS.set( KINDOF_FS_WARFACTORY );
KINDOFMASK_FS.set( KINDOF_FS_AIRFIELD );
}

View File

@@ -0,0 +1,451 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: List
//
// File name: WSYS_List.cpp
//
// Created: 10/31/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/List.h"
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// LList::LList
//============================================================================
LList::LList( )
: m_sortMode(DESCENDING)
{
m_head.setItem( &m_head.m_item);
};
//=================================================================
// LList::add
//=================================================================
void LList::add( LListNode* new_node )
{
LListNode* node;
Int pri;
if ( m_addToEndOfGroup )
{
pri = new_node->priority();
node = &m_head;
while( (node = node->prev() ))
{
if( (m_sortMode == ASCENDING && node->priority() >= pri)
|| (m_sortMode == DESCENDING && node->priority() <= pri) )
{
node->append( new_node );
return;
}
}
m_head.append( new_node );
}
else
{
pri = new_node->priority();
node = &m_head;
while( (node = node->next() ))
{
if( (m_sortMode == ASCENDING && node->priority() <= pri)
|| (m_sortMode == DESCENDING && node->priority() >= pri) )
{
node->insert( new_node );
return;
}
}
m_head.insert( new_node );
}
}
//============================================================================
// LList::addGDFNode
//============================================================================
void LList::addItem( Int pri, void* item )
{
LListNode *node = NEW LListNode(); // poolify
if ( node )
{
node->setPriority( pri );
node->setItem( item );
node->autoDelete();
add( node );
}
}
//============================================================================
// LList::addGDFNodeToHead
//============================================================================
void LList::addItemToHead( void *item )
{
LListNode *node = NEW LListNode();
if ( node )
{
node->setItem( item );
node->autoDelete();
addToHead( node );
}
}
//============================================================================
// LList::addGDFNodeToTail
//============================================================================
void LList::addItemToTail( void *item )
{
LListNode *node = NEW LListNode();
if ( node )
{
node->setItem( item );
node->autoDelete();
addToTail( node );
}
}
//============================================================================
// LList::Clear
//============================================================================
void LList::clear( void )
{
LListNode *node;
while ( (node = firstNode()) != NULL )
{
node->remove();
node->destroy();
}
}
//=================================================================
// LList::nodeCount
//=================================================================
Int LList::nodeCount( void )
{
LListNode* node;
Int count = 0;
node = firstNode();
while(node)
{
count++;
node = node->next();
}
return count;
}
//=================================================================
// LList::getNode
//=================================================================
LListNode* LList::getNode( Int index )
{
LListNode* node;
node = firstNode();
while( node && index >= 0 )
{
if( index-- == 0 )
{
return node;
}
node = node->next();
}
return NULL;
}
//============================================================================
// LList::merge
//============================================================================
void LList::merge( LList *list )
{
if ( list == NULL || list->isEmpty() )
{
return;
}
m_head.m_prev->m_next = list->m_head.m_next;
list->m_head.m_next->m_prev = m_head.m_prev;
list->m_head.m_prev->m_next = &m_head;
m_head.m_prev = list->m_head.m_prev;
list->m_head.m_next = &list->m_head;
list->m_head.m_prev = &list->m_head;
}
//============================================================================
// LList::hasReference
//============================================================================
Bool LList::hasItem( void *item )
{
return findItem( item ) != NULL;
}
//============================================================================
// LList::findReference
//============================================================================
LListNode* LList::findItem( void *item )
{
LListNode* node;
node = firstNode();
while( node )
{
if( node->item() == item )
{
return node;
}
node = node->next();
}
return NULL;
}
//============================================================================
// LListNode::LListNode
//============================================================================
LListNode::LListNode()
: m_pri(0),
m_item(NULL),
m_autoDelete(FALSE)
{
m_next = m_prev = this;
};
//=================================================================
// LListNode::insert
//=================================================================
void LListNode::insert( LListNode* new_node )
{
new_node->m_prev = m_prev;
new_node->m_next = this;
m_prev = new_node;
new_node->m_prev->m_next = new_node;
}
//=================================================================
// LListNode::append
//=================================================================
void LListNode::append( LListNode* new_node )
{
new_node->m_prev = this;
new_node->m_next = m_next;
this->m_next = new_node;
new_node->m_next->m_prev = new_node;
}
//=================================================================
// LListNode::remove
//=================================================================
void LListNode::remove( void )
{
m_prev->m_next = m_next;
m_next->m_prev = m_prev;
m_prev = m_next = this; // so we know that the node is not in a list
}
//=================================================================
// LListNode::next
//=================================================================
LListNode* LListNode::next( void )
{
if( m_next->isHead( ))
{
return NULL;
}
return m_next;
}
//=================================================================
// LListNode::prev
//=================================================================
LListNode* LListNode::prev( void )
{
if( m_prev->isHead())
{
return NULL;
}
return m_prev;
}
//=================================================================
// LListNode::loopNext
//=================================================================
LListNode* LListNode::loopNext( void )
{
LListNode* next;
next = m_next;
if( next->isHead( ))
{
// skip head node
next = next->m_next;
if( next->isHead( ))
{
return NULL; // it is an empty list
}
}
return next;
}
//=================================================================
// LListNode::loopPrev
//=================================================================
LListNode* LListNode::loopPrev( void )
{
LListNode* prev;
prev = m_prev;
if( prev->isHead())
{
// skip head node
prev = prev->m_prev;
if( prev->isHead())
{
return NULL; // it is an empty list
}
}
return prev;
}
//============================================================================
// LListNode::destroy
//============================================================================
void LListNode::destroy( void )
{
if ( m_autoDelete )
{
delete this;
}
}
//============================================================================
// LList::addToEndOfGroup
//============================================================================
void LList::addToEndOfGroup( Bool yes )
{
m_addToEndOfGroup = yes;
}

View File

@@ -0,0 +1,627 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WSYS Library
//
// Module: IO_
//
// File name: IO_LocalFile.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <ctype.h>
#include "Common/LocalFile.h"
#include "Common/RAMFile.h"
#include "Lib/BaseType.h"
#include "Common/PerfTimer.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
static Int s_totalOpen = 0;
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//=================================================================
// LocalFile::LocalFile
//=================================================================
LocalFile::LocalFile()
#ifdef USE_BUFFERED_IO
: m_file(NULL)
#else
: m_handle(-1)
#endif
{
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//=================================================================
// LocalFile::~LocalFile
//=================================================================
LocalFile::~LocalFile()
{
#ifdef USE_BUFFERED_IO
if (m_file)
{
fclose(m_file);
m_file = NULL;
--s_totalOpen;
}
#else
if( m_handle != -1 )
{
_close( m_handle );
m_handle = -1;
--s_totalOpen;
}
#endif
File::close();
}
//=================================================================
// LocalFile::open
//=================================================================
/**
* This function opens a file using the standard C open() call. Access flags
* are mapped to the appropriate open flags. Returns true if file was opened
* successfully.
*/
//=================================================================
//DECLARE_PERF_TIMER(LocalFile)
Bool LocalFile::open( const Char *filename, Int access )
{
//USE_PERF_TIMER(LocalFile)
if( !File::open( filename, access) )
{
return FALSE;
}
/* here we translate WSYS file access to the std C equivalent */
#ifdef USE_BUFFERED_IO
char mode[32];
char* m = mode;
if (m_access & APPEND)
{
DEBUG_CRASH(("not yet supported by buffered mode"));
}
if (m_access & TRUNCATE)
{
DEBUG_CRASH(("not yet supported by buffered mode"));
}
if((m_access & READWRITE ) == READWRITE )
{
if (m_access & CREATE)
{
*m++ = 'w';
*m++ = '+';
}
else
{
*m++ = 'r';
*m++ = '+';
}
}
else if(m_access & WRITE)
{
*m++ = 'w';
}
else
{
*m++ = 'r';
DEBUG_ASSERTCRASH(!(m_access & TRUNCATE), ("cannot truncate with read-only"));
}
if (m_access & TEXT)
{
*m++ = 't';
}
if (m_access & BINARY)
{
*m++ = 'b';
}
*m++ = 0;
m_file = fopen(filename, mode);
if (m_file == NULL)
{
goto error;
}
//setvbuf(m_file, m_vbuf, _IOFBF, sizeof(BUF_SIZE));
#else
int flags = 0;
if (m_access & CREATE)
{
flags |= _O_CREAT;
}
if (m_access & TRUNCATE)
{
flags |= _O_TRUNC;
}
if (m_access & APPEND)
{
flags |= _O_APPEND;
}
if (m_access & TEXT)
{
flags |= _O_TEXT;
}
if (m_access & BINARY)
{
flags |= _O_BINARY;
}
if((m_access & READWRITE )== READWRITE )
{
flags |= _O_RDWR;
}
else if(m_access & WRITE)
{
flags |= _O_WRONLY;
flags |= _O_CREAT;
}
else
{
flags |= _O_RDONLY;
}
m_handle = _open( filename, flags , _S_IREAD | _S_IWRITE);
if( m_handle == -1 )
{
goto error;
}
#endif
++s_totalOpen;
/// DEBUG_LOG(("LocalFile::open %s (total %d)\n",filename,s_totalOpen));
if ( m_access & APPEND )
{
if ( seek ( 0, END ) < 0 )
{
goto error;
}
}
return TRUE;
error:
close();
return FALSE;
}
//=================================================================
// LocalFile::close
//=================================================================
/**
* Closes the current file if it is open.
* Must call LocalFile::close() for each successful LocalFile::open() call.
*/
//=================================================================
void LocalFile::close( void )
{
File::close();
}
//=================================================================
// LocalFile::read
//=================================================================
Int LocalFile::read( void *buffer, Int bytes )
{
//USE_PERF_TIMER(LocalFile)
if( !m_open )
{
return -1;
}
if (buffer == NULL)
{
#ifdef USE_BUFFERED_IO
fseek(m_file, bytes, SEEK_CUR);
#else
_lseek(m_handle, bytes, SEEK_CUR);
#endif
return bytes;
}
#ifdef USE_BUFFERED_IO
Int ret = fread(buffer, 1, bytes, m_file);
#else
Int ret = _read( m_handle, buffer, bytes );
#endif
return ret;
}
//=================================================================
// LocalFile::write
//=================================================================
Int LocalFile::write( const void *buffer, Int bytes )
{
if( !m_open || !buffer )
{
return -1;
}
#ifdef USE_BUFFERED_IO
Int ret = fwrite(buffer, 1, bytes, m_file);
#else
Int ret = _write( m_handle, buffer, bytes );
#endif
return ret;
}
//=================================================================
// LocalFile::seek
//=================================================================
Int LocalFile::seek( Int pos, seekMode mode)
{
int lmode;
switch( mode )
{
case START:
lmode = SEEK_SET;
break;
case CURRENT:
lmode = SEEK_CUR;
break;
case END:
DEBUG_ASSERTCRASH(pos <= 0, ("LocalFile::seek - pos should be <= 0 for a seek starting at the end of the file"));
lmode = SEEK_END;
break;
default:
// bad seek mode
return -1;
}
#ifdef USE_BUFFERED_IO
Int ret = fseek(m_file, pos, lmode);
if (ret == 0)
return ftell(m_file);
else
return -1;
#else
Int ret = _lseek( m_handle, pos, lmode );
#endif
return ret;
}
//=================================================================
// LocalFile::scanInt
//=================================================================
// skips preceding whitespace and stops at the first non-number
// or at EOF
Bool LocalFile::scanInt(Int &newInt)
{
newInt = 0;
AsciiString tempstr;
Char c;
Int val;
// skip preceding non-numeric characters
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-')));
if (val == 0) {
return FALSE;
}
do {
tempstr.concat(c);
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && ((c >= '0') && (c <= '9')));
// put the last read char back, since we didn't use it.
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
newInt = atoi(tempstr.str());
return TRUE;
}
//=================================================================
// LocalFile::scanReal
//=================================================================
// skips preceding whitespace and stops at the first non-number
// or at EOF
Bool LocalFile::scanReal(Real &newReal)
{
newReal = 0.0;
AsciiString tempstr;
Char c;
Int val;
Bool sawDec = FALSE;
// skip the preceding white space
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read( m_handle, &c, 1);
#endif
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-') && (c != '.')));
if (val == 0) {
return FALSE;
}
do {
tempstr.concat(c);
if (c == '.') {
sawDec = TRUE;
}
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (((c >= '0') && (c <= '9')) || ((c == '.') && !sawDec)));
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
newReal = atof(tempstr.str());
return TRUE;
}
//=================================================================
// LocalFile::scanString
//=================================================================
// skips preceding whitespace and stops at the first whitespace
// or at EOF
Bool LocalFile::scanString(AsciiString &newString)
{
Char c;
Int val;
newString.clear();
// skip the preceding whitespace
do {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (isspace(c)));
if (val == 0) {
return FALSE;
}
do {
newString.concat(c);
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} while ((val != 0) && (!isspace(c)));
if (val != 0) {
#ifdef USE_BUFFERED_IO
fseek(m_file, -1, SEEK_CUR);
#else
_lseek(m_handle, -1, SEEK_CUR);
#endif
}
return TRUE;
}
//=================================================================
// LocalFile::nextLine
//=================================================================
// scans to the first character after a new-line or at EOF
void LocalFile::nextLine(Char *buf, Int bufSize)
{
Char c = 0;
Int val;
Int i = 0;
// seek to the next new-line.
do {
if ((buf == NULL) || (i >= (bufSize-1))) {
#ifdef USE_BUFFERED_IO
val = fread(&c, 1, 1, m_file);
#else
val = _read(m_handle, &c, 1);
#endif
} else {
#ifdef USE_BUFFERED_IO
val = fread(buf + i, 1, 1, m_file);
#else
val = _read(m_handle, buf + i, 1);
#endif
c = buf[i];
}
++i;
} while ((val != 0) && (c != '\n'));
if (buf != NULL) {
if (i < bufSize) {
buf[i] = 0;
} else {
buf[bufSize] = 0;
}
}
}
//=================================================================
//=================================================================
File* LocalFile::convertToRAMFile()
{
RAMFile *ramFile = newInstance( RAMFile );
if (ramFile->open(this))
{
if (this->m_deleteOnClose)
{
ramFile->deleteOnClose();
this->close(); // is deleteonclose, so should delete.
}
else
{
this->close();
this->deleteInstance();
}
return ramFile;
}
else
{
ramFile->close();
ramFile->deleteInstance();
return this;
}
}
//=================================================================
// LocalFile::readEntireAndClose
//=================================================================
/**
Allocate a buffer large enough to hold entire file, read
the entire file into the buffer, then close the file.
the buffer is owned by the caller, who is responsible
for freeing is (via delete[]). This is a Good Thing to
use because it minimizes memory copies for BIG files.
*/
char* LocalFile::readEntireAndClose()
{
UnsignedInt fileSize = size();
char* buffer = NEW char[fileSize];
read(buffer, fileSize);
close();
return buffer;
}

View File

@@ -0,0 +1,103 @@
/*
** Command & Conquer Generals Zero Hour(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright(C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Game Engine
//
// Module: IO
//
// File name: LocalFileSystem.cpp
//
// Created: 4/23/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h"
#include "Common/LocalFileSystem.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
LocalFileSystem *TheLocalFileSystem = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
void LocalFileSystem::init() {
}
void LocalFileSystem::reset() {
}
void LocalFileSystem::update() {
}

Some files were not shown because too many files have changed in this diff Show More