From cddae6f4567e9d1c6af6ad3c23de6142bf2d7673 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 20 Jan 2015 15:08:57 -0800 Subject: [PATCH 1/3] Upgrading the normal sound hook for games like CS:GO --- extensions/sdktools/vsound.cpp | 97 +++++++++++++++++++++++++++++- plugins/include/sdktools_sound.inc | 6 +- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/extensions/sdktools/vsound.cpp b/extensions/sdktools/vsound.cpp index a015c385..ad7d988d 100644 --- a/extensions/sdktools/vsound.cpp +++ b/extensions/sdktools/vsound.cpp @@ -283,6 +283,57 @@ void SoundHooks::OnEmitAmbientSound(int entindex, const Vector &pos, const char } } +#if SOURCE_ENGINE >= SE_PORTAL2 +// This should probably be moved to the gamedata +#define SOUND_ENTRY_HASH_SEED 0x444F5441 + +uint32 GenerateSoundEntryHash(char const *pSoundEntry) +{ + // First we need to convert the sound entry to lowercase before we calculate the hash + int nSoundEntryLength = strlen(pSoundEntry); + char *pSoundEntryLowerCase = (char *)stackalloc(nSoundEntryLength + 1); + + for (int nIndex = 0; nIndex < nSoundEntryLength; nIndex++) + pSoundEntryLowerCase[nIndex] = tolower(pSoundEntry[nIndex]); + + // Second we need to calculate the hash using the algorithm reconstructed from CS:GO + const uint32 nMagicNumber = 0x5bd1e995; + + uint32 nSoundHash = SOUND_ENTRY_HASH_SEED ^ nSoundEntryLength; + + unsigned char *pData = (unsigned char *)pSoundEntryLowerCase; + + while (nSoundEntryLength >= 4) + { + uint32 nLittleDWord = LittleDWord(*(uint32 *)pData); + + nLittleDWord *= nMagicNumber; + nLittleDWord ^= nLittleDWord >> 24; + nLittleDWord *= nMagicNumber; + + nSoundHash *= nMagicNumber; + nSoundHash ^= nLittleDWord; + + pData += 4; + nSoundEntryLength -= 4; + } + + switch (nSoundEntryLength) + { + case 3: nSoundHash ^= pData[2] << 16; + case 2: nSoundHash ^= pData[1] << 8; + case 1: nSoundHash ^= pData[0]; + nSoundHash *= nMagicNumber; + }; + + nSoundHash ^= nSoundHash >> 13; + nSoundHash *= nMagicNumber; + nSoundHash ^= nSoundHash >> 15; + + return nSoundHash; +} +#endif + #if SOURCE_ENGINE >= SE_PORTAL2 int SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChannel, const char *pSoundEntry, unsigned int nSoundEntryHash, const char *pSample, float flVolume, soundlevel_t iSoundlevel, int nSeed, int iFlags, int iPitch, const Vector *pOrigin, @@ -306,6 +357,15 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann char buffer[PLATFORM_MAX_PATH]; strcpy(buffer, pSample); + char soundEntry[PLATFORM_MAX_PATH]; +#if SOURCE_ENGINE >= SE_PORTAL2 + strcpy(soundEntry, pSoundEntry); +#endif + +#if SOURCE_ENGINE < SE_PORTAL2 + int nSeed = 0; +#endif + for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++) { int players[SM_MAXPLAYERS], size; @@ -321,6 +381,8 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann pFunc->PushCellByRef(reinterpret_cast(&iSoundlevel)); pFunc->PushCellByRef(&iPitch); pFunc->PushCellByRef(&iFlags); + pFunc->PushStringEx(soundEntry, sizeof(soundEntry), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + pFunc->PushCellByRef(&nSeed); g_InSoundHook = true; pFunc->Execute(&res); g_InSoundHook = false; @@ -360,6 +422,16 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann #endif } +#if SOURCE_ENGINE >= SE_PORTAL2 + if (strcmp(pSoundEntry, soundEntry) != 0 || strcmp(pSample, buffer) != 0) + { + if (strcmp(soundEntry, buffer) == 0) + nSoundEntryHash = -1; + else if (strcmp(soundEntry, "") != 0) + nSoundEntryHash = GenerateSoundEntryHash(soundEntry); + } +#endif + CellRecipientFilter crf; crf.Initialize(players, size); #if SOURCE_ENGINE >= SE_PORTAL2 @@ -368,7 +440,7 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann -1, static_cast *, bool, float, int)>(&IEngineSound::EmitSound), - (crf, iEntIndex, iChannel, buffer, -1, buffer, flVolume, iSoundlevel, nSeed, iFlags, iPitch, pOrigin, + (crf, iEntIndex, iChannel, soundEntry, nSoundEntryHash, buffer, flVolume, iSoundlevel, nSeed, iFlags, iPitch, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity) ); #elif SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_TF2 @@ -421,6 +493,15 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan char buffer[PLATFORM_MAX_PATH]; strcpy(buffer, pSample); + char soundEntry[PLATFORM_MAX_PATH]; +#if SOURCE_ENGINE >= SE_PORTAL2 + strcpy(soundEntry, pSoundEntry); +#endif + +#if SOURCE_ENGINE < SE_PORTAL2 + int nSeed = 0; +#endif + for (iter=m_NormalFuncs.begin(); iter!=m_NormalFuncs.end(); iter++) { int players[SM_MAXPLAYERS], size; @@ -436,6 +517,8 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan pFunc->PushCellByRef(&sndlevel); pFunc->PushCellByRef(&iPitch); pFunc->PushCellByRef(&iFlags); + pFunc->PushStringEx(soundEntry, sizeof(soundEntry), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + pFunc->PushCellByRef(&nSeed); g_InSoundHook = true; pFunc->Execute(&res); g_InSoundHook = false; @@ -475,6 +558,16 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan #endif } +#if SOURCE_ENGINE >= SE_PORTAL2 + if (strcmp(pSoundEntry, soundEntry) != 0 || strcmp(pSample, buffer) != 0) + { + if (strcmp(soundEntry, buffer) == 0) + nSoundEntryHash = -1; + else if (strcmp(soundEntry, "") != 0) + nSoundEntryHash = GenerateSoundEntryHash(soundEntry); + } +#endif + CellRecipientFilter crf; crf.Initialize(players, size); #if SOURCE_ENGINE >= SE_PORTAL2 @@ -483,7 +576,7 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan -1, static_cast *, bool, float, int)>(&IEngineSound::EmitSound), - (crf, iEntIndex, iChannel, buffer, -1, buffer, flVolume, SNDLVL_TO_ATTN(static_cast(sndlevel)), + (crf, iEntIndex, iChannel, soundEntry, nSoundEntryHash, buffer, flVolume, SNDLVL_TO_ATTN(static_cast(sndlevel)), nSeed, iFlags, iPitch, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity) ); #elif SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_SDK2013 || SOURCE_ENGINE == SE_TF2 diff --git a/plugins/include/sdktools_sound.inc b/plugins/include/sdktools_sound.inc index 12a5b7b0..39552ed7 100644 --- a/plugins/include/sdktools_sound.inc +++ b/plugins/include/sdktools_sound.inc @@ -307,6 +307,8 @@ typedef AmbientSHook = function Action ( * @param level Sound level. * @param pitch Sound pitch. * @param flags Sound flags. + * @param soundEntry Game sound entry name. (Used in engines newer than Portal 2) + * @param seed Sound seed. (Used in engines newer than Portal 2) * @return Plugin_Continue to allow the sound to be played, Plugin_Stop to block it, * Plugin_Changed when any parameter has been modified. */ @@ -319,7 +321,9 @@ typedef NormalSHook = function Action ( float &volume, int &level, int &pitch, - int &flags + int &flags, + char soundEntry[PLATFORM_MAX_PATH], + int &seed ); /** From ee0575bbb0e8f97a5bc623ce98ac8b98d6781a91 Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 21 Jan 2015 10:33:29 -0800 Subject: [PATCH 2/3] Adding EmitSoundEntry() for engines >= portal 2 --- extensions/sdktools/vsound.cpp | 142 +++++++++++++++++++++++++++++ plugins/include/sdktools_sound.inc | 42 +++++++++ 2 files changed, 184 insertions(+) diff --git a/extensions/sdktools/vsound.cpp b/extensions/sdktools/vsound.cpp index ad7d988d..18d3e46d 100644 --- a/extensions/sdktools/vsound.cpp +++ b/extensions/sdktools/vsound.cpp @@ -1130,6 +1130,147 @@ static cell_t EmitSound(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t EmitSoundEntry(IPluginContext *pContext, const cell_t *params) +{ +#if SOURCE_ENGINE < SE_PORTAL2 + return pContext->ThrowNativeError("EmitSoundEntry is not available in this game."); +#else + cell_t *addr, *cl_array; + CellRecipientFilter crf; + unsigned int numClients; + int client; + IGamePlayer *pPlayer = NULL; + + pContext->LocalToPhysAddr(params[1], &cl_array); + numClients = params[2]; + + /* Client validation */ + for (unsigned int i = 0; i < numClients; i++) + { + client = cl_array[i]; + pPlayer = playerhelpers->GetGamePlayer(client); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } + else if (!pPlayer->IsInGame()) { + return pContext->ThrowNativeError("Client %d is not connected", client); + } + } + + crf.Initialize(cl_array, numClients); + + char *soundEntry; + pContext->LocalToString(params[3], &soundEntry); + + char *sample; + pContext->LocalToString(params[4], &sample); + + // By default, we want the hash to equal maxint + unsigned int soundEntryHash = -1; + + // We only generate a hash if the sample is not the same as the sound entry and the sound entry is not empty. + if (strcmp(soundEntry, sample) != 0 && strcmp(soundEntry, "") != 0) + soundEntryHash = GenerateSoundEntryHash(soundEntry); + + int entity = SoundReferenceToIndex(params[5]); + int channel = params[6]; + int level = params[7]; + int seed = params[8]; + int flags = params[9]; + float vol = sp_ctof(params[10]); + int pitch = params[11]; + int speakerentity = params[12]; + + Vector *pOrigin = NULL, origin; + Vector *pDir = NULL, dir; + + pContext->LocalToPhysAddr(params[13], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pOrigin = &origin; + origin.x = sp_ctof(addr[0]); + origin.y = sp_ctof(addr[1]); + origin.z = sp_ctof(addr[2]); + } + + pContext->LocalToPhysAddr(params[14], &addr); + if (addr != pContext->GetNullRef(SP_NULL_VECTOR)) + { + pDir = &dir; + dir.x = sp_ctof(addr[0]); + dir.y = sp_ctof(addr[1]); + dir.z = sp_ctof(addr[2]); + } + + bool updatePos = params[15] ? true : false; + float soundtime = sp_ctof(params[16]); + + CUtlVector *pOrigVec = NULL; + CUtlVector origvec; + if (params[0] > 16) + { + pOrigVec = &origvec; + for (cell_t i = 17; i <= params[0]; i++) + { + Vector vec; + pContext->LocalToPhysAddr(params[i], &addr); + vec.x = sp_ctof(addr[0]); + vec.y = sp_ctof(addr[1]); + vec.z = sp_ctof(addr[2]); + origvec.AddToTail(vec); + } + } + + /* If we're going to a "local player" and this is a dedicated server, + * intelligently redirect each sound. + */ + + if (entity == -2 && engine->IsDedicatedServer()) + { + for (unsigned int i = 0; i < numClients; i++) + { + cell_t player[1]; + player[0] = cl_array[i]; + crf.Reset(); + crf.Initialize(player, 1); + + if (g_InSoundHook) + { + SH_CALL(enginesoundPatch, + static_cast *, bool, float, int)> + (&IEngineSound::EmitSound))(crf, player[0], channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed, + flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity); + } + else + { + engsound->EmitSound(crf, player[0], channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed, + flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity); + } + } + } + else { + if (g_InSoundHook) + { + SH_CALL(enginesoundPatch, + static_cast *, bool, float, int)> + (&IEngineSound::EmitSound))(crf, entity, channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, + seed, flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity); + } + else + { + engsound->EmitSound(crf, entity, channel, soundEntry, soundEntryHash, sample, vol, (soundlevel_t)level, seed, + flags, pitch, pOrigin, pDir, pOrigVec, updatePos, soundtime, speakerentity); + } + } + + return 1; +#endif +} + static cell_t EmitSentence(IPluginContext *pContext, const cell_t *params) { cell_t *addr; @@ -1346,6 +1487,7 @@ sp_nativeinfo_t g_SoundNatives[] = {"EmitAmbientSound", EmitAmbientSound}, {"EmitSentence", EmitSentence}, {"EmitSound", EmitSound}, + {"EmitSoundEntry", EmitSoundEntry}, {"FadeClientVolume", FadeClientVolume}, {"GetSoundDuration", GetSoundDuration}, {"PrefetchSound", PrefetchSound}, diff --git a/plugins/include/sdktools_sound.inc b/plugins/include/sdktools_sound.inc index 39552ed7..2ac153d9 100644 --- a/plugins/include/sdktools_sound.inc +++ b/plugins/include/sdktools_sound.inc @@ -221,6 +221,48 @@ native EmitSound(const clients[], Float:soundtime = 0.0, any:...); +/** + * Emits a sound or game sound to a list of clients using the latest version of the engine sound interface. + * This native is only available in engines that are greater than or equal to Portal 2. + * + * @param clients Array of client indexes. + * @param numClients Number of clients in the array. + * @param soundEntry Sound entry name. + * @param sample Sound file name relative to the "sounds" folder. + * @param entity Entity to emit from. + * @param channel Channel to emit with. + * @param level Sound level. + * @param seed Sound seed. + * @param flags Sound flags. + * @param volume Sound volume. + * @param pitch Sound pitch. + * @param speakerentity Unknown. + * @param origin Sound origin. + * @param dir Sound direction. + * @param updatePos Unknown (updates positions?) + * @param soundtime Alternate time to play sound for. + * @param ... Optional list of Float[3] arrays to specify additional origins. + * @noreturn + * @error Invalid client index. + */ +native EmitSoundEntry(const clients[], + numClients, + const String:soundEntry[], + const String:sample[], + entity = SOUND_FROM_PLAYER, + channel = SNDCHAN_AUTO, + level = SNDLEVEL_NORMAL, + seed = 0, + flags = SND_NOFLAGS, + Float:volume = SNDVOL_NORMAL, + pitch = SNDPITCH_NORMAL, + speakerentity = -1, + const Float:origin[3] = NULL_VECTOR, + const Float:dir[3] = NULL_VECTOR, + bool:updatePos = true, + Float:soundtime = 0.0, + any:...); + /** * Emits a sentence to a list of clients. * From 3558418ed7242b2b3003da9efb7eab4d95007581 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 5 Feb 2015 11:12:33 -0800 Subject: [PATCH 3/3] Initializing soundEntry and using something safer than strcpy --- extensions/sdktools/vsound.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/sdktools/vsound.cpp b/extensions/sdktools/vsound.cpp index 18d3e46d..cbf2da5c 100644 --- a/extensions/sdktools/vsound.cpp +++ b/extensions/sdktools/vsound.cpp @@ -357,9 +357,9 @@ void SoundHooks::OnEmitSound(IRecipientFilter &filter, int iEntIndex, int iChann char buffer[PLATFORM_MAX_PATH]; strcpy(buffer, pSample); - char soundEntry[PLATFORM_MAX_PATH]; + char soundEntry[PLATFORM_MAX_PATH] = ""; #if SOURCE_ENGINE >= SE_PORTAL2 - strcpy(soundEntry, pSoundEntry); + Q_strncpy(soundEntry, pSoundEntry, sizeof(soundEntry)); #endif #if SOURCE_ENGINE < SE_PORTAL2 @@ -493,9 +493,9 @@ void SoundHooks::OnEmitSound2(IRecipientFilter &filter, int iEntIndex, int iChan char buffer[PLATFORM_MAX_PATH]; strcpy(buffer, pSample); - char soundEntry[PLATFORM_MAX_PATH]; + char soundEntry[PLATFORM_MAX_PATH] = ""; #if SOURCE_ENGINE >= SE_PORTAL2 - strcpy(soundEntry, pSoundEntry); + Q_strncpy(soundEntry, pSoundEntry, sizeof(soundEntry)); #endif #if SOURCE_ENGINE < SE_PORTAL2