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
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,848 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <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)
{
if (TheFileSystem->doesFileExist(strToAdjust.str()))
{
return;
}
const char *str = strToAdjust.reverseFind('\\');
if (!str) {
return;
}
AsciiString filename = str;
strToAdjust = generateFilenamePrefix(m_eventInfo->m_soundType, TRUE);
strToAdjust.concat(filename);
}
//-------------------------------------------------------------------------------------------------
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(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()
{
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,125 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <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"
//----------------------------------------------------------------------------
// 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(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(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(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 );
}