diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /soundemittersystem | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'soundemittersystem')
| -rw-r--r-- | soundemittersystem/cbase.h | 22 | ||||
| -rw-r--r-- | soundemittersystem/soundemittersystem.vpc | 76 | ||||
| -rw-r--r-- | soundemittersystem/soundemittersystembase.cpp | 1663 | ||||
| -rw-r--r-- | soundemittersystem/soundemittersystembase.h | 183 | ||||
| -rw-r--r-- | soundemittersystem/xbox/xbox.def | 3 |
5 files changed, 1947 insertions, 0 deletions
diff --git a/soundemittersystem/cbase.h b/soundemittersystem/cbase.h new file mode 100644 index 0000000..42b8bd2 --- /dev/null +++ b/soundemittersystem/cbase.h @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CBASE_H +#define CBASE_H +#ifdef _WIN32 +#pragma once +#endif + +// Shared header file +#include "basetypes.h" +#include "tier1/strtools.h" +#include "vstdlib/random.h" + +#ifdef _WIN32 +extern IUniformRandomStream *random; +#endif + +#endif // CBASE_H diff --git a/soundemittersystem/soundemittersystem.vpc b/soundemittersystem/soundemittersystem.vpc new file mode 100644 index 0000000..62ad4ae --- /dev/null +++ b/soundemittersystem/soundemittersystem.vpc @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// SOUNDEMITTERSYSTEM.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Linker + { + $SystemLibraries "iconv" [$OSXALL] + } + $Compiler + { + $AdditionalIncludeDirectories ".\;$BASE;$SRCDIR\game\shared" + $PreprocessorDefinitions "$BASE;SOUNDEMITTERSYSTEM_EXPORTS;_WINDOWS;PROTECTED_THINGS_ENABLE" + $PreprocessorDefinitions "$BASE;fopen=dont_use_fopen" [$WINDOWS] + } +} + +$Project "SoundEmitterSystem" +{ + $Folder "Source Files" + { + $File "$SRCDIR\game\shared\interval.cpp" + $File "soundemittersystembase.cpp" + $File "$SRCDIR\public\SoundParametersInternal.cpp" + } + + $Folder "Header Files" + { + $File "soundemittersystembase.h" + $File "cbase.h" + $File "$SRCDIR\game\shared\interval.h" + } + + $Folder "Public Header Files" + { + $File "$SRCDIR\public\tier0\basetypes.h" + $File "$SRCDIR\public\Color.h" + $File "$SRCDIR\public\tier0\commonmacros.h" + $File "$SRCDIR\public\tier0\dbg.h" + $File "$SRCDIR\public\tier0\fasttimer.h" + $File "$SRCDIR\public\filesystem.h" + $File "$SRCDIR\public\appframework\IAppSystem.h" + $File "$SRCDIR\public\tier0\icommandline.h" + $File "$SRCDIR\public\engine\IEngineSound.h" + $File "$SRCDIR\public\vstdlib\IKeyValuesSystem.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\public\irecipientfilter.h" + $File "$SRCDIR\public\SoundEmitterSystem\isoundemittersystembase.h" + $File "$SRCDIR\public\tier1\KeyValues.h" + $File "$SRCDIR\public\tier0\mem.h" + $File "$SRCDIR\public\tier0\memdbgoff.h" + $File "$SRCDIR\public\tier0\memdbgon.h" + $File "$SRCDIR\public\tier0\platform.h" + $File "$SRCDIR\public\tier0\protected_things.h" + $File "$SRCDIR\public\vstdlib\random.h" + $File "$SRCDIR\public\soundchars.h" + $File "$SRCDIR\public\soundflags.h" + $File "$SRCDIR\public\string_t.h" + $File "$SRCDIR\public\tier1\strtools.h" + $File "$SRCDIR\public\tier1\utlbuffer.h" + $File "$SRCDIR\public\tier1\utldict.h" + $File "$SRCDIR\public\tier1\utlmemory.h" + $File "$SRCDIR\public\tier1\utlrbtree.h" + $File "$SRCDIR\public\tier1\utlsymbol.h" + $File "$SRCDIR\public\tier1\utlvector.h" + $File "$SRCDIR\public\vstdlib\vstdlib.h" + } +} diff --git a/soundemittersystem/soundemittersystembase.cpp b/soundemittersystem/soundemittersystembase.cpp new file mode 100644 index 0000000..41d01e3 --- /dev/null +++ b/soundemittersystem/soundemittersystembase.cpp @@ -0,0 +1,1663 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + + +#include <KeyValues.h> +#include "filesystem.h" +#include "utldict.h" +#include "interval.h" +#include "engine/IEngineSound.h" +#include "soundemittersystembase.h" +#include "utlbuffer.h" +#include "soundchars.h" +#include "vstdlib/random.h" +#include "checksum_crc.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "ifilelist.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MANIFEST_FILE "scripts/game_sounds_manifest.txt" +#define GAME_SOUNDS_HEADER_BLOCK "scripts/game_sounds_header.txt" + +static IFileSystem* filesystem = 0; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSoundEmitterSystemBase::CSoundEmitterSystemBase() : + m_nInitCount( 0 ), + m_uManifestPlusScriptChecksum( 0 ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::First() const +{ + return m_Sounds.FirstHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : i - +// Output : int +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::Next( int i ) const +{ + return m_Sounds.NextHandle(i); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::InvalidIndex() const +{ + return m_Sounds.InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// +// implementation of IUniformRandomStream +// +//----------------------------------------------------------------------------- +class CSoundEmitterUniformRandomStream : public IUniformRandomStream +{ +public: + // Sets the seed of the random number generator + void SetSeed( int iSeed ) + { + // Never call this from the client or game! + Assert(0); + } + + // Generates random numbers + float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ) + { + return ::RandomFloat( flMinVal, flMaxVal ); + } + + int RandomInt( int iMinVal, int iMaxVal ) + { + return ::RandomInt( iMinVal, iMaxVal ); + } + + float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ) + { + return ::RandomFloatExp( flMinVal, flMaxVal, flExponent ); + } + +}; + +static CSoundEmitterUniformRandomStream g_RandomStream; +IUniformRandomStream *randomStream = &g_RandomStream; + + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::Connect( CreateInterfaceFn factory ) +{ + // If someone already connected us up, don't redo the connection + if ( NULL != filesystem ) + { + return true; + } + + filesystem = (IFileSystem *)factory( FILESYSTEM_INTERFACE_VERSION, NULL ); + if( !filesystem ) + { + Error( "The soundemittersystem system requires the filesystem to run!\n" ); + return false; + } + + return true; +} + + +void CSoundEmitterSystemBase::Disconnect() +{ + filesystem = NULL; +} + + +//----------------------------------------------------------------------------- +// Query interface +//----------------------------------------------------------------------------- +void *CSoundEmitterSystemBase::QueryInterface( const char *pInterfaceName ) +{ + // Loading the engine DLL mounts *all* soundemitter interfaces + CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary + return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing. +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +InitReturnVal_t CSoundEmitterSystemBase::Init() +{ + return INIT_OK; +} + +void CSoundEmitterSystemBase::Shutdown() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Helper for checksuming script files and manifest to determine if soundname caches +// need to be blown away. +// Input : *crc - +// *filename - +// Output : static void +//----------------------------------------------------------------------------- +static void AccumulateFileNameAndTimestampIntoChecksum( CRC32_t *crc, char const *filename ) +{ + if ( IsX360() ) + { + // this is an expensive i/o operation due to search path fall through + // 360 doesn't need or use the checksums + return; + } + + long ft = filesystem->GetFileTime( filename, "GAME" ); + CRC32_ProcessBuffer( crc, &ft, sizeof( ft ) ); + CRC32_ProcessBuffer( crc, filename, Q_strlen( filename ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::InternalModInit() +{ + /* + if ( m_SoundKeyValues.Count() > 0 ) + { + Shutdown(); + } + */ + + LoadGlobalActors(); + + m_uManifestPlusScriptChecksum = 0u; + + CRC32_t crc; + CRC32_Init( &crc ); + + KeyValues *manifest = new KeyValues( MANIFEST_FILE ); + if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDEMITTER, MANIFEST_FILE, "GAME" ) ) + { + AccumulateFileNameAndTimestampIntoChecksum( &crc, MANIFEST_FILE ); + + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( !Q_stricmp( sub->GetName(), "precache_file" ) ) + { + AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() ); + + // Add and always precache + AddSoundsFromFile( sub->GetString(), false ); + continue; + } + else if ( !Q_stricmp( sub->GetName(), "preload_file" ) ) + { + AccumulateFileNameAndTimestampIntoChecksum( &crc, sub->GetString() ); + + // Add and always precache + AddSoundsFromFile( sub->GetString(), true ); + continue; + } + else if ( !Q_stricmp( sub->GetName(), "faceposer_file" ) ) + { + // do nothing for these files; they're only used for faceposer + continue; + } + + Warning( "CSoundEmitterSystemBase::BaseInit: Manifest '%s' with bogus file type '%s', expecting 'declare_file' or 'precache_file'\n", + MANIFEST_FILE, sub->GetName() ); + } + } + else + { + Error( "Unable to load manifest file '%s'\n", MANIFEST_FILE ); + } + manifest->deleteThis(); + + CRC32_Final( &crc ); + + m_uManifestPlusScriptChecksum =( unsigned int )crc; + +// Only print total once, on server +#if !defined( CLIENT_DLL ) && !defined( FACEPOSER ) + DevMsg( 1, "CSoundEmitterSystem: Registered %i sounds\n", m_Sounds.Count() ); +#endif + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::ModInit() +{ + ++m_nInitCount; + + if ( m_nInitCount > 1 ) + { + return true; + } + + return InternalModInit(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::InternalModShutdown() +{ + int i; + m_SoundKeyValues.RemoveAll(); + + for ( UtlHashHandle_t nIndex = m_Sounds.FirstHandle(); nIndex != m_Sounds.InvalidHandle(); nIndex = m_Sounds.NextHandle( nIndex ) ) + { + delete m_Sounds[ nIndex ]; + } + + m_Sounds.Purge(); + + for ( i = 0; i < m_SavedOverrides.Count() ; ++i ) + { + delete m_SavedOverrides[ i ]; + } + m_SavedOverrides.Purge(); + m_Waves.RemoveAll(); + m_ActorGenders.Purge(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::ModShutdown() +{ + if ( --m_nInitCount > 0 ) + return; + + InternalModShutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pName - +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::GetSoundIndex( const char *pName ) const +{ + if ( !pName ) + return -1; + + CSoundEntry search; + search.m_Name = pName; + UtlHashHandle_t idx = m_Sounds.Find( pName ); + if ( idx == m_Sounds.InvalidHandle() ) + return -1; + + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::IsValidIndex( int index ) +{ + return m_Sounds.IsValidHandle( index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : char const +//----------------------------------------------------------------------------- +const char *CSoundEmitterSystemBase::GetSoundName( int index ) +{ + if ( !IsValidIndex( index ) ) + return ""; + + return m_Sounds[ index ]->m_Name.Get(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::GetSoundCount( void ) +{ + return m_Sounds.Count(); +} + +void CSoundEmitterSystemBase::EnsureAvailableSlotsForGender( SoundFile *pSoundnames, int c, gender_t gender ) +{ + int i; + if ( c <= 0 ) + { + return; + } + + CUtlVector< int > slots; + + bool needsreset = false; + for ( i = 0; i < c; i++ ) + { + if ( pSoundnames[ i ].gender != gender ) + continue; + + // There was at least one match for the gender + needsreset = true; + + // This sound is unavailable + if ( !pSoundnames[ i ].available ) + continue; + + slots.AddToTail( i ); + } + + if ( slots.Count() == 0 && needsreset ) + { + // Reset all slots for the specified gender!!! + for ( i = 0; i < c; i++ ) + { + if ( pSoundnames[ i ].gender != gender ) + continue; + + pSoundnames[ i ].available = true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : gender - +// soundnames - +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::FindBestSoundForGender( SoundFile *pSoundnames, int c, gender_t gender ) +{ + // Check for recycling of random sounds... + EnsureAvailableSlotsForGender( pSoundnames, c, gender ); + + if ( c <= 0 ) + { + return -1; + } + + CUtlVector< int > slots; + + for ( int i = 0; i < c; i++ ) + { + if ( pSoundnames[ i ].gender == gender && + pSoundnames[ i ].available ) + { + slots.AddToTail( i ); + } + } + + if ( slots.Count() >= 1 ) + { + int idx = slots[ randomStream->RandomInt( 0, slots.Count() - 1 ) ]; + return idx; + } + + int idx = randomStream->RandomInt( 0, c - 1 ); + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *soundname - +// params - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ ) +{ + HSOUNDSCRIPTHANDLE index = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname ); + if ( index == SOUNDEMITTER_INVALID_HANDLE ) + { + static CUtlSymbolTable soundWarnings; + char key[ 256 ]; + Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname ); + if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) ) + { + soundWarnings.AddString( key ); + + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname ); + } + return false; + } + + return GetParametersForSoundEx( soundname, index, params, gender, isbeingemitted ); +} + +CSoundParametersInternal *CSoundEmitterSystemBase::InternalGetParametersForSound( int index ) +{ + if ( !m_Sounds.IsValidHandle( index ) ) + { + Assert( !"CSoundEmitterSystemBase::InternalGetParametersForSound: Bogus index" ); + return NULL; + } + + return &m_Sounds[ index ]->m_SoundParams; +} + +static void SplitName( char const *input, int splitchar, int splitlen, char *before, int beforelen, char *after, int afterlen ) +{ + char const *in = input; + char *out = before; + + int c = 0; + int l = 0; + int maxl = beforelen; + while ( *in ) + { + if ( c == splitchar ) + { + while ( --splitlen >= 0 ) + { + in++; + } + + *out = 0; + out = after; + maxl = afterlen; + c++; + continue; + } + + if ( l >= maxl ) + { + in++; + c++; + continue; + } + + *out++ = *in++; + l++; + c++; + } + + *out = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : params - +// *wavename - +// gender - +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::AddSoundName( CSoundParametersInternal& params, char const *wavename, gender_t gender ) +{ + CUtlSymbol sym = m_Waves.AddString( wavename ); + SoundFile e; + e.symbol = sym; + e.gender = gender; + if ( gender != GENDER_NONE ) + { + params.SetUsesGenderToken( true ); + } + params.AddSoundName( e ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : params - +// *wavename - +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename ) +{ + char const *p = Q_stristr( wavename, SOUNDGENDER_MACRO ); + + if ( !p ) + { + AddSoundName( params, wavename, GENDER_NONE ); + return; + } + + int offset = p - wavename; + Assert( offset >= 0 ); + int duration = SOUNDGENDER_MACRO_LENGTH; + + // Create a "male" and "female" version of the sound + char before[ 256 ], after[ 256 ]; + Q_memset( before, 0, sizeof( before ) ); + Q_memset( after, 0, sizeof( after ) ); + + SplitName( wavename, offset, duration, before, sizeof( before ), after, sizeof( after ) ); + + char temp[ 256 ]; + Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "male", after ); + AddSoundName( params, temp, GENDER_MALE ); + Q_snprintf( temp, sizeof( temp ), "%s%s%s", before, "female", after ); + AddSoundName( params, temp, GENDER_FEMALE ); + + // Add the conversion entry with the gender tags still in it + CUtlSymbol sym = m_Waves.AddString( wavename ); + SoundFile e; + e.symbol = sym; + e.gender = GENDER_NONE; + params.AddConvertedName( e ); +} + +void CSoundEmitterSystemBase::GenderExpandString( gender_t gender, char const *in, char *out, int maxlen ) +{ + // Assume the worst + Q_strncpy( out, in, maxlen ); + + char const *p = Q_stristr( in, SOUNDGENDER_MACRO ); + if ( !p ) + { + return; + } + + // Look up actor gender + if ( gender == GENDER_NONE ) + { + return; + } + + int offset = p - in; + Assert( offset >= 0 ); + int duration = SOUNDGENDER_MACRO_LENGTH; + + // Create a "male" and "female" version of the sound + char before[ 256 ], after[ 256 ]; + Q_memset( before, 0, sizeof( before ) ); + Q_memset( after, 0, sizeof( after ) ); + + SplitName( in, offset, duration, before, sizeof( before ), after, sizeof( after ) ); + + switch ( gender ) + { + default: + case GENDER_NONE: + { + Assert( !"CSoundEmitterSystemBase::GenderExpandString: expecting MALE or FEMALE!" ); + } + break; + case GENDER_MALE: + { + Q_snprintf( out, maxlen, "%s%s%s", before, "male", after ); + } + break; + case GENDER_FEMALE: + { + Q_snprintf( out, maxlen, "%s%s%s", before, "female", after ); + } + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actorname - +// *in - +// *out - +// maxlen - +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen ) +{ + gender_t gender = GetActorGender( actormodel ); + GenderExpandString( gender, in, out, maxlen ); +} + +void CSoundEmitterSystemBase::LoadGlobalActors() +{ + // Now load the global actor list from the scripts/globalactors.txt file + KeyValues *allActors = NULL; + + allActors = new KeyValues( "allactors" ); + if ( allActors->LoadFromFile( filesystem, "scripts/global_actors.txt", NULL ) ) + { + KeyValues *pvkActor; + for ( pvkActor = allActors->GetFirstSubKey(); pvkActor != NULL; pvkActor = pvkActor->GetNextKey() ) + { + UtlHashHandle_t idx = m_ActorGenders.Find( pvkActor->GetName() ); + if ( idx == m_ActorGenders.InvalidHandle() ) + { + if ( m_ActorGenders.Count() > 254 ) + { + Warning( "Exceeded max number of actors in scripts/global_actors.txt\n" ); + break; + } + + gender_t gender = GENDER_NONE; + if ( !Q_stricmp( pvkActor->GetString(), "male" ) ) + { + gender = GENDER_MALE; + } + else if (!Q_stricmp( pvkActor->GetString(), "female" ) ) + { + gender = GENDER_FEMALE; + } + m_ActorGenders.Insert( pvkActor->GetName(), gender ); + } + } + } + allActors->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actorname - +// Output : gender_t +//----------------------------------------------------------------------------- +gender_t CSoundEmitterSystemBase::GetActorGender( char const *actormodel ) +{ + char actor[ 256 ]; + actor[0] = 0; + if ( actormodel ) + { + Q_FileBase( actormodel, actor, sizeof( actor ) ); + } + + UtlHashHandle_t idx = m_ActorGenders.Find( actor ); + if ( idx == m_ActorGenders.InvalidHandle() ) + return GENDER_NONE; + + return m_ActorGenders[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *soundname - +// params - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSoundEmitterSystemBase::InitSoundInternalParameters( const char *soundname, KeyValues *kv, CSoundParametersInternal& params ) +{ + KeyValues *pKey = kv->GetFirstSubKey(); + while ( pKey ) + { + if ( !Q_strcasecmp( pKey->GetName(), "channel" ) ) + { + params.ChannelFromString( pKey->GetString() ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "volume" ) ) + { + params.VolumeFromString( pKey->GetString() ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) ) + { + params.PitchFromString( pKey->GetString() ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "wave" ) ) + { + ExpandSoundNameMacros( params, pKey->GetString() ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) ) + { + KeyValues *pWaves = pKey->GetFirstSubKey(); + while ( pWaves ) + { + ExpandSoundNameMacros( params, pWaves->GetString() ); + + pWaves = pWaves->GetNextKey(); + } + } + else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) || !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" ) ) + { + if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) ) + { + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"attenuation\" with %s value!\n", + soundname, pKey->GetString() ); + } + + if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) ) + { + params.SetSoundLevel( ATTN_TO_SNDLVL( TranslateAttenuation( pKey->GetString() ) ) ); + } + else + { + interval_t interval; + interval = ReadInterval( pKey->GetString() ); + + // Translate from attenuation to soundlevel + float start = interval.start; + float end = interval.start + interval.range; + + params.SetSoundLevel( ATTN_TO_SNDLVL( start ), ATTN_TO_SNDLVL( end ) - ATTN_TO_SNDLVL( start ) ); + } + + // Goldsrc compatibility mode.. feed the sndlevel value through the sound engine interface in such a way + // that it can reconstruct the original sndlevel value and flag the sound as using Goldsrc attenuation. + bool bCompatibilityAttenuation = !Q_strcasecmp( pKey->GetName(), "CompatibilityAttenuation" ); + if ( bCompatibilityAttenuation ) + { + if ( params.GetSoundLevel().range != 0 ) + { + Warning( "CompatibilityAttenuation for sound %s must have same start and end values.\n", soundname ); + } + + params.SetSoundLevel( SNDLEVEL_TO_COMPATIBILITY_MODE( params.GetSoundLevel().start ) ); + } + } + else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) ) + { + if ( !Q_strncasecmp( pKey->GetString(), "ATTN_", strlen( "ATTN_" ) ) ) + { + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has \"soundlevel\" with %s value!\n", + soundname, pKey->GetString() ); + } + + params.SoundLevelFromString( pKey->GetString() ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "play_to_owner_only" ) ) + { + params.SetOnlyPlayToOwner( pKey->GetInt() ? true : false ); + } + else if ( !Q_strcasecmp( pKey->GetName(), "delay_msec" ) ) + { + // Don't allow negative delay + params.SetDelayMsec( max( 0, pKey->GetInt() ) ); + + } + + pKey = pKey->GetNextKey(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *soundname - +// Output : char const +//----------------------------------------------------------------------------- +const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, char const *actormodel ) +{ + gender_t gender = GetActorGender( actormodel ); + return GetWavFileForSound( soundname, gender ); +} + +const char *CSoundEmitterSystemBase::GetWavFileForSound( const char *soundname, gender_t gender ) +{ + CSoundParameters params; + if ( !GetParametersForSound( soundname, params, gender ) ) + { + return soundname; + } + + if ( !params.soundname[ 0 ] ) + { + return soundname; + } + + static char outsound[ 512 ]; + Q_strncpy( outsound, params.soundname, sizeof( outsound ) ); + return outsound; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *soundname - +// Output : soundlevel_t +//----------------------------------------------------------------------------- +soundlevel_t CSoundEmitterSystemBase::LookupSoundLevel( const char *soundname ) +{ + CSoundParameters params; + if ( !GetParametersForSound( soundname, params, GENDER_NONE ) ) + { + return SNDLVL_NORM; + } + + return params.soundlevel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::AddSoundsFromFile( const char *filename, bool bPreload, bool bIsOverride /*=false*/, bool bRefresh /*=false*/ ) +{ + CSoundScriptFile sf; + sf.hFilename = filesystem->FindOrAddFileName( filename ); + sf.dirty = false; + + int scriptindex = m_SoundKeyValues.AddToTail( sf ); + + int replaceCount = 0; + int newOverrideCount = 0; + int duplicatedReplacements = 0; + + // Open the soundscape data file, and abort if we can't + KeyValues *kv = new KeyValues( "" ); + if ( filesystem->LoadKeyValues( *kv, IFileSystem::TYPE_SOUNDEMITTER, filename, "GAME" ) ) + { + // parse out all of the top level sections and save their names + KeyValues *pKeys = kv; + while ( pKeys ) + { + if ( pKeys->GetFirstSubKey() ) + { + if ( m_Sounds.Count() >= 65534 ) + { + Warning( "Exceeded maximum number of sound emitter entries\n" ); + break; + } + + CSoundEntry *pEntry; + + { + MEM_ALLOC_CREDIT(); + pEntry = new CSoundEntry; + } + + pEntry->m_Name = pKeys->GetName(); + pEntry->m_bRemoved = false; + pEntry->m_nScriptFileIndex = scriptindex; + pEntry->m_bIsOverride = bIsOverride; + + if ( bIsOverride ) + { + ++newOverrideCount; + } + + UtlHashHandle_t lookup = m_Sounds.Insert( pEntry ); // insert returns existing item if found + if ( m_Sounds[ lookup ] != pEntry ) + { + if ( bIsOverride ) + { + MEM_ALLOC_CREDIT(); + + // Store off the old sound if it's not already an "override" from another file!!! + // Otherwise, just whack it again!!! + if ( !m_Sounds[ lookup ]->IsOverride() ) + { + m_SavedOverrides.AddToTail( m_Sounds[ lookup ] ); + } + else + { + ++duplicatedReplacements; + } + + InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams ); + pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting. + + m_Sounds.ReplaceKey( lookup, pEntry ); + + ++replaceCount; + } + else if ( bRefresh ) + { + InitSoundInternalParameters( pKeys->GetName(), pKeys, m_Sounds[ lookup ]->m_SoundParams ); + } +#if 0 + else + { + DevMsg( "CSoundEmitterSystem::AddSoundsFromFile(%s): Entry %s duplicated, skipping\n", filename, pKeys->GetName() ); + } +#endif + } + else + { + MEM_ALLOC_CREDIT(); + + InitSoundInternalParameters( pKeys->GetName(), pKeys, pEntry->m_SoundParams ); + pEntry->m_SoundParams.SetShouldPreload( bPreload ); // this gets handled by game code after initting. + } + } + pKeys = pKeys->GetNextKey(); + } + + kv->deleteThis(); + } + else + { + if ( !bIsOverride ) + { + Warning( "CSoundEmitterSystem::AddSoundsFromFile: No such file %s\n", filename ); + } + + // Discard + m_SoundKeyValues.Remove( scriptindex ); + + kv->deleteThis(); + + return; + } + + + if ( bIsOverride ) + { + DevMsg( "SoundEmitter: adding map sound overrides from %s [%i total, %i replacements, %i duplicated replacements]\n", + filename, + newOverrideCount, + replaceCount, + duplicatedReplacements ); + } + + Assert( scriptindex >= 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Reload a sound emitter file (used to refresh files after sv_pure is turned on) +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::ReloadSoundEntriesInList( IFileList *pFilesToReload ) +{ + int i, c; + c = m_SoundKeyValues.Count(); + CUtlVector< const char * > processed; + for ( i = 0; i < c ; i++ ) + { + const char *pszFileName = GetSoundScriptName( i ); + if ( pszFileName && pszFileName[0] ) + { + if ( processed.Find( pszFileName) == processed.InvalidIndex() && pFilesToReload->IsFileInList( pszFileName ) ) + { + Msg( "Reloading sound file '%s' due to pure settings.\n", pszFileName ); + + AddSoundsFromFile( pszFileName, false, false, true ); + + // Now mark this file name as being reloaded + processed.AddToTail( pszFileName ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Force ModShutdown and ModInit, skips checks for how many systems have +// requested inits (for con commands). +//----------------------------------------------------------------------------- +void CSoundEmitterSystemBase::Flush() +{ + InternalModShutdown(); + InternalModInit(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::CheckForMissingWavFiles( bool verbose ) +{ + int missing = 0; + + int c = GetSoundCount(); + int i; + char testfile[ 512 ]; + + for ( i = 0; i < c; i++ ) + { + CSoundParametersInternal *internal = InternalGetParametersForSound( i ); + if ( !internal ) + { + Assert( 0 ); + continue; + } + + int waveCount = internal->NumSoundNames(); + for ( int wave = 0; wave < waveCount; wave++ ) + { + CUtlSymbol sym = internal->GetSoundNames()[ wave ].symbol; + const char *name = m_Waves.String( sym ); + if ( !name || !name[ 0 ] ) + { + Assert( 0 ); + continue; + } + + // Skip ! sentence stuff + if ( name[0] == CHAR_SENTENCE ) + continue; + Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( name ) ); + if ( filesystem->FileExists( testfile ) ) + continue; + + internal->SetHadMissingWaveFiles( true ); + + ++missing; + + if ( verbose ) + { + DevMsg( "Sound %s references missing file %s\n", GetSoundName( i ), name ); + } + } + } + + return missing; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *key - +// Output : float +//----------------------------------------------------------------------------- +float CSoundEmitterSystemBase::TranslateAttenuation( const char *key ) +{ + if ( !key ) + { + Assert( 0 ); + return ATTN_NORM; + } + + if ( !Q_strcasecmp( key, "ATTN_NONE" ) ) + return ATTN_NONE; + + if ( !Q_strcasecmp( key, "ATTN_NORM" ) ) + return ATTN_NORM; + + if ( !Q_strcasecmp( key, "ATTN_IDLE" ) ) + return ATTN_IDLE; + + if ( !Q_strcasecmp( key, "ATTN_STATIC" ) ) + return ATTN_STATIC; + + if ( !Q_strcasecmp( key, "ATTN_RICOCHET" ) ) + return ATTN_RICOCHET; + + if ( !Q_strcasecmp( key, "ATTN_GUNFIRE" ) ) + return ATTN_GUNFIRE; + + DevMsg( "CSoundEmitterSystem: Unknown attenuation key %s\n", key ); + + return ATTN_NORM; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *key - +// Output : soundlevel_t +//----------------------------------------------------------------------------- +soundlevel_t CSoundEmitterSystemBase::TranslateSoundLevel( const char *key ) +{ + return TextToSoundLevel( key ); +} + +//----------------------------------------------------------------------------- +// Purpose: Convert "chan_xxx" into integer value for channel +// Input : *name - +// Output : static int +//----------------------------------------------------------------------------- +int CSoundEmitterSystemBase::TranslateChannel( const char *name ) +{ + return TextToChannel( name ); +} + +const char *CSoundEmitterSystemBase::GetSourceFileForSound( int index ) const +{ + if ( index < 0 || index >= (int)m_Sounds.Count() ) + { + Assert( 0 ); + return ""; + } + + CSoundEntry const *entry = m_Sounds[ index ]; + int scriptindex = entry->m_nScriptFileIndex; + if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() ) + { + Assert( 0 ); + return ""; + } + static char fn[ 512 ]; + if ( filesystem->String( m_SoundKeyValues[ scriptindex ].hFilename, fn, sizeof( fn ) )) + { + return fn; + } + Assert( 0 ); + return ""; +} + +const char *CSoundEmitterSystemBase::GetWaveName( CUtlSymbol& sym ) +{ + return m_Waves.String( sym ); +} + +int CSoundEmitterSystemBase::FindSoundScript( const char *name ) const +{ + int i, c; + + FileNameHandle_t hFilename = filesystem->FindFileName( name ); + if ( hFilename ) + { + // First, make sure it's known + c = m_SoundKeyValues.Count(); + for ( i = 0; i < c ; i++ ) + { + if ( m_SoundKeyValues[ i ].hFilename == hFilename ) + { + return i; + } + } + } + + return m_SoundKeyValues.InvalidIndex(); +} + +bool CSoundEmitterSystemBase::AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params ) +{ + int idx = GetSoundIndex( soundname ); + + + int i = FindSoundScript( scriptfile ); + if ( i == m_SoundKeyValues.InvalidIndex() ) + { + Warning( "CSoundEmitterSystemBase::AddSound( '%s', '%s', ... ), script file not list in manifest '%s'\n", + soundname, scriptfile, MANIFEST_FILE ); + return false; + } + + MEM_ALLOC_CREDIT(); + + // More like an update... + if ( IsValidIndex( idx ) ) + { + CSoundEntry *entry = m_Sounds[ idx ]; + + entry->m_bRemoved = false; + entry->m_nScriptFileIndex = i; + entry->m_SoundParams.CopyFrom( params ); + + m_SoundKeyValues[ i ].dirty = true; + + return true; + } + + CSoundEntry *pEntry = new CSoundEntry; + pEntry->m_Name = soundname; + pEntry->m_bRemoved = false; + pEntry->m_nScriptFileIndex = i; + pEntry->m_SoundParams.CopyFrom( params ); + + m_Sounds.Insert( pEntry ); + + m_SoundKeyValues[ i ].dirty = true; + + return true; +} + +void CSoundEmitterSystemBase::RemoveSound( const char *soundname ) +{ + int idx = GetSoundIndex( soundname ); + if ( !IsValidIndex( idx ) ) + { + Warning( "Can't remove %s, no such sound!\n", soundname ); + return; + } + + m_Sounds[ idx ]->m_bRemoved = true; + + // Mark script as dirty + int scriptindex = m_Sounds[ idx ]->m_nScriptFileIndex; + if ( scriptindex < 0 || scriptindex >= m_SoundKeyValues.Count() ) + { + Assert( 0 ); + return; + } + + m_SoundKeyValues[ scriptindex ].dirty = true; +} + +void CSoundEmitterSystemBase::MoveSound( const char *soundname, const char *newscript ) +{ + int idx = GetSoundIndex( soundname ); + if ( !IsValidIndex( idx ) ) + { + Warning( "Can't move '%s', no such sound!\n", soundname ); + return; + } + + int oldscriptindex = m_Sounds[ idx ]->m_nScriptFileIndex; + if ( oldscriptindex < 0 || oldscriptindex >= m_SoundKeyValues.Count() ) + { + Assert( 0 ); + return; + } + + int newscriptindex = FindSoundScript( newscript ); + if ( newscriptindex == m_SoundKeyValues.InvalidIndex() ) + { + Warning( "CSoundEmitterSystemBase::MoveSound( '%s', '%s' ), script file not list in manifest '%s'\n", + soundname, newscript, MANIFEST_FILE ); + return; + } + + // No actual change + if ( oldscriptindex == newscriptindex ) + { + return; + } + + // Move it + m_Sounds[ idx ]->m_nScriptFileIndex = newscriptindex; + + // Mark both scripts as dirty + m_SoundKeyValues[ oldscriptindex ].dirty = true; + m_SoundKeyValues[ newscriptindex ].dirty = true; +} + +int CSoundEmitterSystemBase::GetNumSoundScripts() const +{ + return m_SoundKeyValues.Count(); +} + +const char *CSoundEmitterSystemBase::GetSoundScriptName( int index ) const +{ + if ( index < 0 || index >= m_SoundKeyValues.Count() ) + return NULL; + + static char fn[ 512 ]; + if ( filesystem->String( m_SoundKeyValues[ index ].hFilename, fn, sizeof( fn ) ) ) + { + return fn; + } + return ""; +} + +bool CSoundEmitterSystemBase::IsSoundScriptDirty( int index ) const +{ + if ( index < 0 || index >= m_SoundKeyValues.Count() ) + return false; + + return m_SoundKeyValues[ index ].dirty; +} + +void CSoundEmitterSystemBase::SaveChangesToSoundScript( int scriptindex ) +{ + const char *outfile = GetSoundScriptName( scriptindex ); + if ( !outfile ) + { + Msg( "CSoundEmitterSystemBase::SaveChangesToSoundScript: No script file for index %i\n", scriptindex ); + return; + } + + if ( filesystem->FileExists( outfile ) && + !filesystem->IsFileWritable( outfile ) ) + { + Warning( "%s is not writable, can't save data to file\n", outfile ); + return; + } + + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + // FIXME: Write sound script header + if ( filesystem->FileExists( GAME_SOUNDS_HEADER_BLOCK ) ) + { + FileHandle_t header = filesystem->Open( GAME_SOUNDS_HEADER_BLOCK, "rb", NULL ); + if ( header != FILESYSTEM_INVALID_HANDLE ) + { + int len = filesystem->Size( header ); + + unsigned char *data = new unsigned char[ len + 1 ]; + Q_memset( data, 0, len + 1 ); + + filesystem->Read( data, len, header ); + filesystem->Close( header ); + + data[ len ] = 0; + + char *p = (char *)data; + while ( *p ) + { + if ( *p != '\r' ) + { + buf.PutChar( *p ); + } + ++p; + } + + delete[] data; + } + + buf.Printf( "\n" ); + } + + + int c = GetSoundCount(); + for ( int i = 0; i < c; i++ ) + { + if ( Q_stricmp( outfile, GetSourceFileForSound( i ) ) ) + continue; + + // It's marked for deletion, just skip it + if ( m_Sounds[ i ]->m_bRemoved ) + continue; + + CSoundParametersInternal *p = InternalGetParametersForSound( i ); + if ( !p ) + continue; + + buf.Printf( "\"%s\"\n{\n", GetSoundName( i ) ); + + buf.Printf( "\t\"channel\"\t\t\"%s\"\n", p->ChannelToString() ); + buf.Printf( "\t\"volume\"\t\t\"%s\"\n", p->VolumeToString() ); + buf.Printf( "\t\"pitch\"\t\t\t\"%s\"\n", p->PitchToString() ); + buf.Printf( "\n" ); + buf.Printf( "\t\"soundlevel\"\t\"%s\"\n", p->SoundLevelToString() ); + + if ( p->OnlyPlayToOwner() ) + { + buf.Printf( "\t\"play_to_owner_only\"\t\"1\"\n" ); + } + + if ( p->GetDelayMsec() != 0 ) + { + buf.Printf( "\t\"delay_msec\"\t\"%i\"\n", p->GetDelayMsec() ); + } + + int totalCount = 0; + + int waveCount = p->NumSoundNames(); + int convertedCount = p->NumConvertedNames(); + + totalCount = ( waveCount - 2 * convertedCount ) + convertedCount; + + if ( totalCount > 0 ) + { + buf.Printf( "\n" ); + + if ( waveCount == 1 ) + { + Assert( p->GetSoundNames()[ 0 ].gender == GENDER_NONE ); + buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ 0 ].symbol ) ); + } + else if ( convertedCount == 1 ) + { + Assert( p->GetConvertedNames()[ 0 ].gender == GENDER_NONE ); + buf.Printf( "\t\"wave\"\t\t\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ 0 ].symbol ) ); + } + else + { + buf.Printf( "\t\"rndwave\"\n" ); + buf.Printf( "\t{\n" ); + + int wave; + for ( wave = 0; wave < waveCount; wave++ ) + { + // Skip macro-expanded names + if ( p->GetSoundNames()[ wave ].gender != GENDER_NONE ) + continue; + + buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetSoundNames()[ wave ].symbol ) ); + } + for ( wave = 0; wave < convertedCount; wave++ ) + { + buf.Printf( "\t\t\"wave\"\t\"%s\"\n", GetWaveName( p->GetConvertedNames()[ wave ].symbol ) ); + } + + buf.Printf( "\t}\n" ); + } + + } + + buf.Printf( "}\n" ); + + if ( i != c - 1 ) + { + buf.Printf( "\n" ); + } + } + + // Write it out baby + FileHandle_t fh = filesystem->Open( outfile, "wt" ); + if (fh) + { + filesystem->Write( buf.Base(), buf.TellPut(), fh ); + filesystem->Close(fh); + + // Changed saved successfully + m_SoundKeyValues[ scriptindex ].dirty = false; + } + else + { + Warning( "SceneManager_SaveSoundsToScriptFile: Unable to write file %s!!!\n", outfile ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : CUtlSymbol +//----------------------------------------------------------------------------- +CUtlSymbol CSoundEmitterSystemBase::AddWaveName( const char *name ) +{ + return m_Waves.AddString( name ); +} + +void CSoundEmitterSystemBase::RenameSound( const char *soundname, const char *newname ) +{ + // Same name? + if ( !Q_stricmp( soundname, newname ) ) + { + return; + } + + int oldindex = GetSoundIndex( soundname ); + if ( !IsValidIndex( oldindex ) ) + { + Msg( "Can't rename %s, no such sound\n", soundname ); + return; + } + + int check = GetSoundIndex( newname ); + if ( IsValidIndex( check ) ) + { + Msg( "Can't rename %s to %s, new name already in list\n", soundname, newname ); + return; + } + + MEM_ALLOC_CREDIT(); + + // Copy out old entry + CSoundEntry *pEntry = m_Sounds[ oldindex ]; + // Remove it + m_Sounds.Remove( pEntry ); + pEntry->m_Name = newname; + // Re-insert in new spot + m_Sounds.Insert( pEntry ); + + // Mark associated script as dirty + m_SoundKeyValues[ pEntry->m_nScriptFileIndex ].dirty = true; +} + +void CSoundEmitterSystemBase::UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params ) +{ + int idx = GetSoundIndex( soundname ); + if ( !IsValidIndex( idx ) ) + { + Msg( "Can't UpdateSoundParameters %s, no such sound\n", soundname ); + return; + } + + CSoundEntry *entry = m_Sounds[ idx ]; + + if ( entry->m_SoundParams == params ) + { + // No changes + return; + } + + // Update parameters + entry->m_SoundParams.CopyFrom( params ); + // Set dirty flag + m_SoundKeyValues[ entry->m_nScriptFileIndex ].dirty = true; +} + +bool CSoundEmitterSystemBase::IsUsingGenderToken( char const *soundname ) +{ + int soundindex = GetSoundIndex( soundname ); + if ( soundindex < 0 ) + return false; + + // Look up the sound level from the soundemitter system + CSoundParametersInternal *params = InternalGetParametersForSound( soundindex ); + if ( !params ) + return false; + + return params->UsesGenderToken(); +} +unsigned int CSoundEmitterSystemBase::GetManifestFileTimeChecksum() +{ + return m_uManifestPlusScriptChecksum; +} + +bool CSoundEmitterSystemBase::GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHANDLE& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted /*= false*/ ) +{ + if ( handle == SOUNDEMITTER_INVALID_HANDLE ) + { + handle = GetSoundIndex( soundname ); + if ( handle == SOUNDEMITTER_INVALID_HANDLE ) + return false; + } + + CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle ); + if ( !internal ) + { + Assert( 0 ); + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: No such sound %s\n", soundname ); + return false; + } + + params.channel = internal->GetChannel(); + params.volume = internal->GetVolume().Random(); + params.pitch = internal->GetPitch().Random(); + params.pitchlow = internal->GetPitch().start; + params.pitchhigh = params.pitchlow + internal->GetPitch().range; + params.delay_msec = internal->GetDelayMsec(); + params.count = internal->NumSoundNames(); + params.soundname[ 0 ] = 0; + + int bestIndex = FindBestSoundForGender( internal->GetSoundNames(), internal->NumSoundNames(), gender ); + + if ( bestIndex >= 0 ) + { + Q_strncpy( params.soundname, GetWaveName( internal->GetSoundNames()[ bestIndex ].symbol), sizeof( params.soundname ) ); + + // If we are actually emitting the sound, mark it as not available... + if ( isbeingemitted ) + { + internal->GetSoundNames()[ bestIndex ].available = 0; + } + } + params.soundlevel = (soundlevel_t)(int)internal->GetSoundLevel().Random(); + params.play_to_owner_only = internal->OnlyPlayToOwner(); + + if ( !params.soundname[ 0 ] ) + { + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound %s has no wave or rndwave key!\n", soundname ); + return false; + } + + if ( internal->HadMissingWaveFiles() && + params.soundname[ 0 ] != CHAR_SENTENCE ) + { + char testfile[ 256 ]; + Q_snprintf( testfile, sizeof( testfile ), "sound/%s", PSkipSoundChars( params.soundname ) ); + if ( !filesystem->FileExists( testfile ) ) + { + // Prevent repetitive spew... + static CUtlSymbolTable soundWarnings; + char key[ 256 ]; + Q_snprintf( key, sizeof( key ), "%s:%s", soundname, params.soundname ); + if ( UTL_INVAL_SYMBOL == soundWarnings.Find( key ) ) + { + soundWarnings.AddString( key ); + + DevMsg( "CSoundEmitterSystemBase::GetParametersForSound: sound '%s' references wave '%s' which doesn't exist on disk!\n", + soundname, + params.soundname ); + } + return false; + } + } + + return true; +} + +soundlevel_t CSoundEmitterSystemBase::LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHANDLE& handle ) +{ + if ( handle == SOUNDEMITTER_INVALID_HANDLE ) + { + handle = (HSOUNDSCRIPTHANDLE)GetSoundIndex( soundname ); + if ( handle == SOUNDEMITTER_INVALID_HANDLE ) + return SNDLVL_NORM; + } + + CSoundParametersInternal *internal = InternalGetParametersForSound( (int)handle ); + if ( !internal ) + { + return SNDLVL_NORM; + } + + return (soundlevel_t)(int)internal->GetSoundLevel().Random(); +} + + +// Called from both client and server (single player) or just one (server only in dedicated server and client only if connected to a remote server) +// Called by LevelInitPreEntity to override sound scripts for the mod with level specific overrides based on custom mapnames, etc. +void CSoundEmitterSystemBase::AddSoundOverrides( char const *scriptfile, bool bPreload /*= false*/ ) +{ + FileNameHandle_t handle = filesystem->FindOrAddFileName( scriptfile ); + if ( m_OverrideFiles.Find( handle ) != m_OverrideFiles.InvalidIndex() ) + return; + + m_OverrideFiles.AddToTail( handle ); + // These are overrides + AddSoundsFromFile( scriptfile, bPreload, true ); +} + +// Called by either client or server in LevelShutdown to clear out custom overrides +void CSoundEmitterSystemBase::ClearSoundOverrides() +{ + int i; + int removed = 0; + + for ( UtlHashHandle_t i = m_Sounds.FirstHandle(); i != m_Sounds.InvalidHandle(); ) + { + CSoundEntry *entry = m_Sounds[ i ]; + if ( entry->IsOverride() ) + { + i = m_Sounds.RemoveAndAdvance( i ); + ++removed; + } + else + { + i = m_Sounds.NextHandle( i ); + } + } + + if (removed > 0 || m_SavedOverrides.Count() > 0 ) + { + Warning( "SoundEmitter: removing map sound overrides [%i to remove, %i to restore]\n", + removed, + m_SavedOverrides.Count() ); + } + + // Now restore the original entries into the main dictionary. + for ( i = 0; i < m_SavedOverrides.Count(); ++i ) + { + CSoundEntry *entry = m_SavedOverrides[ i ]; + m_Sounds.Insert( entry ); + } + + m_SavedOverrides.Purge(); + m_OverrideFiles.Purge(); +} + +CSoundEmitterSystemBase g_SoundEmitterSystemBase; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSoundEmitterSystemBase, ISoundEmitterSystemBase, + SOUNDEMITTERSYSTEM_INTERFACE_VERSION, g_SoundEmitterSystemBase );
\ No newline at end of file diff --git a/soundemittersystem/soundemittersystembase.h b/soundemittersystem/soundemittersystembase.h new file mode 100644 index 0000000..f0c5b01 --- /dev/null +++ b/soundemittersystem/soundemittersystembase.h @@ -0,0 +1,183 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SOUNDEMITTERSYSTEMBASE_H +#define SOUNDEMITTERSYSTEMBASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "soundflags.h" +#include "interval.h" +#include "UtlSortVector.h" +#include <tier1/utlstring.h> +#include <tier1/utlhashtable.h> + +soundlevel_t TextToSoundLevel( const char *key ); + +struct CSoundEntry +{ + CUtlConstString m_Name; + CSoundParametersInternal m_SoundParams; + uint16 m_nScriptFileIndex; + bool m_bRemoved : 1; + bool m_bIsOverride : 1; + + bool IsOverride() const + { + return m_bIsOverride; + } +}; + +struct CSoundEntryHashFunctor : CaselessStringHashFunctor +{ + using CaselessStringHashFunctor::operator(); + unsigned int operator()( CSoundEntry *e ) const + { + return CaselessStringHashFunctor::operator()( e->m_Name.Get() ); + } +}; + +struct CSoundEntryEqualFunctor : CaselessStringEqualFunctor +{ + using CaselessStringEqualFunctor::operator(); + bool operator()( CSoundEntry *lhs, CSoundEntry *rhs ) const + { + return CaselessStringEqualFunctor::operator()( lhs->m_Name.Get(), rhs->m_Name.Get() ); + } + bool operator()( CSoundEntry *lhs, const char *rhs ) const + { + return CaselessStringEqualFunctor::operator()( lhs->m_Name.Get(), rhs ); + } +}; + + + +//----------------------------------------------------------------------------- +// Purpose: Base class for sound emitter system handling (can be used by tools) +//----------------------------------------------------------------------------- +class CSoundEmitterSystemBase : public ISoundEmitterSystemBase +{ +public: + CSoundEmitterSystemBase(); + virtual ~CSoundEmitterSystemBase() { } + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual void *QueryInterface( const char *pInterfaceName ); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + +public: + + virtual bool ModInit(); + virtual void ModShutdown(); + + virtual int GetSoundIndex( const char *pName ) const; + virtual bool IsValidIndex( int index ); + virtual int GetSoundCount( void ); + + virtual const char *GetSoundName( int index ); + virtual bool GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ); + virtual const char *GetWaveName( CUtlSymbol& sym ); + + virtual CUtlSymbol AddWaveName( const char *name ); + + virtual soundlevel_t LookupSoundLevel( const char *soundname ); + virtual const char *GetWavFileForSound( const char *soundname, char const *actormodel ); + virtual const char *GetWavFileForSound( const char *soundname, gender_t gender ); + + virtual int CheckForMissingWavFiles( bool verbose ); + virtual const char *GetSourceFileForSound( int index ) const; + // Iteration methods + virtual int First() const; + virtual int Next( int i ) const; + virtual int InvalidIndex() const; + + virtual CSoundParametersInternal *InternalGetParametersForSound( int index ); + + + // The host application is responsible for dealing with dirty sound scripts, etc. + virtual bool AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params ); + virtual void RemoveSound( const char *soundname ); + + virtual void MoveSound( const char *soundname, const char *newscript ); + + virtual void RenameSound( const char *soundname, const char *newname ); + virtual void UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params ); + + virtual int GetNumSoundScripts() const; + virtual char const *GetSoundScriptName( int index ) const; + virtual bool IsSoundScriptDirty( int index ) const; + virtual int FindSoundScript( const char *name ) const; + + virtual void SaveChangesToSoundScript( int scriptindex ); + + virtual void ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename ); + virtual gender_t GetActorGender( char const *actormodel ); + virtual void GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen ); + virtual void GenderExpandString( gender_t gender, char const *in, char *out, int maxlen ); + virtual bool IsUsingGenderToken( char const *soundname ); + virtual unsigned int GetManifestFileTimeChecksum(); + + virtual bool GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHANDLE& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ); + virtual soundlevel_t LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHANDLE& handle ); + + + // Called from both client and server (single player) or just one (server only in dedicated server and client only if connected to a remote server) + // Called by LevelInitPreEntity to override sound scripts for the mod with level specific overrides based on custom mapnames, etc. + virtual void AddSoundOverrides( char const *scriptfile, bool bPreload = false ); + + // Called by either client or server in LevelShutdown to clear out custom overrides + virtual void ClearSoundOverrides(); + + virtual void ReloadSoundEntriesInList( IFileList *pFilesToReload ); + + // Called by either client or server to force ModShutdown and ModInit + virtual void Flush(); + +private: + + bool InternalModInit(); + void InternalModShutdown(); + + void AddSoundsFromFile( const char *filename, bool bPreload, bool bIsOverride = false, bool bRefresh = false ); + + bool InitSoundInternalParameters( const char *soundname, KeyValues *kv, CSoundParametersInternal& params ); + + void LoadGlobalActors(); + + float TranslateAttenuation( const char *key ); + soundlevel_t TranslateSoundLevel( const char *key ); + int TranslateChannel( const char *name ); + + int FindBestSoundForGender( SoundFile *pSoundnames, int c, gender_t gender ); + void EnsureAvailableSlotsForGender( SoundFile *pSoundnames, int c, gender_t gender ); + void AddSoundName( CSoundParametersInternal& params, char const *wavename, gender_t gender ); + + CUtlHashtable< CUtlConstString, gender_t, CaselessStringHashFunctor, UTLConstStringCaselessStringEqualFunctor<char> > m_ActorGenders; + CUtlStableHashtable< CSoundEntry*, empty_t, CSoundEntryHashFunctor, CSoundEntryEqualFunctor, uint16, const char* > m_Sounds; + + CUtlVector< CSoundEntry * > m_SavedOverrides; + CUtlVector< FileNameHandle_t > m_OverrideFiles; + + struct CSoundScriptFile + { + FileNameHandle_t hFilename; + bool dirty; + }; + + CUtlVector< CSoundScriptFile > m_SoundKeyValues; + int m_nInitCount; + unsigned int m_uManifestPlusScriptChecksum; + + CUtlSymbolTable m_Waves; +}; + +#endif // SOUNDEMITTERSYSTEMBASE_H diff --git a/soundemittersystem/xbox/xbox.def b/soundemittersystem/xbox/xbox.def new file mode 100644 index 0000000..c3981bb --- /dev/null +++ b/soundemittersystem/xbox/xbox.def @@ -0,0 +1,3 @@ +LIBRARY SoundEmitterSystem_360.dll +EXPORTS + CreateInterface @1 |